Skip to content

Commit fd1640e

Browse files
committed
Add build scripts, fix GCC warnings, unify CI workflows
- Add scripts/clean.sh, test.sh, build.sh, lint.sh as single source of truth for local and CI builds (always clean, always parallel) - Fix -Wmissing-braces: replace {{0}} with memset for struct arrays - Fix -Wrestrict: use intermediate buffer for same-struct snprintf - Update dry-run.yml and release.yml to use the new scripts
1 parent f646da2 commit fd1640e

8 files changed

Lines changed: 198 additions & 72 deletions

File tree

.github/workflows/dry-run.yml

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ permissions:
77
contents: read
88

99
jobs:
10-
# ── Step 1: Lint ──────────────────────────────────────────────
10+
# ── Step 1: Lint (clang-format + cppcheck) ───────────────────
1111
lint:
1212
runs-on: ubuntu-latest
1313
steps:
@@ -16,15 +16,13 @@ jobs:
1616
- name: Install build deps
1717
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev zlib1g-dev cmake
1818

19-
# LLVM 20 (clang-format + clang-tidy) from apt.llvm.org
2019
- name: Install LLVM 20
2120
run: |
2221
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
2322
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" | sudo tee /etc/apt/sources.list.d/llvm-20.list
2423
sudo apt-get update
2524
sudo apt-get install -y clang-format-20
2625
27-
# cppcheck 2.20.0 from source (cached)
2826
- uses: actions/cache@v4
2927
id: cppcheck-cache
3028
with:
@@ -42,18 +40,10 @@ jobs:
4240
- name: Add cppcheck to PATH
4341
run: echo "/opt/cppcheck/bin" >> "$GITHUB_PATH"
4442

45-
- name: Verify versions
46-
run: |
47-
echo "clang-format: $(clang-format-20 --version)"
48-
echo "cppcheck: $(cppcheck --version)"
49-
50-
- name: clang-format
51-
run: make -f Makefile.cbm lint-format CLANG_FORMAT=clang-format-20
52-
53-
- name: cppcheck
54-
run: make -f Makefile.cbm lint-cppcheck
43+
- name: Lint
44+
run: scripts/lint.sh CLANG_FORMAT=clang-format-20
5545

56-
# ── Step 2: Unit tests (ASan + UBSan) ───────────────────────
46+
# ── Step 2: Unit tests (ASan + UBSan) ───────────────────────
5747
test:
5848
needs: [lint]
5949
strategy:
@@ -84,10 +74,10 @@ jobs:
8474
if: startsWith(matrix.os, 'ubuntu')
8575
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev zlib1g-dev
8676

87-
- name: Run C tests (ASan + UBSan)
88-
run: make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm test CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
77+
- name: Test
78+
run: scripts/test.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
8979

90-
# ── Step 3: Build binaries (standard + UI, all OS) ──────────
80+
# ── Step 3: Build binaries (standard + UI, all OS) ──────────
9181
build-unix:
9282
needs: [test]
9383
strategy:
@@ -126,15 +116,15 @@ jobs:
126116
node-version: "22"
127117

128118
- name: Build standard binary
129-
run: make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm cbm CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
119+
run: scripts/build.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
130120

131121
- name: Archive standard binary
132122
run: |
133123
tar -czf codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz \
134124
-C build/c codebase-memory-mcp
135125
136126
- name: Build UI binary
137-
run: make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm cbm-with-ui CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
127+
run: scripts/build.sh --with-ui CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
138128

139129
- name: Archive UI binary
140130
run: |
@@ -168,7 +158,7 @@ jobs:
168158

169159
- name: Build standard binary
170160
shell: msys2 {0}
171-
run: make -j$(nproc) -f Makefile.cbm cbm
161+
run: scripts/build.sh
172162

173163
- name: Archive standard binary
174164
shell: pwsh
@@ -178,7 +168,7 @@ jobs:
178168
179169
- name: Build UI binary
180170
shell: msys2 {0}
181-
run: make -j$(nproc) -f Makefile.cbm cbm-with-ui
171+
run: scripts/build.sh --with-ui
182172

183173
- name: Archive UI binary
184174
shell: pwsh
@@ -191,7 +181,7 @@ jobs:
191181
name: binaries-windows-amd64
192182
path: "*.zip"
193183

194-
# ── Step 4: Smoke test every binary ─────────────────────────
184+
# ── Step 4: Smoke test every binary ─────────────────────────
195185
smoke-unix:
196186
needs: [build-unix]
197187
strategy:

.github/workflows/release.yml

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ permissions:
2121
contents: write
2222

2323
jobs:
24-
# ── Step 1: Lint (clang-format + cppcheck, no clang-tidy) ────
24+
# ── Step 1: Lint (clang-format + cppcheck) ───────────────────
2525
lint:
2626
runs-on: ubuntu-latest
2727
steps:
@@ -54,18 +54,10 @@ jobs:
5454
- name: Add cppcheck to PATH
5555
run: echo "/opt/cppcheck/bin" >> "$GITHUB_PATH"
5656

57-
- name: Verify versions
58-
run: |
59-
echo "clang-format: $(clang-format-20 --version)"
60-
echo "cppcheck: $(cppcheck --version)"
61-
62-
- name: clang-format
63-
run: make -f Makefile.cbm lint-format CLANG_FORMAT=clang-format-20
57+
- name: Lint
58+
run: scripts/lint.sh CLANG_FORMAT=clang-format-20
6459

65-
- name: cppcheck
66-
run: make -f Makefile.cbm lint-cppcheck
67-
68-
# ── Step 2: Unit tests (ASan + UBSan) ────────────────────────
60+
# ── Step 2: Unit tests (ASan + UBSan) ───────────────────────
6961
test:
7062
needs: [lint]
7163
strategy:
@@ -96,10 +88,10 @@ jobs:
9688
if: startsWith(matrix.os, 'ubuntu')
9789
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev zlib1g-dev
9890

99-
- name: Run C tests (ASan + UBSan)
100-
run: make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm test CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
91+
- name: Test
92+
run: scripts/test.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
10193

102-
# ── Step 3: Build binaries (standard + UI, all OS) ──────────
94+
# ── Step 3: Build binaries (standard + UI, all OS) ──────────
10395
build-unix:
10496
needs: [test]
10597
strategy:
@@ -138,27 +130,15 @@ jobs:
138130
node-version: "22"
139131

140132
- name: Build standard binary
141-
env:
142-
VERSION: ${{ inputs.version }}
143-
run: |
144-
CLEAN_VERSION="${VERSION#v}"
145-
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm cbm \
146-
CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} \
147-
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
133+
run: scripts/build.sh --version ${{ inputs.version }} CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
148134

149135
- name: Archive standard binary
150136
run: |
151137
tar -czf codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz \
152138
-C build/c codebase-memory-mcp
153139
154140
- name: Build UI binary
155-
env:
156-
VERSION: ${{ inputs.version }}
157-
run: |
158-
CLEAN_VERSION="${VERSION#v}"
159-
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) -f Makefile.cbm cbm-with-ui \
160-
CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} \
161-
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
141+
run: scripts/build.sh --with-ui --version ${{ inputs.version }} CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
162142

163143
- name: Archive UI binary
164144
run: |
@@ -192,12 +172,7 @@ jobs:
192172

193173
- name: Build standard binary
194174
shell: msys2 {0}
195-
env:
196-
VERSION: ${{ inputs.version }}
197-
run: |
198-
CLEAN_VERSION="${VERSION#v}"
199-
make -j$(nproc) -f Makefile.cbm cbm \
200-
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
175+
run: scripts/build.sh --version ${{ inputs.version }}
201176

202177
- name: Archive standard binary
203178
shell: pwsh
@@ -207,12 +182,7 @@ jobs:
207182
208183
- name: Build UI binary
209184
shell: msys2 {0}
210-
env:
211-
VERSION: ${{ inputs.version }}
212-
run: |
213-
CLEAN_VERSION="${VERSION#v}"
214-
make -j$(nproc) -f Makefile.cbm cbm-with-ui \
215-
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
185+
run: scripts/build.sh --with-ui --version ${{ inputs.version }}
216186

217187
- name: Archive UI binary
218188
shell: pwsh
@@ -225,7 +195,7 @@ jobs:
225195
name: binaries-windows-amd64
226196
path: "*.zip"
227197

228-
# ── Step 4: Smoke test every binary ─────────────────────────
198+
# ── Step 4: Smoke test every binary ─────────────────────────
229199
smoke-unix:
230200
needs: [build-unix]
231201
strategy:
@@ -291,7 +261,7 @@ jobs:
291261
shell: msys2 {0}
292262
run: scripts/smoke-test.sh ./codebase-memory-mcp.exe
293263

294-
# ── Step 5: Create GitHub release ───────────────────────────
264+
# ── Step 5: Create GitHub release ───────────────────────────
295265
release:
296266
needs: [smoke-unix, smoke-windows]
297267
runs-on: ubuntu-latest

scripts/build.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/bash
2+
# build.sh — Clean build of production binary (standard or with UI).
3+
#
4+
# Usage:
5+
# scripts/build.sh # Standard binary
6+
# scripts/build.sh --with-ui # Binary with embedded UI
7+
# scripts/build.sh --version v0.8.0 # With version stamp
8+
#
9+
# This script is the SINGLE source of truth for building release binaries.
10+
# Used identically in local development and CI workflows.
11+
12+
set -euo pipefail
13+
14+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
15+
cd "$ROOT"
16+
17+
# Detect parallelism
18+
NPROC=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
19+
20+
# Parse arguments
21+
WITH_UI=false
22+
VERSION=""
23+
EXTRA_MAKE_ARGS=()
24+
25+
while [[ $# -gt 0 ]]; do
26+
case "$1" in
27+
--with-ui)
28+
WITH_UI=true
29+
shift
30+
;;
31+
--version)
32+
VERSION="$2"
33+
shift 2
34+
;;
35+
CC=*|CXX=*)
36+
export "${1?}"
37+
EXTRA_MAKE_ARGS+=("$1")
38+
shift
39+
;;
40+
*)
41+
EXTRA_MAKE_ARGS+=("$1")
42+
shift
43+
;;
44+
esac
45+
done
46+
47+
# Version flag
48+
CFLAGS_EXTRA=""
49+
if [[ -n "$VERSION" ]]; then
50+
CLEAN_VERSION="${VERSION#v}"
51+
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
52+
fi
53+
54+
echo "=== build.sh: compiler=${CC:-default} ui=$WITH_UI version=${VERSION:-dev} cores=$NPROC ==="
55+
56+
# Step 1: Clean C build artifacts only (not node_modules — npm ci handles that)
57+
rm -rf "$ROOT/build/c"
58+
59+
# Step 2: Build
60+
if $WITH_UI; then
61+
make -j"$NPROC" -f Makefile.cbm cbm-with-ui \
62+
CFLAGS_EXTRA="$CFLAGS_EXTRA" "${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"}"
63+
else
64+
make -j"$NPROC" -f Makefile.cbm cbm \
65+
CFLAGS_EXTRA="$CFLAGS_EXTRA" "${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"}"
66+
fi
67+
68+
echo "=== Build complete: build/c/codebase-memory-mcp ==="

scripts/clean.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
# clean.sh — Remove ALL build artifacts, caches, and generated files.
3+
#
4+
# Usage: scripts/clean.sh
5+
#
6+
# Ensures every subsequent build starts from scratch — no cached .o files,
7+
# no stale node_modules, no leftover dist folders. This is the first step
8+
# in both scripts/test.sh and scripts/build.sh.
9+
10+
set -euo pipefail
11+
12+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
13+
14+
echo "=== Cleaning build artifacts ==="
15+
16+
# C build artifacts
17+
rm -rf "$ROOT/build/c"
18+
19+
# Frontend build artifacts
20+
rm -rf "$ROOT/graph-ui/dist"
21+
rm -rf "$ROOT/graph-ui/node_modules"
22+
23+
# Root-level node artifacts (if any)
24+
rm -rf "$ROOT/node_modules"
25+
26+
# Generated embedded assets (regenerated by embed-frontend.sh)
27+
rm -f "$ROOT/src/ui/embedded_assets.c"
28+
29+
echo "=== Clean complete ==="

scripts/lint.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
# lint.sh — Run all CI linters (clang-format + cppcheck).
3+
#
4+
# Usage:
5+
# scripts/lint.sh # Default tools
6+
# scripts/lint.sh CLANG_FORMAT=clang-format-20 # Override formatter
7+
#
8+
# This script is the SINGLE source of truth for CI linting.
9+
# clang-tidy is intentionally excluded (platform-dependent, pre-commit only).
10+
11+
set -euo pipefail
12+
13+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
14+
cd "$ROOT"
15+
16+
# Forward overrides from args (e.g. CLANG_FORMAT=clang-format-20)
17+
MAKE_ARGS=()
18+
for arg in "$@"; do
19+
MAKE_ARGS+=("$arg")
20+
done
21+
22+
echo "=== lint.sh: running clang-format + cppcheck ==="
23+
24+
# Run format and cppcheck in parallel
25+
make -j2 -f Makefile.cbm lint-ci "${MAKE_ARGS[@]+"${MAKE_ARGS[@]}"}"
26+
27+
echo "=== All linters passed ==="

scripts/test.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
# test.sh — Clean build + run all C tests with ASan + UBSan.
3+
#
4+
# Usage: scripts/test.sh [CC=gcc-14] [CXX=g++-14]
5+
#
6+
# This script is the SINGLE source of truth for running tests.
7+
# Used identically in local development and CI workflows.
8+
#
9+
# Steps:
10+
# 1. Clean all build artifacts (forced fresh compile)
11+
# 2. Build test-runner with sanitizers
12+
# 3. Run all test suites
13+
14+
set -euo pipefail
15+
16+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
17+
cd "$ROOT"
18+
19+
# Detect parallelism
20+
NPROC=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
21+
22+
# Forward CC/CXX from env or args (e.g. scripts/test.sh CC=gcc-14)
23+
for arg in "$@"; do
24+
export "${arg?}"
25+
done
26+
27+
echo "=== test.sh: compiler=${CC:-default} cores=$NPROC ==="
28+
29+
# Step 1: Clean
30+
scripts/clean.sh
31+
32+
# Step 2 + 3: Build and run tests
33+
make -j"$NPROC" -f Makefile.cbm test
34+
35+
echo "=== All tests passed ==="

0 commit comments

Comments
 (0)