diff --git a/.github/actions/build-eve/action.yml b/.github/actions/build-eve/action.yml index fddbb68a5ef..6e61a908dae 100644 --- a/.github/actions/build-eve/action.yml +++ b/.github/actions/build-eve/action.yml @@ -14,6 +14,10 @@ inputs: cache-artifact: required: true description: "Name of the linuxkit cache artifact to download" + cover: + required: false + default: 'false' + description: "Build pillar with COVER=y so zedbox is coverage-instrumented" runs: using: composite @@ -40,14 +44,30 @@ runs: shell: bash env: PR_ID: ${{ github.event.pull_request.number }} + COVER: ${{ inputs.cover }} run: | COMMIT_ID=$(git describe --abbrev=8 --always) - echo "VERSION=0.0.0-pr$PR_ID-$COMMIT_ID" >> $GITHUB_ENV - echo "TAG=evebuild/pr:$PR_ID" >> $GITHUB_ENV + if [ "$COVER" = "true" ]; then + echo "VERSION=0.0.0-pr$PR_ID-$COMMIT_ID-cover" >> "$GITHUB_ENV" + echo "TAG=evebuild/pr-cover:$PR_ID" >> "$GITHUB_ENV" + echo "ARTIFACT_BASE=eve-cover-${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }}" >> "$GITHUB_ENV" + else + echo "VERSION=0.0.0-pr$PR_ID-$COMMIT_ID" >> "$GITHUB_ENV" + echo "TAG=evebuild/pr:$PR_ID" >> "$GITHUB_ENV" + echo "ARTIFACT_BASE=eve-${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }}" >> "$GITHUB_ENV" + fi + - name: Rebuild pillar with coverage instrumentation + if: ${{ inputs.cover == 'true' }} + shell: bash + run: | + make V=1 ROOTFS_VERSION="$VERSION" COVER=y PLATFORM=${{ inputs.platform }} \ + HV=${{ inputs.hv }} ZARCH=${{ inputs.arch }} eve-pillar - name: Build EVE ${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }} shell: bash + env: + COVER_FLAG: ${{ inputs.cover == 'true' && 'COVER=y' || '' }} run: | - make V=1 ROOTFS_VERSION="$VERSION" PLATFORM=${{ inputs.platform }} \ + make V=1 ROOTFS_VERSION="$VERSION" $COVER_FLAG PLATFORM=${{ inputs.platform }} \ HV=${{ inputs.hv }} ZARCH=${{ inputs.arch }} eve - name: Post build report shell: bash @@ -59,13 +79,13 @@ runs: run: | make cache-export ZARCH=${{ inputs.arch }} \ IMAGE=lfedge/eve:$VERSION-${{ inputs.hv }} \ - OUTFILE=eve-${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }}.tar \ + OUTFILE=$ARTIFACT_BASE.tar \ IMAGE_NAME=$TAG-${{ inputs.hv }}-${{ inputs.arch }} - name: Upload EVE image uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: eve-${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }} - path: eve-${{ inputs.hv }}-${{ inputs.arch }}-${{ inputs.platform }}.tar + name: ${{ env.ARTIFACT_BASE }} + path: ${{ env.ARTIFACT_BASE }}.tar - name: Clean if: always() shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87bde0c2f83..2de8818129d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,6 +160,36 @@ jobs: hv: kvm cache-artifact: linuxkit-cache-amd64-generic + # Coverage-instrumented build. Opt-in via the `coverage` PR label so the + # extra ~1× build cost only lands on PRs that want a coverage signal from + # the Eden test matrix. The Eden trusted workflow's tests-master-cover job + # consumes the eve-cover-* artifact this produces. + eve-amd64-kvm-generic-cover: + name: "eve (amd64, kvm, generic, cover)" + needs: pkgs-amd64-generic + if: ${{ contains(github.event.pull_request.labels.*.name, 'coverage') }} + runs-on: zededa-ubuntu-2204 + timeout-minutes: 1440 + steps: + - name: Clear repository + shell: bash + run: | + sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" + rm -fr ~/.linuxkit + docker system prune --all --force --volumes + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: 0 + - uses: ./.github/actions/build-eve + with: + arch: amd64 + platform: generic + hv: kvm + cache-artifact: linuxkit-cache-amd64-generic + cover: 'true' + # ── amd64 rt ── eve-amd64-kvm-rt: diff --git a/.github/workflows/eden-trusted.yml b/.github/workflows/eden-trusted.yml index f22c38d384d..8620ae89558 100644 --- a/.github/workflows/eden-trusted.yml +++ b/.github/workflows/eden-trusted.yml @@ -37,6 +37,7 @@ jobs: gate_run_id: ${{ steps.extract.outputs.gate_run_id }} gate_status_name: ${{ steps.extract.outputs.gate_status_name }} skip_run: ${{ steps.check_gate.outputs.skip_run }} + has_coverage: ${{ steps.labels.outputs.has_coverage }} steps: - name: Download uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -108,6 +109,22 @@ jobs: echo "$field=$value" >> "$GITHUB_OUTPUT" done + - name: Detect coverage label + if: ${{ steps.check_gate.outputs.skip_run == 'false' }} + id: labels + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_ID: ${{ steps.extract.outputs.pr_id }} + run: | + # gh api bypasses the classic-Projects GraphQL deprecation error that + # affects `gh pr view` on lf-edge repos. + has=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_ID}" --jq '.labels[].name' | grep -Fx coverage || true) + if [ -n "$has" ]; then + echo "has_coverage=true" >> "$GITHUB_OUTPUT" + else + echo "has_coverage=false" >> "$GITHUB_OUTPUT" + fi + - name: Define Titles if: ${{ steps.check_gate.outputs.skip_run == 'false' }} id: titles @@ -227,16 +244,36 @@ jobs: artifact_run_id: ${{ needs.context.outputs.original_run_id }} eden_version: "master" + # Coverage-instrumented run, opt-in via the `coverage` PR label. Consumes + # the eve-cover-* artifact produced by build.yml's *-cover job and emits + # the covdata-merged artifact via eden's coverage-merge job. + tests-master-cover: + name: ${{ needs.context.outputs.eden_parent_job_title }} for master (cover) + needs: context + if: >- + needs.context.outputs.skip_run == 'false' + && needs.context.outputs.pr_base_ref == 'master' + && needs.context.outputs.has_coverage == 'true' + uses: lf-edge/eden/.github/workflows/test.yml@master + secrets: inherit + with: + eve_image: "evebuild/pr-cover:${{ needs.context.outputs.pr_id }}" + eve_log_level: "debug" + eve_artifact_name: "eve-cover-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" + artifact_run_id: ${{ needs.context.outputs.original_run_id }} + eden_version: "master" + finalize: name: Eden Tests Complete - needs: [context, tests-13-4-stable, tests-14-5-stable, tests-16-0-stable, tests-master] + needs: [context, tests-13-4-stable, tests-14-5-stable, tests-16-0-stable, tests-master, tests-master-cover] if: always() && needs.context.outputs.skip_run == 'false' runs-on: ubuntu-latest steps: - name: Check test results id: check run: | - # Determine which job ran + # Determine which job ran. The cover variant runs alongside + # tests-master on coverage-labeled PRs; require both to succeed. if [[ "${{ needs.tests-13-4-stable.result }}" != "skipped" ]]; then result="${{ needs.tests-13-4-stable.result }}" elif [[ "${{ needs.tests-14-5-stable.result }}" != "skipped" ]]; then @@ -245,6 +282,10 @@ jobs: result="${{ needs.tests-16-0-stable.result }}" else result="${{ needs.tests-master.result }}" + cover_result="${{ needs.tests-master-cover.result }}" + if [[ "$cover_result" != "skipped" && "$cover_result" != "success" ]]; then + result="$cover_result" + fi fi echo "Eden tests result: $result" diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index f2328a73773..73bef3e27ca 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -89,19 +89,35 @@ jobs: RUN_ID: ${{ steps.meta.outputs.original_run_id }} GH_REPO: ${{ github.repository }} BUILD_JOB_NAME: "eve (${{ matrix.arch }}, ${{ matrix.hv }}, ${{ matrix.platform }})" + COVER_BUILD_JOB_NAME: "eve (${{ matrix.arch }}, ${{ matrix.hv }}, ${{ matrix.platform }}, cover)" run: | - build_conclusion=$(gh api \ - /repos/$GH_REPO/actions/runs/$RUN_ID/jobs \ - -X GET \ - -q ".jobs[] | select(.name == \"$BUILD_JOB_NAME\") | .conclusion" ) || api_status=$? + jobs_json=$(gh api "/repos/$GH_REPO/actions/runs/$RUN_ID/jobs") || api_status=$? if [[ "${api_status:-0}" -ne 0 ]]; then echo "::error::Failed to fetch build job status for run $RUN_ID" echo "build_ok=false" >>"$GITHUB_OUTPUT" - else - echo "build_ok=$([[ "$build_conclusion" == 'success' ]] && echo true || echo false)" >>"$GITHUB_OUTPUT" + exit 0 fi + # On the pull_request_review trigger this step may run while the + # coverage-instrumented build is still in flight. If the cover job + # exists (label present) and is not yet in a terminal state, hold + # the gate -- otherwise eden-trusted's tests-master-cover would + # start before the eve-cover-* artifact is ready. When the label + # is absent the cover job is marked "skipped" at workflow start, + # which is already a terminal state. + cover_status=$(echo "$jobs_json" | \ + jq -r ".jobs[] | select(.name == \"$COVER_BUILD_JOB_NAME\") | .status") + if [[ -n "$cover_status" && "$cover_status" != "completed" ]]; then + echo "Cover build status=$cover_status; gate waits for completion" + echo "build_ok=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + build_conclusion=$(echo "$jobs_json" | \ + jq -r ".jobs[] | select(.name == \"$BUILD_JOB_NAME\") | .conclusion") + echo "build_ok=$([[ "$build_conclusion" == 'success' ]] && echo true || echo false)" >>"$GITHUB_OUTPUT" + - name: Check Gate Condition id: check run: |