diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ed7f58..cc4ca12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,26 @@ jobs: path: coverage/ retention-days: 7 + # ── Memleak (valgrind on Debian Trixie) ────────────────────────────────── + memleak: + runs-on: ubuntu-latest + container: + image: debian:trixie-slim + options: --security-opt seccomp=unconfined + needs: test + steps: + - name: Install valgrind and toolchain deps + run: | + apt-get update + apt-get install -y --no-install-recommends \ + valgrind git curl xz-utils ca-certificates make gpg + - uses: actions/checkout@v6 + - uses: mlugg/setup-zig@v2 + with: + version: 0.15.2 + - name: Run memleak gate + run: make memleak + # ── Cross-compile: verify pure-Zig on Linux + macOS targets ────────────── cross-compile: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 480a07e..3c93608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [0.1.1] - 2026-03-08 + +### Changed + +- CI workflow now runs on pull requests only (`lint`, `test`, `coverage`, `cross-compile`), avoiding duplicate reruns on merge to `main` +- Release workflow now runs on tag pushes only and performs its own sequential gates (`verify-version` -> `lint` -> `test` -> `cross-compile` -> `coverage`) before publishing +- Coverage make target renamed from `test-coverage` to `coverage`; removed `test-depth` gate +- Documentation streamlined: removed hardcoded README version text, simplified usage/architecture sections, and reduced version-specific maintenance text + +### Fixed + +- `verify-fetchable` now creates a deterministic temp workspace and valid Zig project files before `zig fetch --save` +- `build.zig.zon` package name in smoke test now uses a valid bare Zig identifier (`.fetch_test`) +- Release workflow version parsing accepts both `x.y` and `x.y.z` + ## [0.1.0] - 2026-03-08 ### Added @@ -55,3 +70,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Caller latency test threshold adjusted to avoid flaky failures under machine load while preserving the p99 hot-path guard [0.1.0]: https://github.com/usezombie/posthog-zig/releases/tag/v0.1.0 +[0.1.1]: https://github.com/usezombie/posthog-zig/releases/tag/v0.1.1 diff --git a/Makefile b/Makefile index a5eb77e..ff33b6a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ ZIG_GLOBAL_CACHE_DIR ?= $(CURDIR)/.tmp/zig-global-cache ZIG_LOCAL_CACHE_DIR ?= $(CURDIR)/.tmp/zig-local-cache COVERAGE_MIN_LINES ?= 60 +MEMLEAK_TARGET ?= x86_64-linux .DEFAULT_GOAL := help @@ -60,11 +61,11 @@ test-integration: ## Run integration tests against live PostHog (requires POSTH # ── Coverage ───────────────────────────────────────────────────────────────── -test-bin: ## Build test binary for kcov +test-bin: ## Build test binary for kcov / memleak @mkdir -p "$(ZIG_GLOBAL_CACHE_DIR)" "$(ZIG_LOCAL_CACHE_DIR)" @ZIG_GLOBAL_CACHE_DIR="$(ZIG_GLOBAL_CACHE_DIR)" \ ZIG_LOCAL_CACHE_DIR="$(ZIG_LOCAL_CACHE_DIR)" \ - zig build test-bin + zig build test-bin $(if $(TARGET),-Dtarget=$(TARGET),) coverage: ## Run kcov coverage + enforce minimum threshold @command -v kcov >/dev/null 2>&1 || { echo "✗ kcov required (brew install kcov / apt-get install kcov)"; exit 1; } @@ -95,21 +96,24 @@ bench: ## Benchmark capture() hot-path latency memleak: ## Run allocator leak gate @echo "→ Running allocator leak gate..." - @$(MAKE) test-bin @case "$$(uname -s)" in \ Linux) \ + $(MAKE) test-bin TARGET="$(MEMLEAK_TARGET)"; \ command -v valgrind >/dev/null 2>&1 || { echo "✗ valgrind required on Linux"; exit 1; }; \ - valgrind --quiet --leak-check=full --show-leak-kinds=all \ + POSTHOG_MEMLEAK_MODE=1 valgrind --quiet --leak-check=full --show-leak-kinds=all \ --errors-for-leak-kinds=definite,possible --error-exitcode=1 \ zig-out/bin/posthog-tests;; \ Darwin) \ + $(MAKE) test-bin; \ if command -v leaks >/dev/null 2>&1; then \ MallocStackLogging=1 leaks -atExit -- zig-out/bin/posthog-tests >/dev/null || \ echo "→ leaks unavailable in this runtime (allocator gate only)"; \ else \ echo "→ leaks not found; allocator gate only"; \ fi;; \ - *) echo "→ platform=$$(uname -s): allocator gate only";; \ + *) \ + $(MAKE) test-bin; \ + echo "→ platform=$$(uname -s): allocator gate only";; \ esac @echo "✓ memleak gate passed" diff --git a/VERSION b/VERSION index 49d5957..17e51c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1 +0.1.1 diff --git a/build.zig.zon b/build.zig.zon index 1c612f7..aa993c9 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .posthog, - .version = "0.1.0", + .version = "0.1.1", .fingerprint = 0xa5fe060596d90b43, .minimum_zig_version = "0.15.0", .dependencies = .{}, diff --git a/src/client.zig b/src/client.zig index c68308f..8dbba24 100644 --- a/src/client.zig +++ b/src/client.zig @@ -431,5 +431,8 @@ test "integration: capture is non-blocking (avg < 1ms per call for 1000 events)" const elapsed_ns = std.time.nanoTimestamp() - start; const avg_ns = @divFloor(elapsed_ns, 1000); - try std.testing.expect(avg_ns < 1_000_000); // < 1ms per call + // Valgrind instrumentation in memleak mode adds heavy runtime overhead. + const in_memleak_mode = std.posix.getenv("POSTHOG_MEMLEAK_MODE") != null; + const max_avg_ns: i128 = if (in_memleak_mode) 50_000_000 else 1_000_000; + try std.testing.expect(avg_ns < max_avg_ns); }