Migrate published-package smoke tests in-tree with per-release pinning - #1639
Migrate published-package smoke tests in-tree with per-release pinning#1639kixelated wants to merge 5 commits into
Conversation
CI finding: browser → gst interop gapThe first smoke run on this PR surfaced a real (pre-existing, published-package) gap, not a harness bug:
So Handling here: the PR smoke run is scoped to harness mechanics (cargo channel, gst dropped) so this gap doesn't block harness PRs. The nightly keeps the full matrix including (Written by Claude) |
Bring the cross-language interop smoke harness (formerly moq-dev/smoke)
into the monorepo at test/smoke, and shift it from a daily cross-OS cron
to a Linux-only nightly backstop plus per-release slices pinned to the
version just published. macOS now runs only when a macOS artifact ships
(the Swift package), which removes the daily macOS spend that had already
forced those cron cells to be disabled.
What changed:
- test/smoke: smoke.sh, token.sh, smoke.toml, freshness.sh, README, and
all clients moved in-tree. Clients install from public registries and
sit outside the cargo/bun workspaces, so they resolve published
artifacts, never workspace source. git ls-files scoping keeps the
freshness lockfile guard local to the subtree.
- just smoke <lang>: per-language slices (rust, python, go, swift,
kotlin, c, gst, js, js-native) wired into the root justfile as a
module. Each pairs the named client with rust on the opposite axis.
- Pinned mode: MOQ_{RS,GO,NPM,SWIFT,KT,LIBMOQ,GST}_VERSION (rust via
RELAY_BIN/MOQ_BIN) make a client install an exact version instead of
latest, skipping the freshness guard and polling the registry until the
version resolves (publish can finish before the artifact is
downloadable). Package.swift and build.gradle.kts read the env to pick
their dependency requirement.
- CI: .github/workflows/smoke.yml is the nightly Linux backstop
(cargo/apt/nix/docker) plus on-demand and PR-on-harness-change runs.
smoke-release.yml is a reusable per-language slice that release-py,
release-swift, release-kt, release-go, libmoq, and moq-gst call right
after publishing, pinned to the version they just cut.
- Docs: README rewritten for the in-tree context and pinned mode; CLAUDE
.md gains the test/smoke structure entry, a testing note, and a
Cross-Package Sync row.
Verified locally: latest-mode rust and pinned-mode python (moq-rs 0.2.16)
slices pass; shellcheck/shfmt/just-fmt/remark clean; actionlint green on
all touched workflows. The release-workflow hooks are correct by
construction but only exercised by an actual release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PR trigger (which fires only when test/smoke changes) was running the full nightly cross-product on every channel, so a pre-existing published-package interop gap blocked an unrelated harness PR. The first run surfaced browser -> gst: moqsrc reads rust/python-published broadcasts fine but times out on browser (WebCodecs H.264) ones. That is a real gap in the published moq-gst, not the harness. Scope the PR run to validate mechanics: the fast cargo channel only, and drop gst (the one red cell) from the subscriber set. The nightly and on-demand runs keep all four channels and the full matrix including gst, so browser -> gst stays visible there as honest signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
68c9bc0 to
c6cd846
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughThis PR establishes a comprehensive cross-language smoke test suite for published moq packages. It adds post-release validation hooks to six existing release workflows, introduces a nightly CI workflow with four installation channels (cargo, apt, nix, docker), and implements publish/subscribe clients across multiple languages. The orchestration harness coordinates synthetic H.264 streaming through a local relay, verifies frame reception in a publisher-subscriber matrix with per-language failure isolation, and enforces version freshness policies. A separate token verification harness validates JWT/JWK compatibility across implementations. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
test/smoke/clients/token/js/resolve-bin.mjs (1)
1-10: 💤 Low valueConsider condensing the header comment.
The comment explains a genuinely non-obvious WHY (Node's strict ESM resolver vs. bun's permissive behavior), but the current 10-line documentation style could be more conversational. Based on learnings, one short line is almost always enough.
Suggested condensed version
-// Print the absolute path to the published `@moq/token` CLI entrypoint. -// -// token.sh runs this with BOTH node and bun so each runtime resolves the same -// installed package and we drive the *published* bin (compiled dist), not the -// in-tree TypeScript source. We read the installed package.json straight off -// disk rather than via module resolution: `@moq/token`'s `exports` map doesn't -// expose ./package.json, which Node's strict ESM resolver refuses (bun allows -// it), so require.resolve would work under bun but throw under node. Reading the -// file keeps both runtimes on the same path, and the bin name is still taken -// from the published manifest so a rename surfaces here. +// Print the absolute path to the published `@moq/token` CLI entrypoint. We read +// package.json from disk instead of via module resolution because Node's strict +// ESM resolver refuses unexported paths (bun allows it), and this keeps both +// runtimes on the same path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/clients/token/js/resolve-bin.mjs` around lines 1 - 10, Condense the long header comment at the top of resolve-bin.mjs into a single, conversational line that states its purpose: print the absolute path to the published `@moq/token` CLI entrypoint for both node and bun, and note briefly that we read the installed package.json from disk (not via module resolution) to avoid Node’s strict ESM resolver difference with bun while still using the published manifest’s bin name. Reference this behavior in one short sentence so the intent (why we read package.json and that the test targets the published bin) remains clear.Source: Learnings
test/smoke/clients/js-native/subscribe.ts (1)
35-35: ⚡ Quick winRemove redundant fallback for timeout default.
Line 35 uses
values.timeout ?? "20"but theparseArgsoptions on line 28 already definedefault: "20", sovalues.timeoutwill never beundefined. The nullish coalescing is dead code.♻️ Simplify timeout parsing
-const timeoutMs = Number.parseFloat(values.timeout ?? "20") * 1000; +const timeoutMs = Number.parseFloat(values.timeout) * 1000;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/clients/js-native/subscribe.ts` at line 35, The timeout parsing contains a redundant fallback: remove the nullish coalescing and parse values.timeout directly (since parseArgs already sets default "20"); change the expression used to compute timeoutMs (currently using Number.parseFloat(values.timeout ?? "20") * 1000) to parse values.timeout without the "?? '20'" fallback so timeoutMs = Number.parseFloat(values.timeout) * 1000.test/smoke/clients/js/driver.ts (2)
41-41: ⚡ Quick winRemove redundant fallback for timeout default.
Line 41 uses
values.timeout ?? "20"butparseArgsoptions on line 23 already definedefault: "20", sovalues.timeoutwill never beundefined. The nullish coalescing is dead code.♻️ Simplify timeout parsing
-const timeoutMs = Number.parseFloat(values.timeout ?? "20") * 1000; +const timeoutMs = Number.parseFloat(values.timeout) * 1000;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/clients/js/driver.ts` at line 41, The timeout parsing uses a redundant nullish fallback because parseArgs already sets a default; remove the "?? '20'" and parse the provided value directly (update the line creating timeoutMs to use Number.parseFloat(values.timeout) * 1000), referencing the parseArgs usage that defines the default and the timeoutMs constant to locate the change.
95-95: ⚡ Quick winReplace magic number with named constant.
Line 95 uses
timeoutMs / 2to determine the reload timing. The0.5fraction is a magic number. As per coding guidelines, use a named constant to clarify intent.♻️ Extract reload timing constant
+const RELOAD_TIMEOUT_FRACTION = 0.5; + const browser = await chromium.launch({- if (!reloaded && Date.now() - start > timeoutMs / 2) { + if (!reloaded && Date.now() - start > timeoutMs * RELOAD_TIMEOUT_FRACTION) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/clients/js/driver.ts` at line 95, Replace the magic fraction used in the reload timing check with a named constant: introduce a constant (e.g., RELOAD_THRESHOLD_FRACTION = 0.5 or RELOAD_BEFORE_TIMEOUT_RATIO) near the top of the module or the enclosing scope and use it in the condition instead of `timeoutMs / 2` so the check becomes `Date.now() - start > timeoutMs * RELOAD_THRESHOLD_FRACTION`; update any related comments to explain the threshold and keep references to the existing variables `reloaded`, `start`, and `timeoutMs`.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/smoke-release.yml:
- Around line 123-124: The workflow pins Gradle via the gradle-version key with
the outdated value "8.14.4"; update that value to the current Gradle release
(e.g. "9.5.1") in the smoke-release workflow so the action uses the latest
Gradle; locate the gradle-version: "8.14.4" entry and replace the version string
with "9.5.1" (or the value returned by services.gradle.org/versions/current).
In `@test/smoke/README.md`:
- Line 5: The sentence in the README contains a lowercase start after a period
("header. none"); update the text to capitalize the new sentence (change
"header. none" to "header. None") in the README content so sentences start with
a capital letter, and scan nearby lines for any similar lowercase-after-period
occurrences to correct them as well.
- Around line 98-116: The fenced code block in test/smoke/README.md is missing a
language identifier; change the opening fence from ``` to ```text so the
directory listing is rendered/highlighted correctly (update the block that
begins with justfile per-language slices and ends before freshness.sh), ensuring
the closing fence remains ```; no other content changes required.
---
Nitpick comments:
In `@test/smoke/clients/js-native/subscribe.ts`:
- Line 35: The timeout parsing contains a redundant fallback: remove the nullish
coalescing and parse values.timeout directly (since parseArgs already sets
default "20"); change the expression used to compute timeoutMs (currently using
Number.parseFloat(values.timeout ?? "20") * 1000) to parse values.timeout
without the "?? '20'" fallback so timeoutMs = Number.parseFloat(values.timeout)
* 1000.
In `@test/smoke/clients/js/driver.ts`:
- Line 41: The timeout parsing uses a redundant nullish fallback because
parseArgs already sets a default; remove the "?? '20'" and parse the provided
value directly (update the line creating timeoutMs to use
Number.parseFloat(values.timeout) * 1000), referencing the parseArgs usage that
defines the default and the timeoutMs constant to locate the change.
- Line 95: Replace the magic fraction used in the reload timing check with a
named constant: introduce a constant (e.g., RELOAD_THRESHOLD_FRACTION = 0.5 or
RELOAD_BEFORE_TIMEOUT_RATIO) near the top of the module or the enclosing scope
and use it in the condition instead of `timeoutMs / 2` so the check becomes
`Date.now() - start > timeoutMs * RELOAD_THRESHOLD_FRACTION`; update any related
comments to explain the threshold and keep references to the existing variables
`reloaded`, `start`, and `timeoutMs`.
In `@test/smoke/clients/token/js/resolve-bin.mjs`:
- Around line 1-10: Condense the long header comment at the top of
resolve-bin.mjs into a single, conversational line that states its purpose:
print the absolute path to the published `@moq/token` CLI entrypoint for both node
and bun, and note briefly that we read the installed package.json from disk (not
via module resolution) to avoid Node’s strict ESM resolver difference with bun
while still using the published manifest’s bin name. Reference this behavior in
one short sentence so the intent (why we read package.json and that the test
targets the published bin) remains clear.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5fa872e9-a973-4db6-a9d4-c1e8b9a7ea2d
📒 Files selected for processing (41)
.github/workflows/libmoq.yml.github/workflows/moq-gst.yml.github/workflows/release-go.yml.github/workflows/release-kt.yml.github/workflows/release-py.yml.github/workflows/release-swift.yml.github/workflows/smoke-release.yml.github/workflows/smoke.ymlCLAUDE.mdjustfiletest/justfiletest/smoke/.gitignoretest/smoke/README.mdtest/smoke/clients/c/subscribe.ctest/smoke/clients/docker/moq-clitest/smoke/clients/docker/moq-relaytest/smoke/clients/go/go.modtest/smoke/clients/go/smoke.gotest/smoke/clients/js-native/package.jsontest/smoke/clients/js-native/subscribe.tstest/smoke/clients/js/build-esbuild.tstest/smoke/clients/js/driver.tstest/smoke/clients/js/index.htmltest/smoke/clients/js/jsdelivr/index.htmltest/smoke/clients/js/jsdelivr/setup.jstest/smoke/clients/js/package.jsontest/smoke/clients/js/src/main.tstest/smoke/clients/js/vite.config.tstest/smoke/clients/kotlin/build.gradle.ktstest/smoke/clients/kotlin/settings.gradle.ktstest/smoke/clients/kotlin/src/main/kotlin/Main.kttest/smoke/clients/python/smoke.pytest/smoke/clients/swift/Package.swifttest/smoke/clients/swift/Sources/smoke/main.swifttest/smoke/clients/token/js/package.jsontest/smoke/clients/token/js/resolve-bin.mjstest/smoke/freshness.shtest/smoke/justfiletest/smoke/smoke.shtest/smoke/smoke.tomltest/smoke/token.sh
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
test/smoke/README.md (2)
98-98:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSpecify language for fenced code block.
The fenced code block should specify a language identifier for proper syntax highlighting and rendering. For directory structure listings, use
text.📝 Proposed fix
## Layout -``` +```text justfile per-language slices (`just smoke <lang>`), wired into the root justfile smoke.sh orchestrator: relay + media interop matrix🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/README.md` at line 98, The fenced code block in the README (the directory listing containing "justfile ... smoke.sh") is missing a language identifier; update the triple-backtick fence to include a language (use `text`) so the block reads ```text and retains the same contents, e.g., the lines with "justfile per-language slices (`just smoke <lang>`), wired into the root justfile" and "smoke.sh orchestrator: relay + media interop matrix".Source: Linters/SAST tools
5-5:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix capitalization after period.
Line 5 ends with "header. none" but a new sentence must start with a capital letter.
📝 Proposed fix
-The per-language unit tests (`just test`) build every client from workspace source. That proves the code in the tree works; it does **not** prove a real user can install the published artifacts and have them talk to each other. A missing wheel, a stale Homebrew formula, a broken `.deb`, an export that didn't survive packaging, a Go module missing its header. none of that shows up until someone installs from a registry. +The per-language unit tests (`just test`) build every client from workspace source. That proves the code in the tree works; it does **not** prove a real user can install the published artifacts and have them talk to each other. A missing wheel, a stale Homebrew formula, a broken `.deb`, an export that didn't survive packaging, a Go module missing its header. None of that shows up until someone installs from a registry.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/smoke/README.md` at line 5, The sentence fragment "header. none" should start the new sentence with a capital letter; locate the text "header. none" in the README (the sentence ending with "header. none") and change "none" to "None" so the sentence reads "header. None". Ensure punctuation and spacing remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/smoke/clients/swift/Sources/smoke/main.swift`:
- Around line 90-97: The code currently calls withTimeout(args.timeout) which
will convert a Double to UInt64 and can trap for negative/NaN/infinite values;
before calling withTimeout (and before invoking subscribe), validate
args.timeout returned by parseArgs() is finite and >= 0 (and optionally within a
sane max) and handle invalid values by printing the usage/error and exiting
(same path used when args.url or args.broadcast are empty). Update the call
sites around parseArgs(), args.timeout, withTimeout and subscribe to perform
this check and only call withTimeout when the timeout is safe to convert.
---
Duplicate comments:
In `@test/smoke/README.md`:
- Line 98: The fenced code block in the README (the directory listing containing
"justfile ... smoke.sh") is missing a language identifier; update the
triple-backtick fence to include a language (use `text`) so the block reads
```text and retains the same contents, e.g., the lines with "justfile
per-language slices (`just smoke <lang>`), wired into the root justfile" and
"smoke.sh orchestrator: relay + media interop matrix".
- Line 5: The sentence fragment "header. none" should start the new sentence
with a capital letter; locate the text "header. none" in the README (the
sentence ending with "header. none") and change "none" to "None" so the sentence
reads "header. None". Ensure punctuation and spacing remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 473d1ef7-3923-4c48-919c-e08fb87591ea
📒 Files selected for processing (41)
.github/workflows/libmoq.yml.github/workflows/moq-gst.yml.github/workflows/release-go.yml.github/workflows/release-kt.yml.github/workflows/release-py.yml.github/workflows/release-swift.yml.github/workflows/smoke-release.yml.github/workflows/smoke.ymlCLAUDE.mdjustfiletest/justfiletest/smoke/.gitignoretest/smoke/README.mdtest/smoke/clients/c/subscribe.ctest/smoke/clients/docker/moq-clitest/smoke/clients/docker/moq-relaytest/smoke/clients/go/go.modtest/smoke/clients/go/smoke.gotest/smoke/clients/js-native/package.jsontest/smoke/clients/js-native/subscribe.tstest/smoke/clients/js/build-esbuild.tstest/smoke/clients/js/driver.tstest/smoke/clients/js/index.htmltest/smoke/clients/js/jsdelivr/index.htmltest/smoke/clients/js/jsdelivr/setup.jstest/smoke/clients/js/package.jsontest/smoke/clients/js/src/main.tstest/smoke/clients/js/vite.config.tstest/smoke/clients/kotlin/build.gradle.ktstest/smoke/clients/kotlin/settings.gradle.ktstest/smoke/clients/kotlin/src/main/kotlin/Main.kttest/smoke/clients/python/smoke.pytest/smoke/clients/swift/Package.swifttest/smoke/clients/swift/Sources/smoke/main.swifttest/smoke/clients/token/js/package.jsontest/smoke/clients/token/js/resolve-bin.mjstest/smoke/freshness.shtest/smoke/justfiletest/smoke/smoke.shtest/smoke/smoke.tomltest/smoke/token.sh
✅ Files skipped from review due to trivial changes (5)
- test/smoke/clients/kotlin/settings.gradle.kts
- test/smoke/clients/js/package.json
- test/smoke/clients/js-native/package.json
- test/justfile
- test/smoke/.gitignore
🚧 Files skipped from review as they are similar to previous changes (32)
- test/smoke/clients/token/js/package.json
- .github/workflows/release-py.yml
- test/smoke/clients/js/vite.config.ts
- test/smoke/clients/docker/moq-relay
- CLAUDE.md
- test/smoke/clients/js/jsdelivr/index.html
- test/smoke/smoke.toml
- test/smoke/clients/js/index.html
- test/smoke/clients/js/jsdelivr/setup.js
- .github/workflows/release-swift.yml
- test/smoke/clients/swift/Package.swift
- test/smoke/clients/js/driver.ts
- .github/workflows/release-kt.yml
- test/smoke/clients/docker/moq-cli
- .github/workflows/libmoq.yml
- justfile
- .github/workflows/release-go.yml
- test/smoke/clients/go/smoke.go
- test/smoke/clients/js/src/main.ts
- test/smoke/clients/go/go.mod
- test/smoke/clients/js/build-esbuild.ts
- test/smoke/clients/js-native/subscribe.ts
- test/smoke/clients/python/smoke.py
- .github/workflows/moq-gst.yml
- .github/workflows/smoke-release.yml
- test/smoke/clients/kotlin/build.gradle.kts
- test/smoke/justfile
- test/smoke/clients/c/subscribe.c
- test/smoke/token.sh
- test/smoke/freshness.sh
- .github/workflows/smoke.yml
- test/smoke/smoke.sh
- README: capitalize "None" after a period; tag the layout fence as `text`. - driver.ts: name the reload-timing fraction (RELOAD_AT_FRACTION) instead of the bare `timeoutMs / 2`. - resolve-bin.mjs: condense the header comment to the essential WHY. Skipped, with reason: - smoke-release.yml Gradle 8.14.4: kept to match the repo's other Gradle workflows (release-kt) and avoid Gradle 9 breaking changes; not chasing latest. - driver.ts / subscribe.ts `values.timeout ?? "20"`: util.parseArgs types the value as `string | undefined` even with a `default`, so the fallback is a type guard, not dead code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the per-language setup-actions (setup-go/bun/node/uv/java/gradle) and the generic `apt-get install ffmpeg curl jq coreutils procps` with a `smoke` devShell, so the toolchains are pinned via flake.lock and reproducible between a local `nix develop` and CI. The default devShell stays lean: smoke toolchains live in their own shells, so day-to-day `nix develop` doesn't pull go/jdk/gradle/Chromium (this was a reason the smoke harness lived in a separate repo; a separate devShell output solves it without a separate repo). flake.nix: - Add a `smoke` shell (full: every client + the cargo channel + a pinned Chromium) for the nightly, plus lean per-slice shells (`smoke-python`, `smoke-go`, `smoke-kotlin`, `smoke-js`, `smoke-min`) so a release runs only what its slice needs (the swift job on the macOS runner pulls just the harness, not jdk/Chromium). Composed from shared package groups. Workflows now run `nix develop .#smoke[-<slice>] --command ./test/smoke/...`: - smoke.yml (nightly): one nix install for all channels; the cargo channel's `cargo install` uses the devShell's rust. - smoke-release.yml: per-slice shell; reference relay/cli from `nix build`. Two toolchains stay system-level, by design: - GStreamer (gst): the prebuilt moq-gst plugin dynamically links a *system* GStreamer (the .deb/brew scenario it tests); a nix-store gst wouldn't satisfy its NEEDED libs. apt-installed only on the runs that include the gst cell. - C compiler (c): the C client links the prebuilt libmoq.a with the system cc (CC=/usr/bin/cc), the real consumer scenario. Swift likewise uses Xcode. Bonus: the nix-pinned Chromium (PLAYWRIGHT_BROWSERS_PATH) drops the `playwright install-deps` apt step, and freshness.sh now *enforces* the playwright pin (PLAYWRIGHT_VERSION is set) instead of warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The first nix-based run segfaulted on every `* -> c` and `* -> go` cell while all other clients passed. Root cause: `nix develop` sets LIBRARY_PATH / CPATH / NIX_CFLAGS_*, so forcing the C client (and Go's cgo) to compile with the system /usr/bin/cc linked the prebuilt libmoq static lib against a mix of system and nix libs; running the result under `nix develop` then crashed. (python -> python passes because moq-rs is loaded by the nix python, one consistent libc.) Drop the CC=/usr/bin/cc override and let c/go use the devShell's own stdenv cc, so the binary and its runtime share one libc. gst and swift still use the system toolchain: the gst plugin and gst-launch are prebuilt binaries with no compile step, so the nix compile-time vars don't affect them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Brings the cross-language interop smoke harness (formerly the standalone
moq-dev/smokerepo) into the monorepo attest/smoke, and shifts it from a daily cross-OS cron to a Linux-only nightly backstop + per-release slices pinned to the version just published. macOS now runs only when a macOS artifact ships (the Swift package), removing the daily macOS spend that had already forced those cron cells to be disabled.Motivation: the daily matrix was expensive (macOS runners), and "always latest" gives a fuzzy signal. Running a slice immediately after a release, pinned to the exact version, is cheaper and tests what was actually shipped.
What changed
test/smoke/—smoke.sh,token.sh,smoke.toml,freshness.sh, README, and allclients/moved in-tree. Clients install from public registries and sit outside the cargo/bun workspaces, so they resolve published artifacts, never workspace source.git ls-filesscoping keeps the freshness lockfile guard local to the subtree (the monorepo's own root lockfiles are untouched).just smoke <lang>— per-language slices (rust,python,go,swift,kotlin,c,gst,js,js-native) wired into the root justfile as a module; each pairs the named client with rust on the opposite axis.MOQ_{RS,GO,NPM,SWIFT,KT,LIBMOQ,GST}_VERSION(rust viaRELAY_BIN/MOQ_BIN) install an exact version instead of latest, skip the freshness guard, and poll the registry until the version resolves (publish can finish before the artifact is downloadable).Package.swift/build.gradle.ktsread the env to pick their dependency requirement..github/workflows/smoke.ymlis the nightly Linux backstop (cargo/apt/nix/docker) + on-demand + PR-on-harness-change.smoke-release.ymlis a reusable per-language slice thatrelease-py,release-swift,release-kt,release-go,libmoq, andmoq-gstcall right after publishing, pinned to the version they just cut.CLAUDE.mdgains thetest/smokestructure entry, a testing note, and a Cross-Package Sync row.Test plan / verification
just smoke rust(latest mode) passesMOQ_RS_VERSION=0.2.16 just smoke python(pinned mode) passes 4/4, freshness skipped, exact wheel installedjust smoke freshnessexit 0test/smokeshell files (monorepo.editorconfig)actionlintgreen on all 8 touched workflows (incl. reusable-workflow input validation)Reviewer notes
just smoke swiftafter a release." This wires pinned hooks for 6 release workflows. If that's more surface than wanted, the leanest cut is to keep the nightly + only theswiftandpythonhooks (the two verified locally) and drop the kt/go/c/gst hooks until there's demand — go and gst can't pass today anyway. The reusable workflow makes re-adding them a ~6-line job.cargo installs a reference relay+cli (~3–5 min cold);moq-ffi-v*fans out to swift+kt+go = three such jobs per release. Much cheaper than daily-all-OS, but not free.release-js(publishes per-package versions, no single output to pin);moq-cli/moq-relay(rust is the reference, covered by every slice + nightly); token-interop pinning (stays nightly/latest).nix developshell carries no Chromium, sojust smoke jsfalls back to a Playwright download there. Addingplaywright-driver.browsersto the flake is a clean follow-up.moq-dev/smokeonce this merges — its role is now fully in-tree.(Written by Claude)
🤖 Generated with Claude Code