Skip to content

ci(fat): verify fat builds across compilers, macOS, toggles, and all examples - #428

Merged
gg582 merged 1 commit into
devfrom
feat/fat-verify-ci
Oct 22, 2026
Merged

gg582 merged 1 commit into
devfrom
feat/fat-verify-ci

Conversation

@gg582

@gg582 gg582 commented Oct 22, 2026

Copy link
Copy Markdown
Member

Summary

Extends .github/workflows/fat.yml (added in #427) with the coverage the single Ubuntu/gcc job missed:

  • fat (matrix gcc/clang) — the bundle build + embedded-style link smoke now runs under both compilers.
  • fat-macos — exercises the Darwin libtool -static merge path, which the GNU ar MRI script job never touches; includes member/symbol checks and the embedded-style link smoke (bundle + app source only).
  • fat-toggle — thin → fat → thin → CWIST_APP_FAT=1 flips must re-archive libcwist.a without recompiling any object (guards the .build-fat stamp logic), and the fat-dist bundle must stage every header the public API pulls in (cjson, openssl, ttak, sqlite3).
  • fat-examples — builds all examples with CWIST_APP_FAT=1 and asserts no vendored archive path appears on any example link line (guards the APP_LIBS filtering).

All new assertions were verified locally on Linux; the macOS job mirrors the dependency setup precedent from zig.yml.

Test plan

  • fat (gcc) / fat (clang) green
  • fat-macos green
  • fat-toggle green
  • fat-examples green

…examples

Extend the fat archive workflow with the coverage the single Ubuntu/gcc
job missed:

- fat: matrix over gcc and clang for the bundle build and link smoke.
- fat-macos: exercises the Darwin libtool merge path (the GNU ar MRI
  script never runs there) plus member/symbol checks and the
  embedded-style link smoke.
- fat-toggle: flipping CWIST_FAT off/on and CWIST_APP_FAT=1 must
  re-archive libcwist.a without recompiling a single object, and the
  fat-dist bundle must contain every header the public API pulls in.
- fat-examples: builds all examples with CWIST_APP_FAT=1 and asserts no
  vendored archive appears on any example link line.
@gg582
gg582 merged commit 4b02b6a into dev Oct 22, 2026
31 checks passed
gg582 added a commit that referenced this pull request Oct 25, 2026
…#445)

* ci(fat): verify fat builds across compilers, macOS, toggles, and all examples (#428)

Extend the fat archive workflow with the coverage the single Ubuntu/gcc
job missed:

- fat: matrix over gcc and clang for the bundle build and link smoke.
- fat-macos: exercises the Darwin libtool merge path (the GNU ar MRI
  script never runs there) plus member/symbol checks and the
  embedded-style link smoke.
- fat-toggle: flipping CWIST_FAT off/on and CWIST_APP_FAT=1 must
  re-archive libcwist.a without recompiling a single object, and the
  fat-dist bundle must contain every header the public API pulls in.
- fat-examples: builds all examples with CWIST_APP_FAT=1 and asserts no
  vendored archive appears on any example link line.

(cherry picked from commit 4b02b6a)

* feat(build): compile-time feature trimming (CWIST_HTTP3/WS/DB/NATS/JWT/TINY) (#435)

* feat(build): add compile-time feature-trimming knobs

New make knobs drop whole subsystems from libcwist.a at compile time:

  CWIST_HTTP3=0  http3.c/http3_client.c and liblsquic leave the build;
                 CWIST_WEBTRANSPORT is forced to none.
  CWIST_WS=0     websocket*.c, ws_utils.c and graphql_ws.c (rides on
                 websocket_async) leave the build.
  CWIST_DB=0     sqlite3, src/core/db, src/core/orm, db_sync and db_crypt
                 leave the build.
  CWIST_NATS=0   cwist_nats.c and libnats_static leave the build.
  CWIST_JWT=0    jwt.c leaves the build.
  CWIST_TINY=1   meta-knob defaulting all of the above plus CWIST_WEBRTC
                 and CWIST_WEBTRANSPORT=none to off; individual knobs can
                 still be re-enabled explicitly.

Semantics follow the existing CWIST_WEBRTC toggle and the WASM stub idiom:
public headers are untouched and the app-level entry points
(cwist_app_use_http3/https3/webtransport, cwist_app_use_db/nuke_db/db_pool,
the cwist_app_ws* family, cwist_mw_jwt_auth, cwist_job_queue_create_nats)
keep existing as stubs that report the feature as unavailable
(err_i16 = -1 / NULL), so hello-style apps link unchanged. Module-level
APIs of a dropped feature (cwist_db_*, cwist_orm_*, cwist_jwt_*,
cwist_websocket_*) are absent at link time by design.

Guarded call sites: app.c (HTTP/3 context lifecycle, multiport H3 fan-out,
WS upgrade path, DB teardown), big_dumb_reply.c (SQLite disk-spill path;
the in-RAM cache stays), middleware.c (JWT auth handler),
durable_queue.c (NATS JetStream backend; the Redis backend stays).

LIBS keeps the vendored archives in their historical position before the
system libraries via inline $(if) filters: appending them at the end put
liblsquic after -lm and broke --as-needed DSO resolution (lsqpack's
roundf). Tests for trimmed features use the CWIST_WEBRTC else-stub echo
pattern; test_json_heal, test_builtin_mw_chain and test_tls_hot_reload
join the brief's list because they call module-level symbols directly.
Feature-dependent examples are filtered out of EXAMPLE_BINS/MICRO_BINS/
STANDALONE_EXAMPLE_DIRS when their knob is off; default-flag example
builds are unchanged. fat-dist/install now pull the sqlite3 amalgamation
download explicitly so header staging works on fresh trimmed checkouts.

Verified: default, CWIST_TINY=1, CWIST_DB=0 and CWIST_HTTP3=0 builds;
fat-dist member assertions both ways; embedded-style hello smoke on the
trimmed bundles; make test green under default and CWIST_TINY=1;
check_api_baseline/check_test_wiring/check_env_knobs all green.

* ci(fat): add fat-trim matrix job for CWIST_TINY=1 and CWIST_DB=0

Builds the trimmed fat archives, asserts the dropped members are gone
(no lsqpack/sqlite3/nats objects as applicable), asserts the kept core
stays (cJSON.o, SSL_new), runs the same embedded-style hello link smoke
as the full fat job, and records archive sizes.

(cherry picked from commit e78d6a4)

* feat(build): amalgamated single-file distribution (cwist.c drop-in) (#436)

* refactor(src): unique-ify statics and guard macros for unity builds

Preparation for the amalgamated single-file distribution (issue #429):
pasting every translation unit into one cwist.c makes file-local
identifiers collide across former TUs and lets file-local macros leak
into later bodies.

Static renames (one side of each collision, module-prefixed):
- assets.c:        add_header       -> assets_add_header
                   (collided with html_response.c's add_header)
- app.c:           cwist_mem_now    -> cwist_app_mem_now
                   (collided with alloc.c's cwist_mem_now)
- uring_sqpoll.c:  sys_io_uring_setup -> sqpoll_io_uring_setup
                   (collided with reactor.c's sys_io_uring_setup)
- session.c:       base64_encode    -> session_base64_encode,
                   hmac_sha256      -> session_hmac_sha256
                   (collided with ws_utils.c's public base64_encode
                   and jwt.c's hmac_sha256)
- sse.c:           send_all         -> sse_send_all
                   (collided with orm_socket.c's send_all)
- db_sync.c:       write_all        -> db_sync_write_all
                   (collided with log_async.c's write_all)

Macro hygiene:
- siphash.c: guard ROTL and #undef it at end of file; sqlite3.c pastes
  its own unguarded ROTL later in the unity TU.
- app.c, nuke_db.c: guard _DEFAULT_SOURCE; in the unity TU features.h
  has already defined it via an earlier _GNU_SOURCE include.

All identifiers are static (file-local); the per-TU build, tests, and
the public API surface are unchanged.

* feat(build): amalgamated single-file distribution (make amalgamation)

Implements issue #429: 'make amalgamation' stages a drop-in bundle in
dist/amalgamation/:

- cwist.c: every enabled translation unit (the knob-expanded SRCS, in
  Makefile order) pasted into one file by scripts/gen_amalgamation.py.
  Compile-time defines (-D from CFLAGS) are embedded as #define lines,
  each source body is preceded by a #line marker, and quoted includes
  are rewritten to the staged header layout, so the whole file compiles
  with just:
      gcc -O2 -std=gnu17 -Idist/amalgamation/include -c cwist.c
- include/: the public cwist tree plus the vendored headers the public
  API references (cjson, ttak, openssl, sqlite3.h; same staging as
  fat-dist), a generated cwist.h umbrella header, private src/ headers
  under cwist-priv/, and feature-gated vendored headers (lsquic, cnats,
  usrsctp) under cwist-vendor/ so generically named headers never sit
  at the include root.

The trim knobs apply exactly as for libcwist.a: 'make CWIST_TINY=1
amalgamation' emits the 73-source tiny build, the default emits 96
sources including the sqlite3 amalgamation.  Output is deterministic
(same inputs, byte-identical cwist.c).  Vendored code still links from
its static archives, e.g. via 'make fat-dist' alongside.

The default build path is untouched; the target is opt-in and works
from a clean checkout (submodule header anchors initialize submodules
like the existing lsquic.h rule).

* ci(amalgamation): build and smoke-test the drop-in bundle

New workflow following the fat.yml pattern: for the default and
CWIST_TINY=1 configurations, generate the amalgamation plus the fat
archive, copy the bundle out of the repo tree, compile cwist.c with a
single -I, archive it, link example/micro/01-hello against it and the
fat archive (vendored code), then curl the server for 'Hello, CWIST!'.

* style(session): clang-format after the unity-build renames

(cherry picked from commit 5face63)

* feat(tls): pluggable TLS backend with an mbedTLS option for embedded targets (#437)

* feat(tls): pluggable TLS backend with an mbedTLS option for embedded targets

CWIST_TLS=boringssl (default, unchanged behaviour) | CWIST_TLS=mbedtls
selects the TLS/crypto backend.  With mbedtls (vendored as lib/mbedtls,
pinned to the v3.6.7 LTS tag, built with the same cmake stamp-file pattern
as BoringSSL) the produced libcwist.a - thin or fat - contains zero
BoringSSL objects and no SSL_* symbols.

The seam: call sites keep their OpenSSL-shaped code.  Under
CWIST_TLS=mbedtls the include path swaps lib/boringssl/include for
src/security/tls/shim/, whose openssl/*.h headers alias every SSL_*/HMAC_*/
EVP_* name CWIST uses to cwist_mtls_* functions genuinely implemented on
mbedTLS in src/security/tls/backend_mbedtls.c (server/client TLS 1.2+1.3,
cert chain + key load, ALPN, SNI, session cache, hot reload, error mapping,
fork-aware CTR-DRBG).  Public headers are untouched (SSL/SSL_CTX become
opaque typedefs), so the API baseline stays green.

Forced off under mbedtls (Makefile-level, with warnings): CWIST_HTTP3
(lsquic needs BoringSSL's QUIC API), CWIST_WEBTRANSPORT, CWIST_WEBRTC
(DTLS).  Degraded at runtime, documented in docs/performance/tls-backends.md:
JWT EdDSA (no Ed25519 in mbedTLS 3.6; HS256/ES256 work), the session
resumption counter (no public per-connection flag), TLS 1.3 NewSessionTicket
issuance (mbedTLS writes it after the wrap-up, racing clients that close
immediately; TLS 1.2 cache resumption keeps working), AIA intermediate
autoload (no-op stub), ECH, and the hybrid PQC group list (warns, enforces
TLS 1.3 classical groups).

jwt.c gets real CWIST_TLS_MBEDTLS branches (HS256 via mbedtls_md, ES256 via
mbedtls_ecdsa with JOSE raw R||S); session HMAC, seq_auth HMAC and db_crypt
AES-256-CBC (PKCS#7, format-compatible) ride the shim unchanged.

Also fixes a latent Makefile gap: the .build-flags rebuild guard was
attached to $(OBJS) before the feature-gated source appends, so flag flips
never rebuilt feature objects (jwt.o, http3.o, ...); the attachment now
sits after the appends.

CI: new fat-mbedtls job in fat.yml builds the bundle, asserts no BoringSSL
members/symbols, links example/micro/18-https-hello against the bundle and
curl -k smokes it, then runs the TLS-adjacent test subset.  Default-flag
jobs are untouched; make test stays green on the default build.

Closes #430

* refactor(tls): route the mbedTLS backend through cwist_malloc/cwist_free

The raw-allocator audit gate (cwist audit --gate) fails on bare
calloc/free in src/.  cwist_malloc/cwist_free is the right wrapper here:
SSL_CTX/SSL objects deliberately cross threads (handshake shepherd to pool
worker, reload thread), so they must not sit on a thread's full-GC sweep
list - same reason https.c uses cwist_malloc for the pool task ring.

Also clang-format the new seam files.

(cherry picked from commit 10a8b39)

* ci(build): first-class cross-compilation for embedded targets (#438)

* ci(build): first-class cross-compilation for embedded targets (#431)

Honor CROSS_COMPILE=<triplet>- in the Makefile (Closes #431):

Makefile:

- Added CROSS_COMPILE support block near the top that sets CC, CXX,
  AR, RANLIB, and STRIP from the prefix when CROSS_COMPILE is set and
  the individual tools are not already in the environment.
- Derives a CMake system name from the triplet and populates
  EXTRA_CMAKE_FLAGS with CMAKE_SYSTEM_NAME, CMAKE_C_COMPILER,
  CMAKE_CXX_COMPILER, CMAKE_AR, and CMAKE_RANLIB so that the vendored
  cmake dependencies (uriparser, BoringSSL, lsquic) pick up the same
  cross toolchain without separate cmake invocations.
- EXTRA_CMAKE_FLAGS appended to the cmake -S ... configure calls for
  uriparser, BoringSSL, and lsquic.

.github/workflows/cross-compile.yml:

- Matrix: aarch64-linux-gnu and arm-linux-gnueabihf.
- Runs on every push/PR to main/master/dev (same cadence as other
  build checks) and on workflow_dispatch.
- Builds fat-dist with the corresponding CROSS_COMPILE prefix.
- Smoke-checks the fat archive with qemu-user to confirm the objects
  are of the expected target architecture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci(cross): build fat-dist with the target toolchain and serve it under qemu

CC ?= / AR ?= never fire because make predefines both, so the cross job
compiled with the host cc and died linking uriparser with a host ar.
Take CC/CXX/AR from CROSS_COMPILE unless the caller set them, hand the
same toolchain (absolute ar/ranlib, CMAKE_SYSTEM_PROCESSOR) to every
vendored CMake build including cnats, usrsctp and mbedTLS, route
pkg-config through PKG_CONFIG, and build libttak without -march=native.

The workflow installs target dev packages from ports.ubuntu.com, links
the hello example against the bundle with the cross gcc and curls it
under qemu-user instead of compiling a missing tests/smoke_load.c
behind '|| true'.  armhf is dropped until libttak's accel_cpu.c stops
requiring __int128.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Lee Yunjin <gzblues61@daum.net>
(cherry picked from commit 6c58d9d)

* feat(api): runtime OpenAPI 3.1 generation from the route table (#439)

* feat(api): runtime OpenAPI 3.1 generation from the route table (#408)

Extends include/cwist/openapi.h with a runtime document generator and
adds the implementation in src/sys/app/openapi.c. (Closes #408)

include/cwist/openapi.h:

- Added cwist_openapi_meta_t (title, version, description, server_url)
  for per-app document metadata.
- Added cwist_app_enable_openapi(app, json_path, meta): registers a
  GET handler at json_path (default "/openapi.json") that serves an
  OpenAPI 3.1 JSON document built from annotated routes. Must be called
  before any cwist_openapi_annotate() call and before listen.
- Added cwist_openapi_annotate(app, method, path, summary, tags):
  stores per-route metadata (method, path, summary, comma-separated
  tags) for inclusion in the spec. Calling it after
  cwist_app_enable_openapi() invalidates the cached document so the
  next request rebuilds it with the new route.
- Existing CWIST_OPENAPI_* source-level macros are unchanged.

src/sys/app/openapi.c:

- Fixed-size route table (CWIST_OPENAPI_MAX_ROUTES = 256, tunable).
- build_json() emits a minimal but valid OpenAPI 3.1 document covering
  the info object, a single server entry, and each annotated path with
  its operation object (summary, tags, a 200 response stub).
- The document is built on the first GET request and cached; any
  subsequent cwist_openapi_annotate() call invalidates the cache.
- All state is protected by a per-process mutex; building and serving
  are safe under concurrent requests.

docs/api/v4.0-api-baseline.txt: regenerated via make api-baseline to
add cwist_app_enable_openapi, cwist_openapi_annotate, and
cwist_openapi_meta_t.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(openapi): build openapi.c and cover the runtime endpoint

src/sys/app/openapi.c was never added to SRCS, so it hid two compile
errors (res->status, cwist_http_set_header).  Use status_code and
cwist_http_header_add, wire the file into the library, and add
test_openapi, which fetches the served spec through the test client
and checks it parses with the metadata and the annotated operation.

* refactor(openapi): generate the document from the live route table

The endpoint only listed operations passed to cwist_openapi_annotate(),
so a plain cwist_app_get() route never appeared, contrary to the header
and to #408 (routes without annotations still show up).  It also kept
one process-wide state for every app, stored the caller's meta pointers,
emitted a separate key per operation (duplicate "/users" keys for GET
and POST) and hand-assembled JSON in a fixed 64 KiB buffer.

Walk the route table on each request through a private iterator in
app.c (routes_internal.h), build the document with cJSON, map :id
segments to {id} path parameters, and keep metadata and annotations in
per-app state owned by the spec route, so it is freed with the app.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Lee Yunjin <gzblues61@daum.net>
(cherry picked from commit 24908e0)

* ci(mem): zero-allocation gate for the hot path (#440)

* ci(mem): zero-allocation gate for the hot path (#432)

Adds the CI tooling to verify that the CWIST request path performs no
heap allocations after cwist_app_listen() completes initialization.
(Closes #432)

tests/zero_alloc_counter.c:

An LD_PRELOAD shim that interposes malloc/calloc/realloc via RTLD_NEXT.
Three atomic operations control it from the gate server process:
- cwist_zero_alloc_arm()    starts counting
- cwist_zero_alloc_disarm() stops counting
- cwist_zero_alloc_count()  returns the accumulated count

The counter is thread-safe (C11 atomics); the interposition is
per-process and intentionally does not intercept vendored libraries
(BoringSSL, SQLite) because CWIST's own allocator hooks gate that path.

scripts/ci/zero_alloc_gate.sh:

1. Builds the shim if stale.
2. Starts rss_gate_server (HTTP) with CWIST_ZERO_ALLOC_GATE=1 and
   LD_PRELOAD pointing at the shim.
3. Sends ZERO_ALLOC_WARMUP_REQS (default 200) warm-up requests to let
   arenas, caches and ring buffers reach steady state.
4. Arms the counter via /zero_alloc_arm, sends ZERO_ALLOC_GATE_REQS
   (default 1000) gate requests, disarms via /zero_alloc_count.
5. PASS if the reported count == 0; FAIL otherwise. Writes a summary
   block to GITHUB_STEP_SUMMARY when running under GitHub Actions.
   All limits are environment-variable overrides.

.github/workflows/zero-alloc.yml:

- Runs on push/PR to main/master/dev and nightly at 03:00 UTC.
- workflow_dispatch exposes warmup_reqs and gate_reqs overrides for
  targeted manual runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci(mem): measure hot-path allocations against a budget

The gate could not run: it spoke plain HTTP to rss_gate_server, which
only serves HTTPS, and called /zero_alloc_arm and /zero_alloc_count
routes that no server registered.  The shim resolved the allocators
with dlsym(RTLD_NEXT), which itself calls calloc and recurses, and the
counter would have been per worker while the requests were spread over
four.

- tests/zero_alloc_server.c: plain HTTP, one worker, arm/count routes
  bound to the preloaded shim through dlsym(RTLD_DEFAULT)
- the shim calls glibc's __libc_* allocators and also counts the
  aligned ones
- the gate sends every request over one keep-alive connection and
  subtracts the cost of the arm/count requests themselves

Measured: exactly 2 allocations per request (the response body
sstring), so the hot path is not allocation-free yet.  The gate fails
anything above ZERO_ALLOC_BUDGET=2 and the deterministic memory mode
of #432 lowers it to 0.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Lee Yunjin <gzblues61@daum.net>
(cherry picked from commit 0ba2ed0)

* feat(middleware): OpenTelemetry tracing export (#442)

* feat(middleware): OpenTelemetry tracing export (#402)

Adds cwist_mw_otel_trace() and cwist_mw_otel_get_trace_id() to the
middleware layer. (Closes #402)

include/cwist/sys/app/middleware.h:

- cwist_otel_proto_t: CWIST_OTEL_PROTO_HTTP (default) and
  CWIST_OTEL_PROTO_GRPC.
- cwist_otel_cfg_t: endpoint URL, service_name, proto, queue_cap,
  batch_size; all optional, NULL falls back to built-in defaults
  (http://localhost:4318/v1/traces, "cwist", 512-span queue, 64
  per batch).
- cwist_mw_otel_trace(cfg): factory that initializes a process-global
  OTel state on first call (subsequent calls return the same middleware
  func) and returns a cwist_middleware_func for use with cwist_app_use().
- cwist_mw_otel_get_trace_id(req): reads the W3C trace-id propagated
  into the request by the middleware.

src/sys/app/otel.c:

- Middleware function: reads the incoming "traceparent" header, extracts
  or generates a 128-bit trace-id and 64-bit span-id, stores the
  trace-id on the request as "X-Cwist-Trace-Id", calls next(), then
  records method/path/status/duration and enqueues the span.
- Bounded ring-buffer queue (default 512); when full the oldest span is
  dropped silently (fail-open: collector downtime never fails requests).
- Background exporter thread: batches spans and calls export_spans().
  export_spans() is a JSON-over-HTTP scaffold for v4.1; the TODO comment
  marks where a real OTLP/HTTP POST (via the cwist HTTP client) goes.

docs/api/v4.0-api-baseline.txt: regenerated to add cwist_mw_otel_trace,
cwist_mw_otel_get_trace_id, cwist_otel_cfg_t, cwist_otel_proto_t,
CWIST_OTEL_PROTO_HTTP, CWIST_OTEL_PROTO_GRPC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(otel): build otel.c and cover trace-id propagation

src/sys/app/otel.c was never added to SRCS, which hid three compile
errors (res->status, cwist_sstring_cstr, the int method table).  Fix
them, wire the file into the library, and add test_otel: a traceparent
trace id reaches the handler, and a request without one gets a fresh
128-bit id.

* refactor(otel): export real OTLP/HTTP spans from every worker

export_spans() built a JSON string and threw it away (a TODO in place
of the POST), so no span ever reached a collector.  The exporter thread
was also created before cwist_app_listen() forks the workers, so the
children never had one; ids came from rand(); spans had no timestamps;
traceparent was accepted unvalidated; and the trace id lived in an
X-Cwist-Trace-Id request header that a client could set itself.

- POST batches as OTLP/HTTP JSON (cJSON, cwist_http_client, 2 s
  timeout), with start/end times, SERVER kind, semconv attributes and
  error status for 5xx; failures drop the batch (fail-open)
- start the exporter on a process's first span and reset it in a
  pthread_atfork child hook, as log_async.c does
- RAND_bytes ids; W3C validation (hex, non-zero ids, version ff)
- keep the trace id in a thread-local context like the JWT claims
- drop cwist_otel_proto_t: gRPC was never implemented
- test_otel receives the export on a loopback collector socket

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Lee Yunjin <gzblues61@daum.net>
(cherry picked from commit 01f59c4)

* fix(otel): module-prefix otel.c statics for the amalgamation (#444)

* fix(otel): module-prefix otel.c statics for the amalgamation

The amalgamation pastes every translation unit into one cwist.c, and
otel.c's g_lock, g_work and atfork_* redefined log_async.c's, so both
Amalgamation jobs fail on dev since #442.  Prefix every file-scope
static in otel.c with otel_, as #436 did for the earlier collisions.

* style(otel): prefix parse_traceparent and wrap to the 100-column limit

The rename left parse_traceparent unprefixed and pushed six lines past
.clang-format's ColumnLimit; clang-format otel.c and test_otel.c.

(cherry picked from commit 79c807a)

* ci(release): attach CI-built targets to every published release (#443)

* build: CWIST_PORTABLE=1 builds libttak without -march=native

libttak tunes for the build host by default, so an archive built on one
machine can use instructions another CPU of the same architecture lacks.
CWIST_PORTABLE=1 hands libttak the same portable flag set a cross build
already used; CROSS_COMPILE now implies it.  CWIST's own objects never
used host tuning.

* ci(release): attach CI-built targets to every published release

release-assets.yml runs when a release is published (or by hand for an
existing tag) and builds from the tag, with CWIST_PORTABLE=1:

- fat-dist bundles: linux-x86_64, linux-x86_64-mbedtls, linux-aarch64
  (cross, smoke-run under qemu-user) and macos-arm64
- the amalgamation, default and CWIST_TINY=1
- the cwist-wasm npm package

Each bundle is linked and served by scripts/ci/bundle_smoke.sh before
upload, the upload job runs only after every build passed, and it adds
a SHA256SUMS file.  The maintainer-uploaded source tarball that the
Homebrew formula pins is never touched.  Linux assets build on
ubuntu-22.04 so the archives do not reference glibc 2.38+ symbols.

docs/deployment/release-assets.md (and the ko mirror) lists the assets
and how to link them.

* ci(release): run the amalgamation smoke through bundle_smoke.sh

The amalgamation job carried its own copy of the link, run and curl
loop.  bundle_smoke.sh takes APP_OBJS (objects linked ahead of the
archive), so both jobs share one smoke.

(cherry picked from commit 1378ac5)

---------

Co-authored-by: Bhumika Gurav <guravbhumika808@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant