Add clang-format, clang-tidy, ASan/UBSan CI lanes

Format and tidy enforce only on lines changed in PRs (diff-only via
git-clang-format and clang-tidy-diff.py) — existing files keep their
current style until edited. Mass reformat deferred; .git-blame-ignore-revs
stub is in place for whenever that happens.

Sanitizer lane builds with -fsanitize=address,undefined and runs the
unit suite. continue-on-error: true initially so we can triage findings
without blocking PRs. UB categories pervasive in the Hash9 C cascade
(alignment, signed-integer-overflow, vptr) are suppressed pending
file-by-file fixes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 17:37:01 -07:00
parent 269498453e
commit 426e23d8be
5 changed files with 250 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
# Triangles code style.
# Conservative: do not reflow long lines, do not reorganize includes.
# This config is enforced *only on changed lines* via `git clang-format` in CI,
# so it shapes new/edited code without touching legacy files until they're touched.
BasedOnStyle: LLVM
Language: Cpp
Standard: c++17
IndentWidth: 4
TabWidth: 4
UseTab: Never
ContinuationIndentWidth: 4
AccessModifierOffset: -4
ColumnLimit: 0 # Don't reflow long lines — too disruptive for legacy code.
ReflowComments: false
BreakBeforeBraces: Attach
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
PointerAlignment: Left
DerivePointerAlignment: false
SpaceAfterCStyleCast: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeAssignmentOperators: true
NamespaceIndentation: None
FixNamespaceComments: true
# Includes: don't shuffle — header order in this codebase is load-bearing
# (e.g. main.cpp's mix of project + system headers carries platform meaning).
SortIncludes: false
IncludeBlocks: Preserve
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignTrailingComments: true
# Don't auto-add braces to single-statement bodies — too invasive.
InsertBraces: false
+46
View File
@@ -0,0 +1,46 @@
# Triangles clang-tidy config.
#
# Goal: catch real bugs in new/edited code without drowning in noise from
# legacy patterns. Enforced *diff-only* in CI (changed lines on PRs).
#
# Conservative starter set. Graduate checks to WarningsAsErrors only after
# the codebase is clean for that check.
Checks: >
-*,
bugprone-*,
performance-*,
readability-misleading-indentation,
readability-redundant-control-flow,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-redundant-string-init,
readability-string-compare,
modernize-use-nullptr,
modernize-use-override,
modernize-deprecated-headers,
cppcoreguidelines-init-variables,
cppcoreguidelines-pro-type-member-init,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-branch-clone,
-bugprone-signed-char-misuse,
-bugprone-reserved-identifier,
-bugprone-unchecked-optional-access,
-performance-no-int-to-ptr,
-performance-avoid-endl
# Warn-only initially. Once a check is clean repo-wide we can promote it here.
WarningsAsErrors: ''
# Run on project sources; skip vendored/generated code.
HeaderFilterRegex: '^.*src/(?!json/nlohmann_json|leveldb|lz4|tor/tor-src).*\.h$'
FormatStyle: file
CheckOptions:
- key: readability-identifier-naming.IgnoreMainLikeFunctions
value: '1'
- key: cppcoreguidelines-init-variables.IncludeStyle
value: 'google'
+11
View File
@@ -0,0 +1,11 @@
# Revisions listed here are skipped by `git blame` when --ignore-revs-file
# is configured. GitHub honors this file automatically.
#
# Add the SHA of any large mechanical reformat / rename / mass-style commit
# below, with a one-line comment.
#
# Example:
# abc1234567890abcdef # repo-wide clang-format (no behavior change)
#
# To enable locally:
# git config blame.ignoreRevsFile .git-blame-ignore-revs
+44
View File
@@ -37,6 +37,50 @@ jobs:
- name: Run unit tests
run: cd build && ctest --output-on-failure || true
test-linux-sanitizers:
# ASan + UBSan build of the daemon + unit tests. Allowed to fail until
# findings are triaged — see .github/workflows/lint.yml comment block.
# Once the test suite is clean under sanitizers, drop continue-on-error.
runs-on: ubuntu-22.04
continue-on-error: true
env:
# ASan: leak detection off by default (BDB and OpenSSL produce noise on shutdown).
# Re-enable once we've quieted the legitimate suspects.
ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1:abort_on_error=1:print_stacktrace=1:strict_string_checks=1:detect_stack_use_after_return=1"
# UBSan: print full stack traces on first error and exit non-zero.
UBSAN_OPTIONS: "halt_on_error=1:abort_on_error=1:print_stacktrace=1"
# Suppress UB categories that are pervasive in the Hash9 C cascade
# and BDB until they're fixed file-by-file.
SAN_FLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined -fno-sanitize=alignment,signed-integer-overflow,vptr"
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build \
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
libevent-dev libminiupnpc-dev zlib1g-dev
- name: Configure with sanitizers
run: |
cmake -B build-san -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="$SAN_FLAGS" \
-DCMAKE_CXX_FLAGS="$SAN_FLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="$SAN_FLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="$SAN_FLAGS" \
-DBUILD_QT=OFF \
-DBUILD_DAEMON=ON \
-DBUILD_TESTS=ON \
-DUSE_UPNP=OFF
- name: Build
run: cmake --build build-san -j$(nproc)
- name: Run unit tests under sanitizers
run: cd build-san && ctest --output-on-failure
build-windows-qt:
runs-on: windows-latest
defaults:
+100
View File
@@ -0,0 +1,100 @@
name: Lint
on:
pull_request:
branches: [master]
workflow_dispatch:
# Diff-only enforcement: clang-format and clang-tidy run only on lines changed
# in the PR. Existing files keep their current style until they're edited.
# See .clang-format and .clang-tidy for the rule sets.
jobs:
clang-format-diff:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
# Need merge-base with target branch to compute the diff.
fetch-depth: 0
- name: Install clang-format
run: |
sudo apt-get update
sudo apt-get install -y clang-format-15
sudo ln -sf /usr/bin/clang-format-15 /usr/local/bin/clang-format
- name: Check format on changed lines
run: |
BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD)
echo "Comparing against merge-base: $BASE_SHA"
# git-clang-format prints a diff if any changed line violates style.
# --diff exits non-zero when reformatting would change something.
OUTPUT=$(git clang-format --diff "$BASE_SHA" -- '*.cpp' '*.h' '*.hpp' '*.cc' || true)
if [ -z "$OUTPUT" ] || [ "$OUTPUT" = "no modified files to format" ] || [ "$OUTPUT" = "clang-format did not modify any files" ]; then
echo "clang-format: clean"
exit 0
fi
echo "::error::clang-format wants to change the following on lines you touched."
echo "Run \`git clang-format $BASE_SHA\` locally and commit the result."
echo "$OUTPUT"
exit 1
clang-tidy-diff:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies + clang-tidy
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build clang-tidy-15 \
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
libevent-dev libminiupnpc-dev zlib1g-dev
sudo ln -sf /usr/bin/clang-tidy-15 /usr/local/bin/clang-tidy
- name: Configure (export compile_commands.json)
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_QT=OFF \
-DBUILD_DAEMON=ON \
-DBUILD_TESTS=ON \
-DUSE_UPNP=OFF
- name: Generate build artifacts that headers depend on
# build.h, qt UI headers, etc. — clang-tidy needs them to parse sources.
run: cmake --build build --target generate_build_info
- name: Run clang-tidy on changed lines
run: |
BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD)
echo "Comparing against merge-base: $BASE_SHA"
# clang-tidy-diff.py ships with clang-tidy; runs tidy only on changed lines.
DIFF_SCRIPT=$(dpkg -L clang-tidy-15 | grep clang-tidy-diff.py | head -1)
if [ -z "$DIFF_SCRIPT" ]; then
DIFF_SCRIPT=/usr/share/clang/clang-tidy-diff.py
fi
echo "Using: $DIFF_SCRIPT"
# -p1 strips the leading "a/"/"b/" from git diff paths.
# -path=build points clang-tidy at compile_commands.json.
# -iregex restricts to project sources (not vendored).
git diff -U0 "$BASE_SHA" -- 'src/*.cpp' 'src/*.h' \
':(exclude)src/json/nlohmann_json.hpp' \
':(exclude)src/leveldb/*' \
':(exclude)src/lz4/*' \
':(exclude)src/tor/tor-src/*' \
| python3 "$DIFF_SCRIPT" -p1 -path build \
-iregex '.*\.(cpp|cc|h|hpp)$' \
-j$(nproc) || EXIT=$?
# Warn-only initially. Flip this to `exit ${EXIT:-0}` once we're clean.
exit 0