如何在CircleCI流水线中存储并对比Node覆盖率测试结果
实现CircleCI覆盖率对比与流水线失败控制
步骤1:保存本次覆盖率结果为工件
修改build_and_test任务,将npx lcov-total的输出保存到本地文件,再上传为CircleCI工件以便后续读取:
jobs: build_and_test: executor: node/default steps: - nodesetup - run: npm run test - run: npm run ci-coverage # 计算覆盖率并写入文件 - run: name: Calculate and save coverage total command: | npx lcov-total coverage/lcov.info > coverage_total.txt echo "本次覆盖率: $(cat coverage_total.txt)" # 上传覆盖率文件为工件 - store_artifacts: path: coverage_total.txt destination: coverage_total
步骤2:拉取上次成功流水线的覆盖率结果
通过CircleCI API获取上一次成功流水线的覆盖率工件,需要先在项目设置中添加环境变量CIRCLE_API_TOKEN(需拥有read-pipelines和read-artifacts权限),然后在任务中添加拉取步骤:
jobs: build_and_test: executor: node/default steps: - nodesetup # 获取上次成功流水线的覆盖率数据 - run: name: Fetch previous coverage total command: | # 获取上一次成功的流水线ID(跳过当前正在执行的流水线) PREV_PIPELINE_ID=$(curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pipeline?status=success&limit=2" | \ jq -r '.items[1].id') if [ -n "$PREV_PIPELINE_ID" ]; then # 查找对应工件的下载链接 ARTIFACT_URL=$(curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/pipeline/$PREV_PIPELINE_ID/workflow" | \ jq -r '.items[0].id' | xargs -I {} curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/workflow/{}/artifact" | \ jq -r '.items[] | select(.path | contains("coverage_total.txt")) | .url') # 下载上次的覆盖率文件 if [ -n "$ARTIFACT_URL" ]; then curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" "$ARTIFACT_URL" > previous_coverage_total.txt echo "上次覆盖率: $(cat previous_coverage_total.txt)" else echo "未找到上次覆盖率工件,跳过对比" touch previous_coverage_total.txt fi else echo "无历史成功流水线,跳过对比" touch previous_coverage_total.txt fi environment: CIRCLE_API_TOKEN: $CIRCLE_API_TOKEN
步骤3:对比覆盖率并控制流水线状态
添加对比逻辑,若本次覆盖率低于上次则主动终止流水线:
jobs: build_and_test: executor: node/default steps: # 前面的nodesetup、拉取上次结果、测试生成覆盖率步骤同上 # ... # 计算覆盖率并写入文件步骤同上 # ... # 对比覆盖率数值 - run: name: Compare coverage totals command: | # 检查是否存在有效历史数据 if [ -s previous_coverage_total.txt ]; then # 去除百分号,转为纯数字 PREV_TOTAL=$(cat previous_coverage_total.txt | tr -d '%') CURRENT_TOTAL=$(cat coverage_total.txt | tr -d '%') # 浮点数值对比,若本次更低则失败 if (( $(echo "$CURRENT_TOTAL < $PREV_TOTAL" | bc -l) )); then echo "错误:本次覆盖率($CURRENT_TOTAL%)低于上次($PREV_TOTAL%),流水线终止" exit 1 else echo "覆盖率符合要求:本次($CURRENT_TOTAL%) ≥ 上次($PREV_TOTAL%)" fi else echo "无历史覆盖率数据,跳过对比" fi # 上传本次覆盖率工件步骤同上 # ...
完整修改后的config.yaml
orbs: node: circleci/node@5.0.2 commands: nodesetup: description: checkout code, restore cache, install dependencies, save cache steps: - checkout - node/install-packages: cache-path: ~/project/node_modules app-dir: ~/project/ jobs: build_and_test: executor: node/default steps: - nodesetup # 获取上次成功流水线的覆盖率结果 - run: name: Fetch previous coverage total command: | PREV_PIPELINE_ID=$(curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pipeline?status=success&limit=2" | \ jq -r '.items[1].id') if [ -n "$PREV_PIPELINE_ID" ]; then ARTIFACT_URL=$(curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/pipeline/$PREV_PIPELINE_ID/workflow" | \ jq -r '.items[0].id' | xargs -I {} curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" \ "https://circleci.com/api/v2/workflow/{}/artifact" | \ jq -r '.items[] | select(.path | contains("coverage_total.txt")) | .url') if [ -n "$ARTIFACT_URL" ]; then curl -s -H "Circle-Token: $CIRCLE_API_TOKEN" "$ARTIFACT_URL" > previous_coverage_total.txt echo "上次覆盖率: $(cat previous_coverage_total.txt)" else echo "未找到上次覆盖率工件,跳过对比" touch previous_coverage_total.txt fi else echo "无历史成功流水线,跳过对比" touch previous_coverage_total.txt fi environment: CIRCLE_API_TOKEN: $CIRCLE_API_TOKEN - run: npm run test - run: npm run ci-coverage # 计算覆盖率并保存到文件 - run: name: Calculate and save coverage total command: | npx lcov-total coverage/lcov.info > coverage_total.txt echo "本次覆盖率: $(cat coverage_total.txt)" # 对比覆盖率,若本次更低则失败 - run: name: Compare coverage totals command: | if [ -s previous_coverage_total.txt ]; then PREV_TOTAL=$(cat previous_coverage_total.txt | tr -d '%') CURRENT_TOTAL=$(cat coverage_total.txt | tr -d '%') if (( $(echo "$CURRENT_TOTAL < $PREV_TOTAL" | bc -l) )); then echo "错误:本次覆盖率($CURRENT_TOTAL%)低于上次($PREV_TOTAL%),流水线终止" exit 1 else echo "覆盖率符合要求:本次($CURRENT_TOTAL%) ≥ 上次($PREV_TOTAL%)" fi else echo "无历史覆盖率数据,跳过对比" fi # 上传覆盖率文件为工件 - store_artifacts: path: coverage_total.txt destination: coverage_total workflows: models_pipeline: jobs: - build_and_test
注意事项
- CircleCI默认Ubuntu镜像已包含
jq(JSON解析工具)和bc(浮点运算工具),无需额外安装 CIRCLE_API_TOKEN需在项目设置的「环境变量」中添加,权限至少包含read-pipelines和read-artifacts- 首次执行流水线时无历史数据,会自动跳过对比;从第二次成功执行开始,后续流水线会触发覆盖率校验
内容的提问来源于stack exchange,提问作者bitterman
相关产品推荐
相关产品推荐

