chore: add coverage tracking for multi-platform-controller #354
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Generate Konflux Coverage Dashboard | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: '0 3 * * *' # Daily at 03:00 UTC | |
| workflow_dispatch: | |
| jobs: | |
| generate-coverage: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: read # Allow reading PR information | |
| steps: | |
| - name: Checkout this repo (dashboard) | |
| uses: actions/checkout@v4 | |
| with: | |
| # For pull requests, this will checkout the PR branch with merge commit | |
| # For push events, this will checkout the pushed branch | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.23' | |
| cache: false # Disable caching since this isn't a Go project | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y jq | |
| pip install yq | |
| - name: Install and setup envtest | |
| run: | | |
| # Install setup-envtest | |
| go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest | |
| # Setup envtest binaries and get the path | |
| ENVTEST_ASSETS_DIR=$(setup-envtest use -p path) | |
| echo "KUBEBUILDER_ASSETS=$ENVTEST_ASSETS_DIR" >> $GITHUB_ENV | |
| echo "Envtest assets installed at: $ENVTEST_ASSETS_DIR" | |
| - name: Prepare workspace | |
| run: mkdir workspace | |
| - name: Checkout gh-pages | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: gh-pages | |
| path: gh-pages | |
| - name: Clone repos and calculate coverage | |
| run: | | |
| cd workspace | |
| echo "[" > ../_temp_coverage.json | |
| OLD_COVERAGE_JSON=$(cat ../gh-pages/coverage.json || echo "[]") | |
| # Iterate through all repository configuration files | |
| for config_file in ../repos/*.yaml; do | |
| REPO=$(yq -r '.name' "$config_file") | |
| REPO_NAME=$(basename "$REPO") | |
| echo "→ Processing $REPO (from $(basename "$config_file"))..." | |
| # Get exclude patterns BEFORE entering repo directory | |
| EXCLUDE_DIRS_ARRAY=$(yq '.exclude_dirs // []' "$config_file") | |
| EXCLUDE_FILES=$(yq '.exclude_files // []' "$config_file") | |
| echo "→ Cloning $REPO..." | |
| git clone --depth 1 "https://github.com/$REPO.git" | |
| cd "$REPO_NAME" | |
| # Build the exclude pattern more carefully | |
| EXCLUDE_PATTERN="" | |
| if [ "$EXCLUDE_DIRS_ARRAY" != "[]" ]; then | |
| # Process each exclude pattern individually | |
| PATTERN_COUNT=$(echo "$EXCLUDE_DIRS_ARRAY" | yq 'length') | |
| PATTERNS="" | |
| for k in $(seq 0 $((PATTERN_COUNT - 1))); do | |
| PATTERN=$(echo "$EXCLUDE_DIRS_ARRAY" | yq ".[$k]" | tr -d '"') | |
| echo " Processing exclude pattern: '$PATTERN'" | |
| # Handle different pattern types | |
| case "$PATTERN" in | |
| */*) | |
| # Pattern with slashes - treat as path pattern | |
| if [[ "$PATTERN" == /* ]]; then | |
| # Starts with slash, use as-is but escape special chars | |
| ESCAPED_PATTERN=$(echo "$PATTERN" | sed 's/[[\.*^$()+{}|]/\\&/g') | |
| else | |
| # Add leading slash and treat trailing slash specially | |
| if [[ "$PATTERN" == */ ]]; then | |
| # Ends with slash - match directory component | |
| DIR_NAME=${PATTERN%/} | |
| ESCAPED_PATTERN="/${DIR_NAME}(/|$)" | |
| else | |
| # Has slash but doesn't end with one | |
| ESCAPED_PATTERN="/$(echo "$PATTERN" | sed 's/[[\.*^$()+{}|]/\\&/g')" | |
| fi | |
| fi | |
| ;; | |
| *) | |
| # Simple directory name - match as path component | |
| ESCAPED_PATTERN="/${PATTERN}(/|$)" | |
| ;; | |
| esac | |
| echo " Escaped pattern: '$ESCAPED_PATTERN'" | |
| if [ -z "$PATTERNS" ]; then | |
| PATTERNS="$ESCAPED_PATTERN" | |
| else | |
| PATTERNS="$PATTERNS|$ESCAPED_PATTERN" | |
| fi | |
| done | |
| EXCLUDE_PATTERN="$PATTERNS" | |
| echo " Final exclude pattern: '$EXCLUDE_PATTERN'" | |
| fi | |
| # Get packages and apply exclusions | |
| ALL_PACKAGES=$(go list ./... 2>/dev/null || true) | |
| echo " All packages found:" | |
| echo "$ALL_PACKAGES" | while read pkg; do echo " $pkg"; done | |
| if [ -n "$EXCLUDE_PATTERN" ] && [ -n "$ALL_PACKAGES" ]; then | |
| echo " Applying exclusion pattern: $EXCLUDE_PATTERN" | |
| INCLUDED_PACKAGES=$(echo "$ALL_PACKAGES" | grep -vE "$EXCLUDE_PATTERN" || true) | |
| echo " Packages after exclusion:" | |
| echo "$INCLUDED_PACKAGES" | while read pkg; do [ -n "$pkg" ] && echo " $pkg"; done | |
| echo " Original packages: $(echo "$ALL_PACKAGES" | wc -l)" | |
| echo " Included packages: $(echo "$INCLUDED_PACKAGES" | wc -l)" | |
| else | |
| INCLUDED_PACKAGES="$ALL_PACKAGES" | |
| echo " No exclusion pattern applied" | |
| fi | |
| STATUS="ok" | |
| COVERAGE="0.0" | |
| if [ -n "$INCLUDED_PACKAGES" ]; then | |
| echo " Running tests with coverage..." | |
| # Fail fast - let test failures fail the workflow for visibility | |
| # Filter out packages with no test files to avoid covdata errors | |
| TESTABLE_PACKAGES="" | |
| for pkg in $INCLUDED_PACKAGES; do | |
| if go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.ImportPath}}{{end}}' $pkg 2>/dev/null | grep -q .; then | |
| TESTABLE_PACKAGES="$TESTABLE_PACKAGES $pkg" | |
| fi | |
| done | |
| if [ -z "$TESTABLE_PACKAGES" ]; then | |
| echo " ❌ No packages with test files found" | |
| STATUS="failed" | |
| COVERAGE=null | |
| else | |
| go test -coverprofile=coverage_raw.out $TESTABLE_PACKAGES | |
| fi | |
| if [ -f coverage_raw.out ]; then | |
| cp coverage_raw.out coverage.out | |
| # Remove excluded files from coverage | |
| FILE_COUNT=$(echo "$EXCLUDE_FILES" | yq 'length') | |
| for j in $(seq 0 $((FILE_COUNT - 1))); do | |
| FILE=$(echo "$EXCLUDE_FILES" | yq ".[$j]" | tr -d '"') | |
| echo " Removing $FILE from coverage" | |
| sed -i "/$FILE/d" coverage.out || true | |
| done | |
| COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%') | |
| # Extract per-package coverage by parsing coverage profile directly | |
| # This gives statement-weighted coverage, not function-count average | |
| PACKAGES_JSON=$(awk ' | |
| NR==1 { next } # Skip "mode: set" line | |
| { | |
| # Parse: file.go:start.col,end.col numStmts covered | |
| split($1, parts, ":") | |
| file = parts[1] | |
| numStmts = $2 | |
| covered = ($3 > 0) ? numStmts : 0 | |
| # Extract package path (everything before last /) | |
| n=split(file, pathParts, "/") | |
| pkg="" | |
| for(i=1; i<n; i++) { | |
| if(i>1) pkg = pkg "/" | |
| pkg = pkg pathParts[i] | |
| } | |
| # Accumulate covered and total statements per package | |
| pkgTotal[pkg] += numStmts | |
| pkgCovered[pkg] += covered | |
| } | |
| END { | |
| printf "[" | |
| first=1 | |
| for(pkg in pkgTotal) { | |
| if(!first) printf "," | |
| cov = (pkgTotal[pkg] > 0) ? (pkgCovered[pkg] / pkgTotal[pkg] * 100) : 0 | |
| printf "{\"package\":\"%s\",\"coverage\":%.1f}", pkg, cov | |
| first=0 | |
| } | |
| printf "]" | |
| }' coverage.out) | |
| # Generate HTML report | |
| go tool cover -html=coverage.out -o coverage.html | |
| mkdir -p ../../gh-pages/coverage/$REPO | |
| cp coverage.html ../../gh-pages/coverage/$REPO/index.html | |
| else | |
| echo " ❌ No coverage file generated - tests may not have run properly" | |
| STATUS="failed" | |
| COVERAGE=null | |
| PACKAGES_JSON="[]" | |
| fi | |
| else | |
| echo " ❌ No testable packages found" | |
| STATUS="failed" | |
| COVERAGE=null | |
| PACKAGES_JSON="[]" | |
| fi | |
| echo " → $REPO coverage: $COVERAGE% (status: $STATUS)" | |
| echo " {\"repo\": \"$REPO\", \"coverage\": $COVERAGE, \"status\": \"$STATUS\", \"packages\": $PACKAGES_JSON }," >> ../../_temp_coverage.json | |
| cd .. | |
| done | |
| cd .. | |
| sed -i '$ s/,$//' _temp_coverage.json | |
| echo "]" >> _temp_coverage.json | |
| - name: Wrap coverage.json with run_url | |
| run: | | |
| RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| jq -n \ | |
| --arg run_url "$RUN_URL" \ | |
| --argjson data "$(cat _temp_coverage.json)" \ | |
| '{run_url: $run_url, data: $data}' > coverage.json | |
| - name: Commit and push updated coverage.json | |
| # Only push to gh-pages from main branch pushes and scheduled runs (not on PRs) | |
| if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' | |
| run: | | |
| cp coverage.json gh-pages/coverage.json | |
| cp index.html gh-pages/index.html | |
| cd gh-pages | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@github.com" | |
| git add coverage.json coverage/ index.html | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "Update coverage data on $(date --utc)" | |
| git push origin gh-pages | |
| fi |