Skip to content

Commit 9662063

Browse files
committed
Add Alpine portable as 4th local test platform, fix musl compat
Local test infra now covers 4 platforms: - Linux arm64 (Ubuntu, ASan+LeakSan) - Linux portable (Alpine musl, static binary) - Windows (mingw cross-compile) - macOS (native) Fixes: - Remove sys/unistd.h and sys/poll.h (glibc-only, musl lacks them) - Add STATIC=1 support to Makefile (appends -static to LDFLAGS) - CI portable build uses docker run alpine instead of container directive (GitHub Actions JS actions break in Alpine containers on ARM64)
1 parent 4f05c64 commit 9662063

6 files changed

Lines changed: 113 additions & 33 deletions

File tree

.github/workflows/_build.yml

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ jobs:
136136
path: "*.zip"
137137

138138
build-linux-portable:
139-
# Static musl builds — uses musl-tools on Ubuntu (not Alpine containers,
140-
# which break on ARM64 due to GitHub Actions JS action limitation).
139+
# Static musl builds via `docker run alpine` inside Ubuntu runners.
140+
# Can't use `container: alpine` on ARM64 (GitHub Actions JS actions break).
141+
# Checkout happens on Ubuntu host, build runs inside Alpine container.
141142
strategy:
142143
fail-fast: false
143144
matrix:
@@ -147,39 +148,35 @@ jobs:
147148
- arch: arm64
148149
runner: ubuntu-24.04-arm
149150
runs-on: ${{ matrix.runner }}
150-
timeout-minutes: 15
151+
timeout-minutes: 20
151152
steps:
152153
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
153154

154-
- name: Install musl build deps
155-
run: |
156-
sudo apt-get update
157-
sudo apt-get install -y musl-tools zlib1g-dev nodejs npm
158-
159-
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
160-
with:
161-
node-version: "22"
162-
163-
- name: Build standard binary (static musl)
164-
run: scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=musl-gcc CXX=g++ STATIC=1
165-
166-
- name: Verify static linking
167-
run: file build/c/codebase-memory-mcp | grep -q "statically linked"
168-
169-
- name: Archive standard binary
155+
- name: Build standard binary (Alpine static)
170156
run: |
157+
docker run --rm -v "$PWD:/src" -w /src alpine:3.21 sh -c "
158+
apk add --no-cache build-base linux-headers zlib-dev zlib-static nodejs npm bash &&
159+
bash scripts/build.sh ${VERSION_FLAG} CC=gcc CXX=g++ STATIC=1
160+
"
161+
file build/c/codebase-memory-mcp | grep -q "statically linked"
162+
echo "=== Verified: statically linked ==="
171163
cp LICENSE install.sh build/c/
172164
tar -czf codebase-memory-mcp-linux-${{ matrix.arch }}-portable.tar.gz \
173165
-C build/c codebase-memory-mcp LICENSE install.sh
166+
env:
167+
VERSION_FLAG: ${{ inputs.version && format('--version {0}', inputs.version) || '' }}
174168

175-
- name: Build UI binary (static musl)
176-
run: scripts/build.sh --with-ui ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=musl-gcc CXX=g++ STATIC=1
177-
178-
- name: Archive UI binary
169+
- name: Build UI binary (Alpine static)
179170
run: |
171+
docker run --rm -v "$PWD:/src" -w /src alpine:3.21 sh -c "
172+
apk add --no-cache build-base linux-headers zlib-dev zlib-static nodejs npm bash &&
173+
bash scripts/build.sh --with-ui ${VERSION_FLAG} CC=gcc CXX=g++ STATIC=1
174+
"
180175
cp LICENSE install.sh build/c/
181176
tar -czf codebase-memory-mcp-ui-linux-${{ matrix.arch }}-portable.tar.gz \
182177
-C build/c codebase-memory-mcp LICENSE install.sh
178+
env:
179+
VERSION_FLAG: ${{ inputs.version && format('--version {0}', inputs.version) || '' }}
183180

184181
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
185182
with:

Makefile.cbm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ ifeq ($(IS_MINGW),yes)
8181
WIN32_LIBS := -lws2_32 -lpsapi -Wl,--allow-multiple-definition -Wl,--stack,8388608 -static
8282
endif
8383

84-
LDFLAGS = -lm -lstdc++ -lpthread -lz $(LIBGIT2_LIBS) $(WIN32_LIBS)
84+
# STATIC=1 produces a fully static binary (for Alpine/musl portable builds)
85+
ifeq ($(STATIC),1)
86+
STATIC_FLAGS := -static
87+
endif
88+
89+
LDFLAGS = -lm -lstdc++ -lpthread -lz $(LIBGIT2_LIBS) $(WIN32_LIBS) $(STATIC_FLAGS)
8590
LDFLAGS_TEST = -lm -lstdc++ -lpthread -lz $(SANITIZE) $(LIBGIT2_LIBS) $(WIN32_LIBS)
8691
LDFLAGS_TSAN = -lm -lstdc++ -lpthread -lz -fsanitize=thread $(LIBGIT2_LIBS) $(WIN32_LIBS)
8792

src/mcp/mcp.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ enum {
5858
#include <process.h> /* _getpid */
5959
#else
6060
#include <unistd.h>
61-
#include <sys/unistd.h>
62-
#include <sys/poll.h>
6361
#include <poll.h>
6462
#include <fcntl.h>
6563
#endif
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Mirrors the Alpine CI environment for portable (musl static) builds.
2+
# - Alpine 3.21 with musl libc
3+
# - GCC + static zlib for fully static binaries
4+
# - Produces portable Linux binaries that run on any distro
5+
#
6+
# Build: docker build -f test-infrastructure/Dockerfile.alpine -t cbm-alpine test-infrastructure/
7+
# Run: docker run --rm -v $(pwd):/src cbm-alpine
8+
9+
FROM alpine:3.21
10+
11+
RUN apk add --no-cache \
12+
build-base \
13+
linux-headers \
14+
zlib-dev \
15+
zlib-static \
16+
bash \
17+
git \
18+
python3 \
19+
nodejs \
20+
npm \
21+
ca-certificates
22+
23+
WORKDIR /src
24+
25+
ENTRYPOINT ["bash", "scripts/build.sh"]
26+
CMD ["CC=gcc", "CXX=g++", "STATIC=1"]

test-infrastructure/docker-compose.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,42 @@ services:
129129
mv build/c/codebase-memory-mcp build/c/codebase-memory-mcp.exe 2>/dev/null || true &&
130130
CBM_DIAGNOSTICS=1 WINEDEBUG=-all scripts/soak-test.sh ./build/c/codebase-memory-mcp.exe 10
131131
132+
# ── Linux portable (Alpine musl static) ────────────────────
133+
test-portable:
134+
build:
135+
context: ..
136+
dockerfile: test-infrastructure/Dockerfile.alpine
137+
platform: linux/arm64
138+
volumes:
139+
- ..:/src
140+
entrypoint: ["bash", "scripts/test.sh"]
141+
command: ["CC=gcc", "CXX=g++"]
142+
143+
build-portable:
144+
build:
145+
context: ..
146+
dockerfile: test-infrastructure/Dockerfile.alpine
147+
platform: linux/arm64
148+
volumes:
149+
- ..:/src
150+
entrypoint: ["bash", "scripts/build.sh"]
151+
command: ["CC=gcc", "CXX=g++", "STATIC=1"]
152+
153+
smoke-portable:
154+
build:
155+
context: ..
156+
dockerfile: test-infrastructure/Dockerfile.alpine
157+
platform: linux/arm64
158+
volumes:
159+
- ..:/src
160+
entrypoint: ["bash", "-c"]
161+
command:
162+
- |
163+
scripts/build.sh CC=gcc CXX=g++ STATIC=1 &&
164+
file build/c/codebase-memory-mcp | grep -q "statically linked" &&
165+
echo "=== Verified: statically linked ===" &&
166+
scripts/smoke-test.sh ./build/c/codebase-memory-mcp
167+
132168
# ── Lint ────────────────────────────────────────────────────
133169
lint:
134170
build:

test-infrastructure/run.sh

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
# Local CI — test all platforms before pushing.
33
#
44
# Coverage:
5-
# Linux arm64: test (ASan+LeakSan) + build (-O2) [native, fast]
6-
# Linux amd64: test + build [QEMU, slower]
7-
# Windows: cross-compile with mingw-w64 [compile-check]
8-
# macOS: run natively (not in Docker)
5+
# Linux arm64: test (ASan+LeakSan) + build (-O2) [native, fast]
6+
# Linux amd64: test + build [QEMU, slower]
7+
# Linux portable: Alpine musl static build + smoke [portable binary]
8+
# Windows: cross-compile with mingw-w64 [compile-check]
9+
# macOS: run natively (not in Docker)
910
#
1011
# Usage:
11-
# ./test-infrastructure/run.sh # Linux test+build + Windows compile
12+
# ./test-infrastructure/run.sh # arm64 test+build + portable + Windows
1213
# ./test-infrastructure/run.sh all # above + amd64
14+
# ./test-infrastructure/run.sh portable # Alpine portable build + smoke only
1315
# ./test-infrastructure/run.sh windows # Windows cross-compile only
1416
# ./test-infrastructure/run.sh test # Linux arm64 test only (no perf)
1517
# ./test-infrastructure/run.sh perf # Linux arm64 perf/incremental only
@@ -29,6 +31,8 @@ case "${1:-full}" in
2931
$COMPOSE run --rm build
3032
echo "=== Linux arm64: smoke test ==="
3133
$COMPOSE run --rm smoke
34+
echo "=== Linux portable: Alpine static build + smoke ==="
35+
$COMPOSE run --rm smoke-portable
3236
echo "=== Windows: cross-compile ==="
3337
$COMPOSE run --rm build-windows
3438
echo "=== All passed ==="
@@ -46,9 +50,17 @@ case "${1:-full}" in
4650
$COMPOSE run --rm build
4751
;;
4852
smoke)
49-
echo "=== Linux arm64: smoke test (build + run all 7 phases) ==="
53+
echo "=== Linux arm64: smoke test (build + run all phases) ==="
5054
$COMPOSE run --rm smoke
5155
;;
56+
portable)
57+
echo "=== Linux portable: Alpine static build + smoke ==="
58+
$COMPOSE run --rm smoke-portable
59+
;;
60+
portable-test)
61+
echo "=== Linux portable: Alpine test (ASan + LeakSan) ==="
62+
$COMPOSE run --rm -e CBM_SKIP_PERF=1 test-portable
63+
;;
5264
windows)
5365
echo "=== Windows: cross-compile + smoke (Wine) ==="
5466
$COMPOSE run --rm smoke-windows
@@ -71,6 +83,8 @@ case "${1:-full}" in
7183
$COMPOSE run --rm -e CBM_SKIP_PERF=1 test
7284
$COMPOSE run --rm build
7385
$COMPOSE run --rm smoke
86+
echo "=== Linux portable: Alpine static build + smoke ==="
87+
$COMPOSE run --rm smoke-portable
7488
echo "=== Linux amd64: test + build + smoke ==="
7589
$COMPOSE run --rm -e CBM_SKIP_PERF=1 test-amd64
7690
$COMPOSE run --rm build-amd64
@@ -87,8 +101,12 @@ case "${1:-full}" in
87101
echo "=== Debug shell (Linux arm64) ==="
88102
$COMPOSE run --rm --entrypoint bash test
89103
;;
104+
shell-alpine)
105+
echo "=== Debug shell (Alpine) ==="
106+
$COMPOSE run --rm --entrypoint bash test-portable
107+
;;
90108
*)
91-
echo "Usage: $0 {full|test|build|smoke|perf|windows|amd64|all|lint|shell}"
109+
echo "Usage: $0 {full|test|build|smoke|portable|portable-test|windows|amd64|all|lint|shell|shell-alpine}"
92110
exit 1
93111
;;
94112
esac

0 commit comments

Comments
 (0)