From 1aac22e30c04f6eaca9da8ecbb5127b725e7185d Mon Sep 17 00:00:00 2001 From: ademboukabes Date: Tue, 13 Oct 2026 01:19:03 +0100 Subject: [PATCH 1/3] ci: add coverage reporting, step summary, and artifacts --- .github/workflows/ci.yml | 26 +++++++++++++- .gitignore | 1 + Makefile | 10 +++++- scripts/coverage-summary.sh | 68 +++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100755 scripts/coverage-summary.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa5bdb9..baf8ae1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,31 @@ jobs: cache: true - run: go vet ./... - - run: go test -race ./... + - name: test with race detector and coverage + run: go test -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: report coverage summary + run: ./scripts/coverage-summary.sh coverage.out + + - name: upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.out + fail_ci_if_error: false + + - name: generate html coverage report + run: go tool cover -html=coverage.out -o coverage.html + + - name: upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + coverage.out + coverage.html + retention-days: 14 + - run: make build - name: validate the sample event diff --git a/.gitignore b/.gitignore index e3e372c..aad4003 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ Thumbs.db /dist/ *.test *.out +coverage.html # goreleaser build output dist/ diff --git a/Makefile b/Makefile index f1c0db0..2c9b6c4 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ LDFLAGS := -X github.com/MicroClub-USTHB/mctf-cli/cmd.version=$(VERSION) \ -X github.com/MicroClub-USTHB/mctf-cli/cmd.commit=$(COMMIT) \ -X github.com/MicroClub-USTHB/mctf-cli/cmd.date=$(DATE) -.PHONY: build test test-integration lint tidy run clean schemas man +.PHONY: build test test-coverage coverage-html test-integration lint tidy run clean schemas man build: schemas go build -ldflags "$(LDFLAGS)" -o mctf . @@ -28,6 +28,14 @@ schemas: test: go test ./... +test-coverage: + go test -race -coverprofile=coverage.out -covermode=atomic ./... + @./scripts/coverage-summary.sh coverage.out + +coverage-html: test-coverage + go tool cover -html=coverage.out -o coverage.html + @echo "HTML coverage report written to coverage.html" + # Integration tests (gated by the `integration` build tag). Boots a real # sshd container + exercises the SSH runner end-to-end. Requires docker # daemon + ssh/scp/ssh-keygen on PATH; skips gracefully otherwise. diff --git a/scripts/coverage-summary.sh b/scripts/coverage-summary.sh new file mode 100755 index 0000000..578623e --- /dev/null +++ b/scripts/coverage-summary.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +# scripts/coverage-summary.sh +# Parses Go coverprofile and generates a Markdown summary table for GitHub Actions step summary +# and terminal output. + +COV_FILE="${1:-coverage.out}" + +if [ ! -f "$COV_FILE" ]; then + echo "Error: coverage profile '$COV_FILE' not found" >&2 + exit 1 +fi + +TOTAL=$(go tool cover -func="$COV_FILE" | grep "^total:" | awk '{print $NF}') + +PKG_ROWS=$(awk ' +NR > 1 { + split($1, parts, "/") + pkg = "" + for (i = 4; i < length(parts); i++) { + if (pkg != "") pkg = pkg "/" parts[i] + else pkg = parts[i] + } + if (pkg == "") { + if (length(parts) == 4) pkg = "." + else pkg = parts[4] + } + stmts = $2 + 0 + count = $3 + 0 + pkg_stmts[pkg] += stmts + if (count > 0) { + pkg_covered[pkg] += stmts + } +} +END { + for (p in pkg_stmts) { + if (pkg_stmts[p] == 0) continue + pct = (pkg_covered[p] / pkg_stmts[p]) * 100 + icon = "🔴" + if (pct >= 70.0) icon = "🟢" + else if (pct >= 30.0) icon = "🟡" + printf "| `%s` | %.1f%% | %s |\n", p, pct, icon + } +} +' "$COV_FILE" | sort) + +SUMMARY_MD=$(cat <> "$GITHUB_STEP_SUMMARY" + echo "$SUMMARY_MD" >> "$GITHUB_STEP_SUMMARY" +fi From ffd8b2385e6ca3cecd0e0b8ea3ec772ef1506141 Mon Sep 17 00:00:00 2001 From: ademboukabes Date: Tue, 13 Oct 2026 01:19:13 +0100 Subject: [PATCH 2/3] docs: document local test coverage workflow and update backlog --- CONTRIBUTING.md | 1 + docs/BACKLOG.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f30503..162c4de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,6 +105,7 @@ Two cases: ## Tests - `go test ./...` runs the unit tests. Add new tests next to the package they cover. +- `make test-coverage` runs the suite with the race detector, generates `coverage.out`, and prints a per-package statement coverage summary. `make coverage-html` generates a browser-viewable report at `coverage.html`. - Tests should not require network, Docker, or a running CTFd. If you need fixtures, put them under `internal//testdata/`. - For interactive prompts (e.g., `cmd/init.go`), inject test input via the `promptIn` variable — see `cmd/init_test.go` for the pattern. - The sample event under `examples/sample-event/` is the integration fixture; CI uses it directly. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index b47effb..e7fd56a 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -41,7 +41,7 @@ Open work organized by size. Each line is intentionally GitHub-issue-shaped: tit ## CI / quality - **golangci-lint** in CI (`go vet` is currently the only static check). -- **Coverage reporting** — `go test -coverprofile` upload to Codecov / similar. +- ~~**Coverage reporting** — `go test -coverprofile` upload to Codecov / similar.~~ **Shipped:** CI runs atomic coverprofile, reports step summary, uploads to Codecov + artifacts, with local `make test-coverage`. - **Fuzz tests** for the manifest loader and the compose port parser. - **End-to-end integration test** — sample-event fixture: validate → generate → build → scan → deploy → solve → down. Real docker daemon, gated on an `e2e` build tag. From 4655962132120e5c6e33a280bf558c13afbbcfa7 Mon Sep 17 00:00:00 2001 From: ademboukabes Date: Tue, 13 Oct 2026 01:22:16 +0100 Subject: [PATCH 3/3] ci: replace emojis with plain text status tags in coverage summary --- scripts/coverage-summary.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/coverage-summary.sh b/scripts/coverage-summary.sh index 578623e..25977ba 100755 --- a/scripts/coverage-summary.sh +++ b/scripts/coverage-summary.sh @@ -37,16 +37,16 @@ END { for (p in pkg_stmts) { if (pkg_stmts[p] == 0) continue pct = (pkg_covered[p] / pkg_stmts[p]) * 100 - icon = "🔴" - if (pct >= 70.0) icon = "🟢" - else if (pct >= 30.0) icon = "🟡" - printf "| `%s` | %.1f%% | %s |\n", p, pct, icon + status = "LOW" + if (pct >= 70.0) status = "GOOD" + else if (pct >= 30.0) status = "WARN" + printf "| `%s` | %.1f%% | %s |\n", p, pct, status } } ' "$COV_FILE" | sort) SUMMARY_MD=$(cat <