feat(stats): add optional ow_stats module and demo5 #116
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: Code Quality | |
| on: | |
| push: | |
| branches: [ "master", "main" ] | |
| pull_request: | |
| branches: [ "master", "main" ] | |
| jobs: | |
| format: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install clang-format (pinned) | |
| run: pip install clang-format==18.1.8 | |
| - name: Check code formatting | |
| run: clang-format --dry-run --Werror inc/ds18b20.h inc/onewire.h inc/app.h src/ds18b20.c src/onewire.c src/app.c src/demo.c src/demo2.c src/demo3.c tests/mock/*.h tests/mock/*.c tests/test/*.c | |
| host-tests: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| backend: [f1, f0, g0] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install host GCC | |
| run: sudo apt-get update && sudo apt-get install -y gcc | |
| - name: Download CMSIS dependencies | |
| # All families up front: the host suites compile against mocks, but | |
| # keeping every backend's dependency set present lets any matrix cell | |
| # configure either target without re-downloading. | |
| run: | | |
| make download-deps | |
| make OW_TARGET=f0 download-deps | |
| make OW_TARGET=g0 download-deps | |
| - name: Build and run host tests (F1) | |
| if: matrix.backend == 'f1' | |
| run: make test | |
| - name: Build and run host tests (F0) | |
| if: matrix.backend == 'f0' | |
| run: make test-f0 | |
| - name: Build and run host tests (G0) | |
| if: matrix.backend == 'g0' | |
| run: make test-g0 | |
| coverage: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| backend: [f1, f0, g0] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install host GCC and lcov | |
| run: sudo apt-get update && sudo apt-get install -y gcc lcov | |
| - name: Download CMSIS dependencies | |
| run: | | |
| make download-deps | |
| make OW_TARGET=f0 download-deps | |
| make OW_TARGET=g0 download-deps | |
| - name: Build and run host tests with coverage | |
| # COVERAGE=1 adds --coverage (gcov instrumentation) to the host test | |
| # build. The test executables run from the repository root, so the | |
| # .gcno/.gcda files land there as well — capture scans the whole | |
| # checkout and the merge step filters out everything that is not the | |
| # driver core. `make clean` first guarantees a fresh instrumented | |
| # build. | |
| run: | | |
| make clean | |
| if [ "${{ matrix.backend }}" = "f0" ]; then | |
| make test-f0 COVERAGE=1 | |
| elif [ "${{ matrix.backend }}" = "g0" ]; then | |
| make test-g0 COVERAGE=1 | |
| else | |
| make test COVERAGE=1 | |
| fi | |
| - name: Capture coverage trace | |
| run: lcov --capture --directory . --output-file coverage-${{ matrix.backend }}.info --rc branch_coverage=1 | |
| - name: Upload coverage trace | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-traces-${{ matrix.backend }} | |
| path: coverage-${{ matrix.backend }}.info | |
| coverage-badge: | |
| needs: coverage | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| # genhtml annotates the merged report with source lines, so the | |
| # checkout must be present (the trace stores runner workspace paths, | |
| # which match $GITHUB_WORKSPACE exactly). | |
| - uses: actions/checkout@v5 | |
| - name: Install lcov | |
| run: sudo apt-get update && sudo apt-get install -y lcov | |
| - name: Download coverage traces | |
| uses: actions/download-artifact@v7 | |
| with: | |
| pattern: coverage-traces-* | |
| path: traces | |
| - name: Merge and filter traces | |
| # Union of every backend's trace measures the MCU-independent core; | |
| # port headers (register plumbing per family) stay out of the metric | |
| # so adding new MCU ports never dilutes the number. | |
| run: | | |
| ARGS="" | |
| for f in traces/*/coverage-*.info; do | |
| ARGS="$ARGS -a $f" | |
| done | |
| lcov $ARGS --output-file union.info --rc branch_coverage=1 | |
| lcov --remove union.info '*/tests/*' '/usr/*' '*/inc/macro.h' '*/port/*' \ | |
| --output-file core.info --rc branch_coverage=1 --ignore-errors unused | |
| lcov --summary core.info --rc branch_coverage=1 > summary.txt || true | |
| cat summary.txt | |
| - name: Generate HTML report and badge SVG | |
| run: | | |
| mkdir -p publish/coverage | |
| genhtml core.info --output-directory publish/coverage \ | |
| --branch-coverage --title "stm32-async-1wire core coverage" | |
| python3 - <<'PY' | |
| import re | |
| text = open("summary.txt").read() | |
| m = re.search(r"lines\.+: ([\d.]+)%", text) | |
| pct = int(round(float(m.group(1)))) | |
| color = "#4c1" if pct >= 90 else "#dfb317" if pct >= 75 else "#cb2431" | |
| svg = ( | |
| '<svg xmlns="http://www.w3.org/2000/svg" width="112" height="20" role="img">' | |
| '<linearGradient id="g" x2="0" y2="100%">' | |
| '<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>' | |
| '<stop offset="1" stop-opacity=".1"/></linearGradient>' | |
| '<clipPath id="r"><rect width="112" height="20" rx="3" fill="#fff"/></clipPath>' | |
| f'<g clip-path="url(#r)"><rect width="69" height="20" fill="#555"/>' | |
| f'<rect x="69" width="43" height="20" fill="{color}"/>' | |
| '<rect width="112" height="20" fill="url(#g)"/></g>' | |
| '<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">' | |
| '<text x="34.5" y="15">coverage</text>' | |
| f'<text x="89.5" y="15">{pct}%</text></g></svg>' | |
| ) | |
| open("publish/coverage-badge.svg", "w").write(svg) | |
| # The report itself lives under coverage/; give the Pages root a | |
| # redirecting landing page so the bare URL works. | |
| open("publish/index.html", "w").write( | |
| '<!doctype html>\n<html lang="en">\n<head>\n' | |
| '<meta charset="utf-8">\n' | |
| '<title>stm32-async-1wire coverage</title>\n' | |
| '<meta http-equiv="refresh" content="0; url=coverage/">\n' | |
| '<link rel="canonical" href="coverage/">\n</head>\n<body>\n' | |
| '<p>Redirecting to the <a href="coverage/">coverage report' | |
| '…</a></p>\n</body>\n</html>\n' | |
| ) | |
| print("badge:", pct, "%") | |
| PY | |
| - name: Publish to gh-pages | |
| env: | |
| REPO_URL: https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git | |
| run: | | |
| set -e | |
| DIR=/tmp/gh-pages | |
| rm -rf "$DIR" | |
| if git clone --depth 1 --branch gh-pages "$REPO_URL" "$DIR" 2>/dev/null; then | |
| cd "$DIR" | |
| git rm -rq . | |
| else | |
| git init -q -b gh-pages "$DIR" | |
| cd "$DIR" | |
| git remote add origin "$REPO_URL" | |
| echo "stm32-async-1wire coverage pages" > .placeholder.md | |
| fi | |
| cp -r "$GITHUB_WORKSPACE"/publish/* . | |
| git add -A | |
| git -c user.name="github-actions[bot]" \ | |
| -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \ | |
| commit -qm "docs(coverage): refresh report and badge (${GITHUB_SHA::7})" \ | |
| || echo "nothing to commit" | |
| git push -q origin gh-pages | |
| cppcheck: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install cppcheck | |
| run: sudo apt-get update && sudo apt-get install -y cppcheck | |
| - name: Download CMSIS dependencies | |
| run: make download-deps | |
| - name: Run static analysis | |
| run: cppcheck --enable=warning,style,performance,portability --error-exitcode=1 --suppress=missingIncludeSystem --suppress=unusedFunction --suppress=constVariablePointer --suppress=constParameterPointer --suppress=comparePointers -I. -Iinc -ICMSIS/core -ICMSIS/device -DSTM32F103xB -D OW_PORT_TARGET_F1 -D__GNUC__ src/ds18b20.c src/onewire.c src/app.c src/demo.c src/demo2.c src/demo3.c | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install ARM GCC | |
| uses: carlosperate/arm-none-eabi-gcc-action@v1 | |
| - name: Cache CMSIS dependencies | |
| id: cache-deps | |
| uses: actions/cache@v5 | |
| with: | |
| path: CMSIS/ | |
| key: ${{ runner.os }}-cmsis-${{ hashFiles('Makefile') }} | |
| - name: Download dependencies | |
| if: steps.cache-deps.outputs.cache-hit != 'true' | |
| run: make download-deps | |
| - name: Build release (demo) | |
| run: make | |
| - name: Build release (demo2) | |
| run: make APP=demo2 | |
| - name: Build release (demo3) | |
| run: make APP=demo3 | |
| # Backend-specific register plumbing (e.g. the F0 GPIO BSRR bit naming) | |
| # only compiles into real firmware here — host suites run against mocks. | |
| # Smoke-build both backends and both clock branches of app.c so a | |
| # family-only breakage cannot reach a tag. | |
| - name: Build release (F0 backend) | |
| run: make clean && make OW_TARGET=f0 | |
| - name: Build release (F0 backend, 8MHz RC) | |
| run: make clean && make OW_TARGET=f0 SYSCLK_MHZ=8 | |
| - name: Build release (G0 backend) | |
| run: make clean && make OW_TARGET=g0 | |
| - name: Build release (G0 backend, 16MHz RC) | |
| run: make clean && make OW_TARGET=g0 SYSCLK_MHZ=16 | |
| - name: Build with GCC analyzer | |
| run: make clean && make all EXT="-fanalyzer" | |
| continue-on-error: true |