forked from Fincept-Corporation/FinceptTerminal
-
Notifications
You must be signed in to change notification settings - Fork 0
288 lines (266 loc) · 13.8 KB
/
Copy pathlint.yml
File metadata and controls
288 lines (266 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
name: Lint
# ─────────────────────────────────────────────────────────────────────────────
# Runs on pull requests (incremental, gating) and on demand (full tree).
#
# It used to be workflow_dispatch-only, and even when run by hand the
# clang-tidy job could not fail: .clang-tidy sets `WarningsAsErrors: ''` and
# the invocation passed no --warnings-as-errors, so it exited 0 no matter what
# it printed — while xargs-ing 1,032 files through a single-threaded process
# (hours). The PR path below is therefore INCREMENTAL: only the .cpp files the
# PR actually changed, in parallel, with a narrow high-signal check set that is
# adoptable today. The full-tree sweep stays available via "Run workflow".
# ─────────────────────────────────────────────────────────────────────────────
on:
pull_request:
paths:
- 'fincept-qt/src/**'
- 'fincept-qt/CMakeLists.txt'
- 'fincept-qt/.clang-tidy'
- 'fincept-qt/.clang-format'
- 'fincept-qt/.cppcheck-suppressions'
- '.github/workflows/lint.yml'
workflow_dispatch:
concurrency:
group: lint-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
QT_VERSION: "6.8.3"
QT_MODULES: "qtcharts qtwebsockets qtmultimedia qtwebengine qtwebchannel qtpositioning qtserialport"
jobs:
# ── clang-format ─────────────────────────────────────────────────────────────
clang-format:
name: clang-format
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install clang-format
run: |
sudo apt-get update -qq
sudo apt-get install -y clang-format-18
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-18 100
# PR path: only the lines this PR touched. A whole-file --dry-run would
# flag pre-existing formatting in untouched lines and fail PRs for code
# their author never wrote — the fastest way to get a check deleted.
- name: Check formatting of changed lines
if: github.event_name == 'pull_request'
working-directory: fincept-qt
run: |
set -euo pipefail
GCF="$(command -v git-clang-format-18 || command -v git-clang-format || true)"
if [ -z "${GCF}" ]; then
echo "::warning::git-clang-format not available — skipping the diff-scoped format check"
exit 0
fi
BASE="${{ github.event.pull_request.base.sha }}"
DIFF=$("${GCF}" --binary clang-format --diff --commit "${BASE}" -- 'src/*.cpp' 'src/*.h' || true)
case "${DIFF}" in
*"no modified files"*|*"did not modify"*|"") echo "Changed lines are correctly formatted."; exit 0 ;;
esac
echo "${DIFF}"
echo "::error::Formatting differs from .clang-format on lines this PR touched. Run: git-clang-format --commit ${BASE}"
exit 1
# Full-tree sweep — manual runs only.
- name: Check formatting (full tree)
if: github.event_name == 'workflow_dispatch'
working-directory: fincept-qt
run: |
# Parentheses required: without them -o has lower precedence than the
# implicit -print, so only .h files get piped to xargs.
find src \( -name "*.cpp" -o -name "*.h" \) | sort | \
xargs clang-format --style=file --dry-run --Werror
echo "All files are correctly formatted."
# ── clang-tidy ───────────────────────────────────────────────────────────────
clang-tidy:
name: clang-tidy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
clang-18 clang-tidy-18 \
cmake ninja-build \
libgl1-mesa-dev libglu1-mesa-dev \
libxkbcommon-dev libxkbcommon-x11-dev \
libxcb-cursor-dev libxcb-icccm4-dev libxcb-image0-dev \
libxcb-keysyms1-dev libxcb-render-util0-dev libxcb-shape0-dev \
libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev \
libdbus-1-dev libfontconfig1-dev libfreetype6-dev libssl-dev
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100
sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-18 100
# Qt 6.8.3 from aqt, NOT apt: ubuntu-24.04 ships Qt 6.4 and CMakeLists.txt
# pins Qt 6.8 (FINCEPT_QT_PIN_MODE=MINOR), so the previous apt-based
# configure could never produce a compile database.
- name: Install Qt ${{ env.QT_VERSION }}
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
arch: linux_gcc_64
modules: ${{ env.QT_MODULES }}
cache: true
cache-key-prefix: qt-linux-x64-lint
- name: Remove dangling Qt SQL driver plugin CMake configs
run: |
set -euo pipefail
SQL_CMAKE="${QT_ROOT_DIR}/lib/cmake/Qt6Sql"
if [ -d "${SQL_CMAKE}" ]; then
for drv in QMYSQLDriverPlugin QPSQLDriverPlugin QODBCDriverPlugin QMimerSQLDriverPlugin; do
rm -fv "${SQL_CMAKE}/Qt6${drv}"*.cmake || true
done
fi
# FINCEPT_DEV_BUILD=ON turns the unity build OFF. That is REQUIRED here:
# with unity ON the compile database contains only unity_*.cxx entries,
# so clang-tidy cannot find a compile command for an individual .cpp.
- name: Configure (generate compile_commands.json)
working-directory: fincept-qt
run: |
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_PREFIX_PATH="${QT_ROOT_DIR}" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DFINCEPT_DEV_BUILD=ON \
-DFINCEPT_BUILD_INSTALLER=OFF \
-DDEPLOY_QT=OFF
# moc/uic output must exist before clang-tidy parses anything that
# includes it, otherwise every hit is a bogus "file not found".
- name: Generate moc/uic sources
working-directory: fincept-qt
run: cmake --build build --target FinceptTerminal_autogen -j "$(nproc)" || echo "::warning::autogen target unavailable — clang-tidy may report missing generated headers"
# ── PR path: incremental + GATING ───────────────────────────────────────
# Narrow, high-signal checks only. bugprone-easily-swappable-parameters,
# -narrowing-conversions and -branch-clone are excluded: thousands of
# pre-existing hits, near-zero defect yield.
- name: clang-tidy (changed files, gating)
if: github.event_name == 'pull_request'
working-directory: fincept-qt
run: |
set -euo pipefail
BASE="${{ github.event.pull_request.base.sha }}"
CHECKS='-*,bugprone-*,concurrency-*,performance-move-const-arg,-bugprone-easily-swappable-parameters,-bugprone-narrowing-conversions,-bugprone-branch-clone'
git diff --name-only --diff-filter=ACMR "${BASE}" HEAD -- 'fincept-qt/src/**/*.cpp' \
| sed 's|^fincept-qt/||' > /tmp/changed.txt || true
# Keep only files the compile database actually knows about (a brand
# new .cpp not yet added to CMakeLists.txt has no compile command).
: > /tmp/tidy_files.txt
while IFS= read -r f; do
[ -n "$f" ] || continue
[ -f "$f" ] || continue
grep -q "\"$(basename "$f")\"\|/$f\"" build/compile_commands.json 2>/dev/null && echo "$f" >> /tmp/tidy_files.txt || \
echo "::warning::$f not in compile_commands.json — skipped by clang-tidy"
done < /tmp/changed.txt
COUNT=$(wc -l < /tmp/tidy_files.txt | tr -d ' ')
if [ "${COUNT}" = "0" ]; then
echo "No changed .cpp files with compile commands — nothing to analyse."
exit 0
fi
echo "Analysing ${COUNT} changed file(s) with: ${CHECKS}"
xargs -a /tmp/tidy_files.txt -P "$(nproc)" -n 1 \
clang-tidy -p build \
--checks="${CHECKS}" \
--warnings-as-errors="${CHECKS}" \
--quiet \
--extra-arg=-std=c++20
echo "clang-tidy clean on changed files."
# ── Manual path: full tree, advisory ────────────────────────────────────
- name: clang-tidy (full tree, advisory)
if: github.event_name == 'workflow_dispatch'
working-directory: fincept-qt
run: |
find src -name "*.cpp" | grep -v "/moc_" | grep -v "/qrc_" | sort | \
xargs -P "$(nproc)" -n 1 clang-tidy \
--config-file=.clang-tidy \
-p build \
--quiet \
--extra-arg=-std=c++20 \
2>&1 || true
echo "clang-tidy sweep complete (advisory — .clang-tidy sets WarningsAsErrors: '')."
# ── datahub-discipline ───────────────────────────────────────────────────────
# Enforces DataHub rules D1 and D4 (see fincept-qt/CLAUDE.md §D):
# D1 — screens must not spawn Python directly.
# D4 — consumers must not call service fetch_* callbacks.
# If this step fails, route the offending code through DataHub. See
# fincept-qt/DATAHUB_ARCHITECTURE.md §4 and docs/DATAHUB_TOPICS.md for the
# producer + topic you should be using instead.
datahub-discipline:
name: datahub-discipline
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Check screens/ does not call PythonRunner directly
working-directory: fincept-qt
run: |
# GRANDFATHER LIST — two pre-existing D1 violations, present when this
# check first became a PR gate. New violations anywhere else fail the
# build; editing one of these files for an unrelated reason does not.
# Delete an entry here the moment its file is migrated to a Producer.
ALLOW='src/screens/equity_research/EquityResearchScreen.cpp|src/screens/portfolio/views/EconomicsView.cpp'
violations=$(grep -rn --include='*.cpp' --include='*.h' \
'PythonRunner::instance()\.run(' src/screens/ | grep -vE "^(${ALLOW})" || true)
if [ -n "$violations" ]; then
echo "$violations"
echo ""
echo "::error::D1 violation — screens must not call PythonRunner directly."
echo "Route through a Producer registered with DataHub."
echo "See fincept-qt/DATAHUB_ARCHITECTURE.md §4."
exit 1
fi
echo "D1 — screens are clean (excluding the 2 grandfathered files)."
- name: Check screens/ does not call deprecated service fetch_* APIs
working-directory: fincept-qt
run: |
# D4 exempts one-shot catalog/info APIs that have no hub topic:
# MarketDataService::fetch_info, MarketDataService::fetch_news,
# DBnomicsService::fetch_providers / fetch_datasets / fetch_series /
# fetch_observations. See fincept-qt/CLAUDE.md §D4 for the rule.
# Match any streaming fetch_* call, then grep -v the allowed ones.
violations=$(grep -rnE --include='*.cpp' --include='*.h' \
'(MarketDataService|NewsService|EconomicsService|DBnomicsService|GovDataService)::instance\(\)\.fetch_' \
src/screens/ \
| grep -vE 'MarketDataService::instance\(\)\.fetch_(info|news)\b' \
| grep -vE 'DBnomicsService::instance\(\)\.fetch_(providers|datasets|series|observations)\b' \
|| true)
if [ -n "$violations" ]; then
echo "$violations"
echo ""
echo "::error::D4 violation — consumers must not call streaming fetch_* APIs."
echo "Use DataHub::subscribe() for live data, DataHub::peek() for snapshots."
echo "See fincept-qt/DATAHUB_ARCHITECTURE.md §4 and docs/DATAHUB_TOPICS.md."
exit 1
fi
echo "D4 — screens are clean."
# ── cppcheck ─────────────────────────────────────────────────────────────────
# Unchanged from its manual-only form — it already gates via
# --error-exitcode=1 and runs the whole tree in a couple of minutes.
cppcheck:
name: cppcheck
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install cppcheck
run: |
sudo apt-get update -qq
sudo apt-get install -y cppcheck
- name: Run cppcheck
working-directory: fincept-qt
run: |
cppcheck \
--enable=warning,performance,portability \
--suppressions-list=.cppcheck-suppressions \
--inline-suppr \
--std=c++20 \
--error-exitcode=1 \
-I src \
-j "$(nproc)" \
src 2>&1
echo "cppcheck complete."