diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9423e48..1dddf9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,15 +2,25 @@ # (both look under .github/workflows/). Every non-checkout step just invokes a # bash script under scripts/ci/, so the entire pipeline is reproducible by # running the same scripts locally - no marketplace build/test/push actions. +# +# Gitea (origin) is the internal/testing remote and runs the full pipeline: +# build, test, docker push, deploy-to-qa, smoke test. GitHub is the public +# mirror and only needs to prove the code builds and tests pass - it has no +# registry secrets and no [self-hosted, qa] runner, so the docker push and +# deploy/smoke jobs are skipped there via the `github.server_url` check +# below (identical on both engines: https://github.com on GitHub, the Gitea +# instance URL on Gitea). name: CI on: push: - branches: [main, build-runner-fix] + paths-ignore: [badges/**] jobs: build-and-push: runs-on: ubuntu-latest + permissions: + contents: write env: REGISTRY: ${{ secrets.REGISTRY }} REGISTRY_OWNER: ${{ secrets.REGISTRY_OWNER }} @@ -25,14 +35,25 @@ jobs: - name: Test run: ./scripts/ci/test.sh + - name: Coverage report + run: ./scripts/ci/coverage.sh + + - name: Publish coverage badge + env: + GITHUB_TOKEN: ${{ github.token }} + run: ./scripts/ci/publish-coverage-badge.sh + - name: Build Docker images + if: github.server_url != 'https://github.com' && github.ref_name == 'main' run: ./scripts/ci/docker-build.sh - name: Push Docker images + if: github.server_url != 'https://github.com' && github.ref_name == 'main' run: ./scripts/ci/docker-push.sh deploy-qa: needs: build-and-push + if: github.server_url != 'https://github.com' && github.ref_name == 'main' runs-on: [self-hosted, qa] env: REGISTRY: ${{ secrets.REGISTRY }} @@ -56,6 +77,7 @@ jobs: smoke-qa: needs: deploy-qa + if: github.server_url != 'https://github.com' runs-on: [self-hosted, qa] env: QA_ADMIN_PORT: ${{ vars.QA_ADMIN_PORT }} diff --git a/.gitignore b/.gitignore index 452a514..bda6c7e 100755 --- a/.gitignore +++ b/.gitignore @@ -476,3 +476,6 @@ ehthumbs.db # Self-installed .NET SDK (scripts/ci/lib.sh ensure_dotnet, used when a CI runner lacks the SDK) /.dotnet/ + +# Self-installed reportgenerator CLI (scripts/ci/lib.sh ensure_reportgenerator) +/.dotnet-tools/ diff --git a/badges/coverage.svg b/badges/coverage.svg new file mode 100644 index 0000000..4b7fad5 --- /dev/null +++ b/badges/coverage.svg @@ -0,0 +1,138 @@ + + + Code coverage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Generated by: ReportGenerator 5.5.10.0 + + + + Coverage + Coverage + 29.8%29.8% + + + + + + + Line coverage + + + + + \ No newline at end of file diff --git a/readme.md b/readme.md index 154ed50..e34021b 100755 --- a/readme.md +++ b/readme.md @@ -1,5 +1,9 @@ # MicCheck +[![GitHub CI](https://github.com/wamplerj/mic-check/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/wamplerj/mic-check/actions/workflows/ci.yml) +[![Gitea CI](https://git.wampler.us/wamplerj/mic-check/actions/workflows/ci.yml/badge.svg?branch=main)](https://git.wampler.us/wamplerj/mic-check/actions?workflow=ci.yml) +![Coverage](badges/coverage.svg) + Open source feature flag management platform. Manage projects, environments, feature flags, segments, and identities across your apps. Built with .NET (API) and Vue.js + Vuetify (admin UI). diff --git a/scripts/ci/coverage.sh b/scripts/ci/coverage.sh new file mode 100755 index 0000000..8590a77 --- /dev/null +++ b/scripts/ci/coverage.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Merges the .NET (coverlet/Cobertura) and admin (Jest/lcov) coverage output +# produced by test.sh into one report via reportgenerator, prints a summary, +# appends a build-report summary when running under Actions, and refreshes +# the coverage badge committed at badges/coverage.svg. Readme embeds that +# badge via a relative path, which resolves on both GitHub and Gitea since +# the same repo content is pushed to both remotes. +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh +cd "$CI_ROOT" + +ensure_dotnet +ensure_reportgenerator + +REPORT_DIR="$CI_ROOT/coverage/report" + +log "Merging coverage reports with reportgenerator" +reportgenerator \ + -reports:"coverage/dotnet/**/coverage.cobertura.xml;src/admin/coverage/lcov.info" \ + -targetdir:"$REPORT_DIR" \ + -reporttypes:"Badges;MarkdownSummaryGithub;TextSummary" + +cat "$REPORT_DIR/Summary.txt" + +if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + cat "$REPORT_DIR/SummaryGithub.md" >> "$GITHUB_STEP_SUMMARY" +fi + +mkdir -p "$CI_ROOT/badges" +cp "$REPORT_DIR/badge_linecoverage.svg" "$CI_ROOT/badges/coverage.svg" + +log "coverage.sh complete" diff --git a/scripts/ci/lib.sh b/scripts/ci/lib.sh index 14f1337..4c6c12f 100755 --- a/scripts/ci/lib.sh +++ b/scripts/ci/lib.sh @@ -125,6 +125,22 @@ ensure_node() { export PATH="$install_dir/bin:$PATH" } +# Installs the dotnet-reportgenerator-globaltool CLI (merges coverlet/Jest +# coverage output into badges + build-summary markdown) into $CI_ROOT/.dotnet-tools +# if it isn't already on PATH. Mirrors ensure_dotnet()/ensure_node() above. +ensure_reportgenerator() { + if command -v reportgenerator > /dev/null 2>&1; then + return 0 + fi + + local tool_dir="$CI_ROOT/.dotnet-tools" + if [[ ! -x "$tool_dir/reportgenerator" ]]; then + log "reportgenerator not found on PATH; installing dotnet-reportgenerator-globaltool" + dotnet tool install dotnet-reportgenerator-globaltool --tool-path "$tool_dir" + fi + export PATH="$tool_dir:$PATH" +} + # The IP address on which a container published on 0.0.0.0/ is # reachable from a sibling container on docker's default bridge network (i.e. # the docker host's bridge-side address, not its public interface). Used to diff --git a/scripts/ci/publish-coverage-badge.sh b/scripts/ci/publish-coverage-badge.sh new file mode 100755 index 0000000..3e5af7f --- /dev/null +++ b/scripts/ci/publish-coverage-badge.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Commits the coverage badge refreshed by coverage.sh straight back to the +# branch that triggered this run, so readme.md's relative badges/coverage.svg +# link stays current. GITHUB_SERVER_URL/GITHUB_REPOSITORY/GITHUB_REF_NAME are +# default context env vars on both GitHub Actions and Gitea Actions (Gitea's +# engine is GitHub-Actions-compatible); GITHUB_TOKEN must be passed in +# explicitly from the workflow (${{ github.token }}) on both platforms. +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh +cd "$CI_ROOT" + +[[ -n "${GITHUB_TOKEN:-}" ]] || fail "GITHUB_TOKEN env var is required to push the badge commit" +[[ -n "${GITHUB_SERVER_URL:-}" ]] || fail "GITHUB_SERVER_URL env var is required to push the badge commit" +[[ -n "${GITHUB_REPOSITORY:-}" ]] || fail "GITHUB_REPOSITORY env var is required to push the badge commit" +[[ -n "${GITHUB_REF_NAME:-}" ]] || fail "GITHUB_REF_NAME env var is required to push the badge commit" + +if git diff --quiet -- badges/coverage.svg; then + log "badges/coverage.svg unchanged; nothing to publish" + exit 0 +fi + +git config user.name "miccheck-ci" +git config user.email "ci@miccheck.local" +git add badges/coverage.svg +git commit -m "chore: refresh coverage badge [skip ci]" + +remote_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" +git -c http.extraheader="AUTHORIZATION: bearer ${GITHUB_TOKEN}" push "$remote_url" "HEAD:${GITHUB_REF_NAME}" + +log "publish-coverage-badge.sh complete" diff --git a/scripts/ci/test.sh b/scripts/ci/test.sh index e8c6cf7..67e026e 100755 --- a/scripts/ci/test.sh +++ b/scripts/ci/test.sh @@ -9,9 +9,10 @@ cd "$CI_ROOT" ensure_dotnet log "Running MicCheck.Api.Tests.Unit" -dotnet test tests/api/MicCheck.Api.Tests.Unit/MicCheck.Api.Tests.Unit.csproj -c Release --logger trx +dotnet test tests/api/MicCheck.Api.Tests.Unit/MicCheck.Api.Tests.Unit.csproj -c Release --logger trx \ + --collect:"XPlat Code Coverage" --results-directory "$CI_ROOT/coverage/dotnet" log "Running admin Jest tests" -npm --prefix src/admin test +npm --prefix src/admin test -- --coverage --coverageReporters=lcov --coverageReporters=text-summary log "test.sh complete"