diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 15c9bf6..60a8a00 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -19,11 +19,6 @@ on: description: GitHub-generated merge commit to test required: true type: string - publish_status: - description: Publish CI / required from the trusted main refresh path - required: false - type: boolean - default: false concurrency: group: ci-pr-${{ inputs.pr_number }} @@ -463,7 +458,6 @@ jobs: timeout-minutes: 5 permissions: contents: read - statuses: write steps: - name: 汇总所有必要任务 id: summary @@ -519,42 +513,6 @@ jobs: require_job nginx "$NGINX_RESULT" "$NGINX_REQUIRED" require_job dependency_review "$DEPENDENCY_RESULT" "$DEPENDENCY_REQUIRED" - - name: 发布 main 更新重检状态 - if: always() && inputs.publish_status - env: - GH_TOKEN: ${{ github.token }} - MERGE_SHA: ${{ inputs.merge_sha }} - SUMMARY_OUTCOME: ${{ steps.summary.outcome }} - shell: bash - run: | - set -euo pipefail - state=failure - description="UniSpeaking 必要 CI 检查未通过" - if [[ "$SUMMARY_OUTCOME" == success ]]; then - state=success - description="UniSpeaking 必要 CI 检查已通过" - fi - - jq -n \ - --arg state "$state" \ - --arg context "CI / required" \ - --arg description "$description" \ - --arg target_url "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \ - '{ - state: $state, - context: $context, - description: $description, - target_url: $target_url - }' > "$RUNNER_TEMP/status.json" - - curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $GH_TOKEN" \ - --header "Accept: application/vnd.github+json" \ - --header "X-GitHub-Api-Version: 2022-11-28" \ - --data-binary "@$RUNNER_TEMP/status.json" \ - "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/statuses/$MERGE_SHA" - - name: 执行最终门禁 if: always() && steps.summary.outcome != 'success' run: exit 1 diff --git a/.github/workflows/ci-refresh-pr.yml b/.github/workflows/ci-refresh-pr.yml index 83f3060..ff4766c 100644 --- a/.github/workflows/ci-refresh-pr.yml +++ b/.github/workflows/ci-refresh-pr.yml @@ -122,4 +122,50 @@ jobs: base_sha: ${{ needs.resolve.outputs.base_sha }} head_sha: ${{ needs.resolve.outputs.head_sha }} merge_sha: ${{ needs.resolve.outputs.merge_sha }} - publish_status: true + + publish: + name: 发布重检状态 + needs: + - resolve + - core + if: always() && needs.resolve.outputs.runnable == 'true' + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + statuses: write + steps: + - name: 发布 CI / required + env: + GH_TOKEN: ${{ github.token }} + MERGE_SHA: ${{ needs.resolve.outputs.merge_sha }} + CORE_RESULT: ${{ needs.core.result }} + shell: bash + run: | + set -euo pipefail + state=failure + description="UniSpeaking 必要 CI 检查未通过" + if [[ "$CORE_RESULT" == success ]]; then + state=success + description="UniSpeaking 必要 CI 检查已通过" + fi + + jq -n \ + --arg state "$state" \ + --arg context "CI / required" \ + --arg description "$description" \ + --arg target_url "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \ + '{ + state: $state, + context: $context, + description: $description, + target_url: $target_url + }' > "$RUNNER_TEMP/status.json" + + curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $GH_TOKEN" \ + --header "Accept: application/vnd.github+json" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + --data-binary "@$RUNNER_TEMP/status.json" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/statuses/$MERGE_SHA" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..3d7d2aa --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,69 @@ +name: Coverage + +on: + push: + branches: + - main + paths: + - backend/** + - .github/workflows/coverage.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: coverage-main + cancel-in-progress: true + +jobs: + backend: + name: 生成并发布后端覆盖率 + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + id-token: write + defaults: + run: + working-directory: backend/unispeaking-server + steps: + - name: 检出 main + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: 配置 Java 21 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + with: + distribution: temurin + java-version: "21" + cache: maven + cache-dependency-path: backend/unispeaking-server/pom.xml + + - name: 运行测试并生成 JaCoCo 聚合报告 + shell: bash + run: >- + ./mvnw + --batch-mode + --no-transfer-progress + -Pci-integration,coverage-aggregate + clean + verify + + - name: 上传 JaCoCo XML 到 Codecov + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6.0.2 + with: + use_oidc: true + files: backend/unispeaking-server/target/site/jacoco-aggregate/jacoco.xml + disable_search: true + fail_ci_if_error: true + flags: backend + name: backend-aggregate + + - name: 保存 JaCoCo 聚合报告 + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: backend-main-coverage + path: backend/unispeaking-server/target/site/jacoco-aggregate + if-no-files-found: error + retention-days: 30 diff --git a/README.md b/README.md index 80174bb..e9bd72e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # UniSpeaking +[![后端测试](https://github.com/1024XEngineer/UniSpeaking/actions/workflows/coverage.yml/badge.svg?branch=main)](https://github.com/1024XEngineer/UniSpeaking/actions/workflows/coverage.yml) +[![后端覆盖率](https://codecov.io/gh/1024XEngineer/UniSpeaking/branch/main/graph/badge.svg?flag=backend)](https://codecov.io/gh/1024XEngineer/UniSpeaking) + +覆盖率目前统计后端 Java 代码,合并单元测试及 PostgreSQL、Redis 集成测试结果; +前端尚未接入自动化测试覆盖率。 + UniSpeaking 是一个面向英语口语学习的 AI 实时陪练系统。当前仓库包含 React 前端、 Spring Boot 后端、PostgreSQL 数据模型以及 Nginx/Docker 部署配置。 diff --git a/docs/ci.md b/docs/ci.md index 9ecff5a..facf964 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -13,6 +13,7 @@ Redis 引入生产运行时。 | `ci-refresh.yml` | `main` 更新后查找全部开放 PR,并行发起重检 | | `ci-refresh-pr.yml` | 等待包含最新 `main` 的 merge SHA,跳过冲突或已变化的 PR | | `ci-status.yml` | 在可信上下文校验当前 base、head、merge SHA 后发布 `CI / required` | +| `coverage.yml` | `main` 后端变更后生成 JaCoCo 聚合报告并通过 OIDC 上传到 Codecov | 同一 PR 的核心检查使用 `ci-pr-` 并发组。出现新提交或 `main` 更新时,旧检查 会自动取消;不同 PR 可以并行执行。PR 工作流只使用只读权限,不接收仓库 Secrets。 @@ -74,6 +75,18 @@ GitHub Actions 在对应运行的 Artifacts 中保留以下内容 30 天: PR 不上传后端 JAR、前端 `dist` 或 Docker 镜像。用于在任务间合并覆盖率和校验 merge SHA 的临时数据只保留 1 天。 +根目录 README 显示 `main` 分支的后端测试状态和 Codecov 覆盖率。覆盖率合并单元测试 +及 PostgreSQL、Redis 集成测试所执行的后端 Java 代码;它不是数据库表或 SQL 语句的 +覆盖率。前端尚未建立自动化测试覆盖率,因此 README 暂不显示前端覆盖率。 +`coverage.yml` 上传的是 JaCoCo 聚合报告 +`backend/unispeaking-server/target/site/jacoco-aggregate/jacoco.xml`,同时使用 +`backend` flag 标记报告。上传通过 GitHub OIDC 鉴权,不需要配置 `CODECOV_TOKEN`。 + +首次启用前,仓库管理员需要在 Codecov 中安装 GitHub App 并激活 +`1024XEngineer/UniSpeaking`。工作流合入 `main` 后会自动进行首次上传;也可以在 +Actions 的 Coverage 工作流中手动运行。首次上传成功前,README 徽章可能显示 +`unknown`。 + ## 首次启用与分支保护 新增工作流的首个 PR 合并前,默认分支尚不存在可供 Ruleset 选择的