diff --git a/.github/workflows/_build-rs.yml b/.github/workflows/_build-rs.yml index 8f1bec88b..adcab1443 100644 --- a/.github/workflows/_build-rs.yml +++ b/.github/workflows/_build-rs.yml @@ -48,6 +48,7 @@ jobs: CARGO_TERM_COLOR: always permissions: contents: read + id-token: write outputs: artifact-id: ${{ steps.artifact.outputs.artifact-id }} steps: @@ -107,6 +108,14 @@ jobs: with: name: authentik_${{ matrix.platform }}_${{ matrix.target }}${{ inputs.artifact_suffix }} path: bin/ + - if: ${{ contains(inputs.rust_build_flags, 'instrument-coverage') }} + run: make rs-build-coverage-convert + - if: ${{ contains(inputs.rust_build_flags, 'instrument-coverage') }} + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v5 + with: + use_oidc: true + flags: rs-e2e-build-${{ matrix.target }} + files: ${{ github.workspace }}/cache/rs-build-coverage.lcov - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4 if: always() with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ef406396..045438f6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -318,17 +318,15 @@ jobs: with: use_oidc: true flags: rs-e2e - files: ${{ github.workspace }}/cache/llvm-cov-target.json + files: | + ${{ github.workspace }}/cache/llvm-cov-target.json + ${{ github.workspace }}/cache/rs-e2e-coverage.lcov - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v5 if: ${{ always() }} with: use_oidc: true flags: rs-e2e - files: ${{ github.workspace }}/cache/llvm-cov-target.json + files: | + ${{ github.workspace }}/cache/llvm-cov-target.json + ${{ github.workspace }}/cache/rs-e2e-coverage.lcov report_type: test_results - - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v5 - if: ${{ always() }} - with: - use_oidc: true - flags: rs-e2e - files: ${{ github.workspace }}/cache/rs-e2e-coverage.lcov diff --git a/Makefile b/Makefile index 447925062..e68056d57 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,12 @@ test-e2e-convert: echo "No Go in-container coverage found, skipping Go coverage conversion"; \ fi find ${PWD}/ak-platform-e2e/coverage - $(call rs_e2e_coverage_convert) + bash hack/rs-coverage-convert.sh "${PWD}/ak-platform-e2e/coverage/rs" rs-e2e \ + "${PWD}/bin/cli/ak" "${PWD}/bin/agent/ak-agent" \ + "${PWD}/bin/nss/libnss_authentik.so" "${PWD}/bin/pam/libpam_authentik.so" + +rs-build-coverage-convert: + $(call rs_build_coverage_convert) test-setup: go run -v ./cmd/cli setup -v http://authentik:9000 diff --git a/common.mk b/common.mk index dccc08d10..f571be1a8 100644 --- a/common.mk +++ b/common.mk @@ -57,6 +57,8 @@ endef ifeq ($(PLATFORM),gnu/linux) define cargo_build + rm -rf "$(CONTAINER_TOP)cache/build-profraw" + mkdir -p "$(CONTAINER_TOP)cache/build-profraw" docker run --rm \ -i \ --volume "$(CONTAINER_TOP):/workspace" \ @@ -65,6 +67,7 @@ define cargo_build --env AK_VERSION="${VERSION}" \ --env AK_BUILDHASH="${VERSION_HASH}" \ --env AK_TAG="${VERSION_TAG}" \ + --env LLVM_PROFILE_FILE="/workspace/cache/build-profraw/%m_%p.profraw" \ $(DOCKER_BUILDER_IMAGE) \ cargo build \ --target-dir /workspace/cache/shared \ @@ -103,35 +106,15 @@ define cargo_test --output-dir "${PWD}/cache/llvm-cov-html/" endef -define rs_e2e_coverage_convert +define rs_build_coverage_convert mkdir -p "${PWD}/cache" - PROFRAW_FILES=$$(find "${PWD}/ak-platform-e2e/coverage/rs" -name '*.profraw' 2>/dev/null | tr '\n' ' '); \ - if [ -z "$$PROFRAW_FILES" ]; then \ - echo "No Rust profraw files found in ak-platform-e2e/coverage/rs, creating empty coverage file"; \ - touch "${PWD}/cache/rs-e2e-coverage.lcov"; \ - else \ - HOST=$$(rustc -vV 2>/dev/null | awk '/^host:/{print $$2}'); \ - TOOLCHAIN=$$(rustup show active-toolchain 2>/dev/null | awk '{print $$1}'); \ - LLVM_DIR=$$(rustup show home 2>/dev/null)/toolchains/$$TOOLCHAIN/lib/rustlib/$$HOST/bin; \ - $$LLVM_DIR/llvm-profdata merge -sparse $$PROFRAW_FILES \ - -o "${PWD}/cache/rs-e2e-merged.profdata"; \ - OBJECTS=""; \ - for bin in "${PWD}/bin/cli/ak" "${PWD}/bin/agent/ak-agent" \ - "${PWD}/bin/nss/libnss_authentik.so" "${PWD}/bin/pam/libpam_authentik.so"; do \ - if [ -f "$$bin" ]; then OBJECTS="$$OBJECTS -object $$bin"; fi; \ - done; \ - if [ -z "$$OBJECTS" ]; then \ - echo "No instrumented Rust binaries found in bin/, creating empty coverage file"; \ - touch "${PWD}/cache/rs-e2e-coverage.lcov"; \ - else \ - $$LLVM_DIR/llvm-cov export \ - -format=lcov \ - -instr-profile="${PWD}/cache/rs-e2e-merged.profdata" \ - $$OBJECTS \ - -ignore-filename-regex='generated|\.cargo' \ - > "${PWD}/cache/rs-e2e-coverage.lcov"; \ - fi; \ - fi + docker run --rm \ + -i \ + --volume "$(CONTAINER_TOP):/workspace" \ + --workdir /workspace \ + $(DOCKER_BUILDER_IMAGE) \ + bash hack/rs-coverage-convert.sh cache/build-profraw rs-build \ + "newest:cache/shared/release/build:*/ak-cli-*/build-script-build" endef TME := docker exec authentik-platform_devcontainer-test-machine-1 diff --git a/hack/rs-coverage-convert.sh b/hack/rs-coverage-convert.sh new file mode 100755 index 000000000..d16fa2a16 --- /dev/null +++ b/hack/rs-coverage-convert.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -euo pipefail + +# Usage: rs-coverage-convert.sh [object ...] +# +# Each trailing "object" is either a plain path to an instrumented binary/library +# (included if it exists), or "newest::" to resolve to +# the most-recently-modified file under matching +# (used to find Cargo's hashed build-script-build output). + +PROFRAW_DIR="$1" +OUTPUT_BASENAME="$2" +shift 2 + +mkdir -p "${PWD}/cache" + +PROFRAW_FILES=$(find "$PROFRAW_DIR" -name '*.profraw' 2>/dev/null | tr '\n' ' ') + +if [ -z "$PROFRAW_FILES" ]; then + echo "No Rust profraw files found in ${PROFRAW_DIR}, creating empty coverage file" + touch "${PWD}/cache/${OUTPUT_BASENAME}-coverage.lcov" + exit 0 +fi + +OBJECTS="" +for arg in "$@"; do + case "$arg" in + newest:*) + spec="${arg#newest:}" + search_dir="${spec%%:*}" + path_pattern="${spec#*:}" + resolved=$(find "$search_dir" -path "$path_pattern" -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-) + if [ -n "$resolved" ]; then OBJECTS="$OBJECTS -object $resolved"; fi + ;; + *) + if [ -f "$arg" ]; then OBJECTS="$OBJECTS -object $arg"; fi + ;; + esac +done + +if [ -z "$OBJECTS" ]; then + echo "No instrumented Rust binaries found, creating empty coverage file" + touch "${PWD}/cache/${OUTPUT_BASENAME}-coverage.lcov" + exit 0 +fi + +HOST=$(rustc -vV 2>/dev/null | awk '/^host:/{print $2}') +TOOLCHAIN=$(rustup show active-toolchain 2>/dev/null | awk '{print $1}') +LLVM_DIR="$(rustup show home 2>/dev/null)/toolchains/${TOOLCHAIN}/lib/rustlib/${HOST}/bin" + +"${LLVM_DIR}/llvm-profdata" merge -sparse ${PROFRAW_FILES} \ + -o "${PWD}/cache/${OUTPUT_BASENAME}-merged.profdata" + +"${LLVM_DIR}/llvm-cov" export \ + -format=lcov \ + -instr-profile="${PWD}/cache/${OUTPUT_BASENAME}-merged.profdata" \ + ${OBJECTS} \ + -ignore-filename-regex='generated|\.cargo' \ + > "${PWD}/cache/${OUTPUT_BASENAME}-coverage.lcov"