mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 17:51:07 +00:00
* feat: emit logos_module_dispatch_async for concurrency:multi C++ modules The cdylib C-ABI exports gain an async dispatch entry (each call run on a worker thread, reply on completion) for universal + cdylib C++ modules. --concurrency multi flag in logos-cpp-generator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: cpp-sdk concurrent-dispatch doctest (concurrency:"multi" showcase) A concurrency:multi C++ worker + a single driver firing concurrent calls, showing the multi worker overlaps them. The C++ cdylib generator needs NO change — its logos_module_dispatch is already safe to call concurrently; the worker pool lives in the Qt glue and the result is deferred via a sentinel + completion event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: wire universal-cdylib modules() independent of the context latch A C++ interface:"universal" cdylib that calls another module via modules().<dep>... segfaulted on its FIRST cross-module call: the typed dependency surface (LogosModules) was wired inside lidlTryFireContext, which returns early when no persistence context was stored (g_ctxStored == false). When the daemon never delivers a context (observed: zero set_context calls for a context-less module), maybeSetLogosModules never ran, m_logosModulesPtr stayed null, and LogosModuleContext::modules() dereferenced null. modules() does not need the context — each dependency client bakes its target+origin at codegen time and creates its lp client lazily on first call. So wire it in its own context-independent once-latch (lidlEnsureModulesWired), called at the top of lidlTryFireContext before the context-gated early return, i.e. on the first dispatch / set_context / set_emit_callback. A module with deps but no stored context now has modules() wired before any handler runs. (Bump the concurrent-dispatch doctest's post-daemon-start sleep 3 -> 6 to match the rust spec's cold-start margin.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: green the universal-cdylib concurrent-dispatch doctest + wire into CI The driver/worker split a declarations-only impl header (so the cpp-generator's --header-to-lidl doesn't choke on inline std calls) from the impl body. That body was never compiled — metadata's nix.cmake.extra_sources is parsed but not consumed by the LogosModule.cmake the build actually uses — so the impl symbols (FanoutDriverModuleImpl::fanOut / ::peak) were UNDEFINED in the dylib and the plugin null-jumped (bl -> 0x0) on the first cross-module call. Pass the impl .cpp via logos_module()'s existing SOURCES argument so it's compiled and linked. With this the cpp universal-cdylib reaches worker peak overlap 4 end-to-end (a single-threaded driver fans out 4 async calls into a concurrency:"multi" worker and all four overlap), matching the Rust half. Wire the spec into doctests.yml so the workspace pipeline runs it. (Auto-wiring metadata.extra_sources — so the split pattern works without listing SOURCES by hand — needs the consumer added to the backend LogosModule.cmake copies in logos-plugin-core / logos-plugin-qt; tracked separately.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump logos-protocol to merged master (protocol#5) logos-protocol 9de4165 → 4ea32a3 (concurrent-dispatch handshake coalescing, now on master) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
260 lines
12 KiB
YAML
260 lines
12 KiB
YAML
name: cpp-sdk Doc-Tests
|
|
|
|
# Runs the executable cpp-sdk doc-tests end-to-end via the shared doctest CLI,
|
|
# both against the commit of logos-cpp-sdk under test (overriding the SDK across
|
|
# every consumer in each closure):
|
|
# - cpp-sdk-module-runtime.test.yaml builds the logoscore runtime against
|
|
# this SDK, packages and installs the real accounts module built against the
|
|
# same SDK, starts the daemon, loads the module, and calls its methods.
|
|
# - cpp-sdk-module-composition.test.yaml builds two modules against this SDK
|
|
# (a callee and a caller that depends on it), loads both in logoscore, and
|
|
# drives the caller so it calls the callee over IPC — exercising the SDK's
|
|
# generated cross-module wrappers (sync, async, and events).
|
|
#
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# One-time setup required for the clickable report links to work:
|
|
#
|
|
# 1. Repo Settings → Pages → "Build and deployment" → Source: "Deploy from a
|
|
# branch", Branch: `gh-pages` / `(root)`. (The publish-report job creates
|
|
# the gh-pages branch on its first run.)
|
|
# 2. Nothing else — GITHUB_TOKEN already has the permissions granted below.
|
|
#
|
|
# Each run publishes the two-column HTML report to:
|
|
# https://<owner>.github.io/<repo>/pr-<N>/<os>/ (pull requests)
|
|
# https://<owner>.github.io/<repo>/main/<os>/ (pushes to main/master)
|
|
# and (for PRs) posts/updates a comment with the links.
|
|
#
|
|
# Note: pull requests opened from forks get a read-only GITHUB_TOKEN, so the
|
|
# Pages push and PR comment are skipped for them — the downloadable artifact is
|
|
# still produced. PRs from branches in this repo get the full clickable links.
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
# Run on every pull request regardless of its base branch — this repo uses
|
|
# stacked PRs (feature branches based on other feature branches), and a
|
|
# master-only filter silently skipped doc-tests for all of them.
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches: [master, main]
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: doctests-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
doctests:
|
|
name: cpp-sdk doc-tests (${{ matrix.os }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, macos-latest]
|
|
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 90
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Install Nix
|
|
uses: DeterminateSystems/nix-installer-action@main
|
|
|
|
- name: Setup Cachix
|
|
uses: cachix/cachix-action@v15
|
|
with:
|
|
name: logos-co
|
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
|
|
# Resolve the commit under test. For pull requests this is the PR's head
|
|
# commit (not the synthetic merge commit); for pushes it's the pushed
|
|
# commit. Passed to --release-for below so the doc-test builds the whole
|
|
# stack against THIS commit of logos-cpp-sdk instead of the latest release.
|
|
#
|
|
# Fork PRs are the exception: their head commit lives in the fork, not in
|
|
# logos-co/logos-cpp-sdk, so nix could not fetch
|
|
# `github:logos-co/logos-cpp-sdk/<sha>`. We blank the SHA for forks
|
|
# (--release-for repo= → pins that repo to latest), so the doc-test still
|
|
# runs for fork PRs, just against master.
|
|
- name: Resolve commit under test
|
|
id: commit
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "pull_request" ] && \
|
|
[ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then
|
|
echo "sha=" >> "$GITHUB_OUTPUT"
|
|
echo "Fork PR detected — doc-test will run against latest master."
|
|
else
|
|
echo "sha=${{ github.event.pull_request.head.sha || github.sha }}" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
# The runner is the shared `doctest` CLI, invoked directly via its flake
|
|
# (github:logos-co/logos-doctest). The flake bundles Python + PyYAML
|
|
# (+ rich), so no pip install step is needed.
|
|
#
|
|
# CHAIN PINS (drop at merge): in repo CI only cpp-sdk's {release} is the
|
|
# commit under test; logoscore-cli/module-builder must come from the
|
|
# extraction-chain branches — their masters don't link against the chain
|
|
# SDK that the spec overrides in.
|
|
# --release-for pins the {release} placeholder for logos-cpp-sdk to the
|
|
# commit under test, so every `github:logos-co/logos-cpp-sdk{release}`
|
|
# override URL in the spec becomes `.../<sha>` — logoscore, liblogos, the
|
|
# module client, the capability module, and the accounts module are all
|
|
# rebuilt against this PR/push. Every other repo URL still resolves to
|
|
# latest.
|
|
- name: Run cpp-sdk doc-tests
|
|
run: |
|
|
# --continue-on-fail so the run walks every step and the published
|
|
# report is complete. The job still fails (non-zero exit) if any step
|
|
# failed; this only changes whether we stop early.
|
|
#
|
|
# Both specs run back-to-back into one combined report (one dropdown
|
|
# entry each): the module-runtime spec and the module-composition spec,
|
|
# which builds two modules against this SDK and has one call the other.
|
|
nix run github:logos-co/logos-doctest -- run \
|
|
doctests/cpp-sdk-module-runtime.test.yaml \
|
|
doctests/cpp-sdk-module-composition.test.yaml \
|
|
doctests/cpp-sdk-worker-thread-http.test.yaml \
|
|
doctests/cpp-sdk-concurrent-dispatch.test.yaml \
|
|
doctests/cpp-sdk-generator-roundtrip.test.yaml \
|
|
--verbose \
|
|
--continue-on-fail \
|
|
--release-for logos-cpp-sdk=${{ steps.commit.outputs.sha }} \
|
|
--report "${{ runner.temp }}/cpp-sdk-doctest-report.html"
|
|
|
|
- name: Stage report for upload
|
|
if: always()
|
|
shell: bash
|
|
run: |
|
|
mkdir -p report-out
|
|
# Name it index.html so the published directory URL renders directly.
|
|
if [ -f "${{ runner.temp }}/cpp-sdk-doctest-report.html" ]; then
|
|
cp "${{ runner.temp }}/cpp-sdk-doctest-report.html" report-out/index.html
|
|
else
|
|
echo "<h1>No report produced</h1>" > report-out/index.html
|
|
fi
|
|
|
|
- name: Upload cpp-sdk execution report
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: cpp-sdk-doctest-report-${{ matrix.os }}
|
|
path: report-out/index.html
|
|
if-no-files-found: warn
|
|
|
|
- name: Verify markdown generation
|
|
run: |
|
|
for spec in cpp-sdk-module-runtime cpp-sdk-module-composition cpp-sdk-worker-thread-http; do
|
|
nix run github:logos-co/logos-doctest -- generate \
|
|
"doctests/$spec.test.yaml" \
|
|
--release-for logos-cpp-sdk=${{ steps.commit.outputs.sha }} \
|
|
-o "/tmp/$spec.md"
|
|
test -s "/tmp/$spec.md"
|
|
done
|
|
echo "Generated markdown successfully"
|
|
|
|
publish-report:
|
|
name: Publish report to GitHub Pages
|
|
needs: doctests
|
|
# Run even when tests fail — a failing run is exactly when you want to open
|
|
# the report. Skip on forks, where GITHUB_TOKEN can't push or comment.
|
|
if: ${{ always() && github.event.pull_request.head.repo.fork != true }}
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions:
|
|
contents: write # push to the gh-pages branch
|
|
pull-requests: write # post/update the PR comment
|
|
|
|
# Serialize Pages pushes so two refs can't race on the gh-pages branch.
|
|
concurrency:
|
|
group: gh-pages-publish
|
|
cancel-in-progress: false
|
|
|
|
steps:
|
|
- name: Download all reports
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: artifacts
|
|
# No `name:` → downloads every artifact into artifacts/<name>/...
|
|
|
|
- name: Arrange site directory
|
|
id: arrange
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
BASE="pr-${{ github.event.pull_request.number }}"
|
|
else
|
|
BASE="main"
|
|
fi
|
|
echo "base=$BASE" >> "$GITHUB_OUTPUT"
|
|
|
|
mkdir -p "site/$BASE"
|
|
found=""
|
|
for os in ubuntu-latest macos-latest; do
|
|
src="artifacts/cpp-sdk-doctest-report-$os/index.html"
|
|
if [ -f "$src" ]; then
|
|
mkdir -p "site/$BASE/$os"
|
|
cp "$src" "site/$BASE/$os/index.html"
|
|
found="$found $os"
|
|
fi
|
|
done
|
|
echo "found=$found" >> "$GITHUB_OUTPUT"
|
|
|
|
# Landing page for this ref linking to each OS report.
|
|
{
|
|
echo "<!doctype html><meta charset=utf-8>"
|
|
echo "<title>cpp-sdk doc-test reports — $BASE</title>"
|
|
echo "<style>body{font:16px system-ui;margin:40px;max-width:640px}a{color:#2563eb}</style>"
|
|
echo "<h1>cpp-sdk doc-test reports</h1>"
|
|
echo "<p><strong>$BASE</strong> · commit <code>${GITHUB_SHA::7}</code></p><ul>"
|
|
for os in ubuntu-latest macos-latest; do
|
|
if [ -d "site/$BASE/$os" ]; then
|
|
echo "<li><a href=\"./$os/\">$os</a></li>"
|
|
fi
|
|
done
|
|
echo "</ul>"
|
|
} > "site/$BASE/index.html"
|
|
|
|
- name: Deploy to gh-pages
|
|
if: steps.arrange.outputs.found != ''
|
|
uses: peaceiris/actions-gh-pages@v4
|
|
with:
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
publish_dir: ./site
|
|
keep_files: true # don't wipe other PRs' directories
|
|
commit_message: "Publish cpp-sdk doc-test report for ${{ steps.arrange.outputs.base }} (${{ github.sha }})"
|
|
|
|
- name: Comment on PR with report links
|
|
if: ${{ github.event_name == 'pull_request' && steps.arrange.outputs.found != '' }}
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const base = "${{ steps.arrange.outputs.base }}";
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
const root = `https://${owner}.github.io/${repo}/${base}`;
|
|
const oses = "${{ steps.arrange.outputs.found }}".trim().split(/\s+/).filter(Boolean);
|
|
|
|
const links = oses.map(os => `- [\`${os}\` report](${root}/${os}/)`).join("\n");
|
|
const marker = "<!-- cpp-sdk-doctest-report-links -->";
|
|
const body =
|
|
`${marker}\n` +
|
|
`### 📊 cpp-sdk doc-test report\n\n` +
|
|
`The real accounts module, run through a logoscore daemon with the ` +
|
|
`whole stack built against this commit of the C++ SDK — rendered ` +
|
|
`alongside the commands actually run and their output (updated each ` +
|
|
`run, commit \`${context.sha.slice(0,7)}\`):\n\n` +
|
|
`${links}\n\n` +
|
|
`_Pages can take a minute to update after the run finishes._`;
|
|
|
|
const { data: comments } = await github.rest.issues.listComments({
|
|
owner, repo, issue_number: context.issue.number, per_page: 100,
|
|
});
|
|
const existing = comments.find(c => c.body && c.body.includes(marker));
|
|
if (existing) {
|
|
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
|
|
} else {
|
|
await github.rest.issues.createComment({ owner, repo, issue_number: context.issue.number, body });
|
|
}
|