This commit is contained in:
Iuri Matias
2026-05-31 01:11:42 +00:00
committed by GitHub
5 changed files with 830 additions and 0 deletions
+235
View File
@@ -0,0 +1,235 @@
name: liblogos Doc-Tests
# Runs the executable liblogos doc-test (doctests/liblogos-module-runtime.test.yaml)
# end-to-end via the shared doctest CLI: builds the logoscore runtime against the
# commit of logos-liblogos under test (via --override-input), builds lgpm, packages
# and installs the real accounts module, starts the daemon, loads the module, calls
# its methods, and asserts on the output.
#
# ──────────────────────────────────────────────────────────────────────────────
# 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.
# ──────────────────────────────────────────────────────────────────────────────
on:
pull_request:
branches: [master, main]
push:
branches: [master, main]
concurrency:
group: doctests-${{ github.ref }}
cancel-in-progress: true
jobs:
doctests:
name: liblogos 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 logoscore
# against THIS commit of logos-liblogos instead of the latest release.
#
# Fork PRs are the exception: their head commit lives in the fork, not in
# logos-co/logos-liblogos, so nix could not fetch
# `github:logos-co/logos-liblogos/<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.
#
# --release-for pins the {release} placeholder for logos-liblogos to the
# commit under test, so the override URL in the spec
# (`github:logos-co/logos-liblogos{release}`) becomes `.../<sha>` — the
# logoscore runtime is rebuilt against this PR/push rather than master.
# Every other repo URL still resolves to latest.
- name: Run liblogos module-runtime doc-test
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.
nix run github:logos-co/logos-doctest -- run \
doctests/liblogos-module-runtime.test.yaml \
--verbose \
--continue-on-fail \
--release-for logos-liblogos=${{ steps.commit.outputs.sha }} \
--report "${{ runner.temp }}/liblogos-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 }}/liblogos-doctest-report.html" ]; then
cp "${{ runner.temp }}/liblogos-doctest-report.html" report-out/index.html
else
echo "<h1>No report produced</h1>" > report-out/index.html
fi
- name: Upload liblogos execution report
if: always()
uses: actions/upload-artifact@v4
with:
name: liblogos-doctest-report-${{ matrix.os }}
path: report-out/index.html
if-no-files-found: warn
- name: Verify markdown generation
run: |
nix run github:logos-co/logos-doctest -- generate \
doctests/liblogos-module-runtime.test.yaml \
--release-for logos-liblogos=${{ steps.commit.outputs.sha }} \
-o /tmp/liblogos-module-runtime.md
test -s /tmp/liblogos-module-runtime.md
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/liblogos-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>liblogos doc-test reports — $BASE</title>"
echo "<style>body{font:16px system-ui;margin:40px;max-width:640px}a{color:#2563eb}</style>"
echo "<h1>liblogos 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 liblogos 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 = "<!-- liblogos-doctest-report-links -->";
const body =
`${marker}\n` +
`### 📊 liblogos doc-test report\n\n` +
`The real accounts module, run through a logoscore daemon built against ` +
`this commit of liblogos — 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 });
}
+4
View File
@@ -29,3 +29,7 @@ examples/cli
.cache
result*
# Doc-test output: generated artifacts are disposable, but keep the rendered .md
doctests/outputs/*
!doctests/outputs/*.md
+241
View File
@@ -0,0 +1,241 @@
name: "Running a Real Module Against This liblogos"
output: liblogos-module-runtime.md
release: ""
intro: |
`logos-liblogos` is the core of the Logos platform — `logos_host` and the
`liblogos_core` C API that every frontend (the `logoscore` CLI, the basecamp
desktop app) builds on. This doc-test exercises **this** liblogos commit
end-to-end through the headless `logoscore` runtime:
1. Build the `logoscore` CLI, **overriding its `logos-liblogos` input with the
commit under test** — so the runtime you exercise is built against the code
in this repository, not the latest published release.
2. Build the `lgpm` local package manager.
3. Build the real [`accounts_module`](https://github.com/logos-co/logos-accounts-module)
as an `.lgx` package straight from its own flake, and install it into a
`./modules` directory with `lgpm`.
4. Start `logoscore` in daemon mode (`-D`), load `accounts_module`, introspect
it, and call one of its methods — verifying the module actually runs on top
of this liblogos.
Because every layer (host, module loader, IPC) comes from the liblogos commit
under test, a green run is real evidence that this change keeps the module
runtime working.
what_you_build: "The real `accounts_module`, installed with `lgpm` and called through a `logoscore` daemon running on this liblogos commit."
what_you_learn:
- How to build `logoscore` against a specific `logos-liblogos` commit via `--override-input`
- How to build a real module's `.lgx` from its own flake
- How to install an `.lgx` into a modules directory with `lgpm`
- How to start the `logoscore` daemon, load a module, and call its methods
prerequisites:
- |
**Nix** with flakes enabled. Install from [nixos.org](https://nixos.org/download.html), then enable flakes:
```bash
mkdir -p ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
```
Verify: `nix flake --help >/dev/null 2>&1 && echo "Flakes enabled"`
- "**git** — to clone the module repository."
- "A Linux or macOS machine."
sections:
- title: "Build logoscore against this liblogos"
step: true
text: |
Build the `logoscore` CLI from its published flake, but **override its
`logos-liblogos` input** so it links against the commit under test rather
than the latest release. The result is symlinked to `./logos/`.
> The override URL is what pins liblogos to a specific commit: the doc-test
> runner expands a release placeholder on it to a concrete ref. Locally that
> is this checkout's `HEAD` (see `run.sh`); in CI it is the commit being
> tested. With no pin it falls back to latest `master`.
steps:
- title: "Build the CLI with the liblogos override"
run: "nix build 'github:logos-co/logos-logoscore-cli' --override-input logos-liblogos 'github:logos-co/logos-liblogos{release}' --out-link ./logos"
code_block: |
nix build 'github:logos-co/logos-logoscore-cli' \
--override-input logos-liblogos 'github:logos-co/logos-liblogos' \
--out-link ./logos
check_file: "logos/bin/logoscore"
post_text: |
The build produces `logos/bin/logoscore` plus bundled runtime libraries
and a `logos/modules/` directory containing the built-in
`capability_module` (required for the auth handshake when loading
modules). Because `follows` propagates the override, the whole
dependency closure — `logos_host`, `liblogos_core`, every module — is
rebuilt against this liblogos.
- title: "Build the lgpm package manager"
step: true
text: |
`lgpm` installs `.lgx` packages into a modules directory and scans what is
installed. Build it from `logos-liblogos`' own `logos-package-manager`
input and link it as `./lgpm`.
steps:
- title: "Build lgpm"
run: "nix build 'github:logos-co/logos-package-manager#cli' -o lgpm"
check_file: "lgpm/bin/lgpm"
post_text: "The executable is at `./lgpm/bin/lgpm`."
- title: "Build and install the accounts module"
step: true
text: |
Clone [`logos-accounts-module`](https://github.com/logos-co/logos-accounts-module),
build its `.lgx` straight from its flake's `#lgx` output, and install it
into a local `./modules` directory with `lgpm`. Every module built with
[`logos-module-builder`](https://github.com/logos-co/logos-module-builder)
exposes a ready-to-install `#lgx`.
steps:
- title: "Clone the module"
text: |
We clone over HTTPS so the step works in CI; over SSH the URL is
`git@github.com:logos-co/logos-accounts-module.git`.
run: "git clone --depth 1 https://github.com/logos-co/logos-accounts-module.git"
check_file: "logos-accounts-module/flake.nix"
- title: "Build the module's .lgx"
text: |
Build the `#lgx` output and link it as `./accounts-lgx`. (This compiles
the module and its SDK dependencies through Nix, so the first build is
slow.)
run: "nix build 'path:./logos-accounts-module#lgx' -o accounts-lgx"
code_block: |
# From inside the clone this is simply: nix build '.#lgx'
nix build 'path:./logos-accounts-module#lgx' -o accounts-lgx
post_text: "The `.lgx` package is now under `./accounts-lgx/`:"
extra_run:
run: "ls accounts-lgx/*.lgx"
- title: "Seed the modules directory with the bundled capability module"
text: |
`accounts_module` is loaded through the host's capability layer, so the
modules directory also needs the `capability_module` that ships with
`logoscore`. Copy it across first.
run: |
mkdir -p modules
cp -RL ./logos/modules/. ./modules/
check_file: "modules/capability_module/manifest.json"
- title: "Install the .lgx with lgpm"
text: |
Install the freshly-built package into `./modules`. `accounts_module` is
a `core` module, so it goes to `--modules-dir`. The package is unsigned
(a local dev build), so we pass `--allow-unsigned`.
run: "./lgpm/bin/lgpm --modules-dir ./modules --allow-unsigned install --file accounts-lgx/*.lgx"
expect_contains:
- "Installed to:"
- title: "Confirm the install"
text: "Scan the directory and confirm the module landed:"
run: "./lgpm/bin/lgpm --modules-dir ./modules list"
expect_contains:
- "accounts_module"
check_file: "modules/accounts_module/manifest.json"
- title: "Run the daemon and call the module"
step: true
text: |
Start `logoscore` in daemon mode pointed at `./modules`, then use the client
subcommands to load `accounts_module`, introspect it, and call one of its
methods. Daemon output is captured in `logs.txt`.
steps:
- title: "Start the daemon"
text: |
Start logoscore in daemon mode in the background, capturing output to
`logs.txt`:
run: "sh -c './logos/bin/logoscore -D -m ./modules > logs.txt 2>&1 &'"
code_block: "logoscore -D -m ./modules > logs.txt &"
post_text: |
The `-D` flag starts the daemon. The client subcommands below connect to
this running process via the config written under `~/.logoscore/`.
- run: "sleep 3"
- title: "Inspect the startup log"
text: "Review the daemon's startup output:"
run: "cat logs.txt"
- title: "Check daemon status"
text: "Verify the daemon is running:"
run: "./logos/bin/logoscore status"
code_block: "logoscore status"
- title: "List discovered modules"
text: "`accounts_module` should be visible in the scan directory:"
run: "./logos/bin/logoscore list-modules"
code_block: "logoscore list-modules"
expect_contains:
- "accounts_module"
- title: "Load the module"
text: "Load `accounts_module` into the running daemon:"
run: "./logos/bin/logoscore load-module accounts_module"
code_block: "logoscore load-module accounts_module"
expect_contains:
- "accounts_module"
- title: "Confirm the module is loaded"
text: |
Re-run `status`; the module that was `not_loaded` before now reports
`loaded`:
run: "./logos/bin/logoscore status"
code_block: "logoscore status"
expect_contains:
- "accounts_module"
- '"status":"loaded"'
- title: "Introspect the module with module-info"
text: |
`module-info` lists the `Q_INVOKABLE` methods the module exposes — the
same methods you can `call`:
run: "./logos/bin/logoscore module-info accounts_module"
code_block: "logoscore module-info accounts_module"
expect_contains:
- "accounts_module"
- "createRandomMnemonic"
- title: "Call a method"
text: |
Generate a fresh 12-word BIP-39 mnemonic. `createRandomMnemonic` takes
the word count and returns the phrase — a real round-trip through the
go-wallet-sdk C library wrapped by the module, dispatched over liblogos'
IPC:
run: "./logos/bin/logoscore call accounts_module createRandomMnemonic 12"
code_block: "logoscore call accounts_module createRandomMnemonic 12"
expect_contains:
- '"result"'
- title: "Call a second method"
text: |
`lengthToEntropyStrength` maps a mnemonic word count to its entropy
strength in bits — 12 words is 128 bits. This exercises an `int`
round-trip:
run: "./logos/bin/logoscore call accounts_module lengthToEntropyStrength 12"
code_block: "logoscore call accounts_module lengthToEntropyStrength 12"
expect_contains:
- '"result":128'
- title: "Stop the daemon"
text: "Shut the daemon down cleanly:"
run: "./logos/bin/logoscore stop"
code_block: "logoscore stop"
post_text: |
The daemon removes its state file and exits.
- run: "sleep 2"
- title: "Confirm the daemon has stopped"
text: |
With no daemon running, the client reports `not_running` and exits
non-zero, so we add `|| true` to let the doc-test assert on the output:
run: "./logos/bin/logoscore status || true"
code_block: "logoscore status"
expect_contains:
- '"status":"not_running"'
+272
View File
@@ -0,0 +1,272 @@
# Running a Real Module Against This liblogos
`logos-liblogos` is the core of the Logos platform — `logos_host` and the
`liblogos_core` C API that every frontend (the `logoscore` CLI, the basecamp
desktop app) builds on. This doc-test exercises **this** liblogos commit
end-to-end through the headless `logoscore` runtime:
1. Build the `logoscore` CLI, **overriding its `logos-liblogos` input with the
commit under test** — so the runtime you exercise is built against the code
in this repository, not the latest published release.
2. Build the `lgpm` local package manager.
3. Build the real [`accounts_module`](https://github.com/logos-co/logos-accounts-module)
as an `.lgx` package straight from its own flake, and install it into a
`./modules` directory with `lgpm`.
4. Start `logoscore` in daemon mode (`-D`), load `accounts_module`, introspect
it, and call one of its methods — verifying the module actually runs on top
of this liblogos.
Because every layer (host, module loader, IPC) comes from the liblogos commit
under test, a green run is real evidence that this change keeps the module
runtime working.
**What you'll build:** The real `accounts_module`, installed with `lgpm` and called through a `logoscore` daemon running on this liblogos commit.
**What you'll learn:**
- How to build `logoscore` against a specific `logos-liblogos` commit via `--override-input`
- How to build a real module's `.lgx` from its own flake
- How to install an `.lgx` into a modules directory with `lgpm`
- How to start the `logoscore` daemon, load a module, and call its methods
## Prerequisites
- **Nix** with flakes enabled. Install from [nixos.org](https://nixos.org/download.html), then enable flakes:
```bash
mkdir -p ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
```
Verify: `nix flake --help >/dev/null 2>&1 && echo "Flakes enabled"`
- **git** — to clone the module repository.
- A Linux or macOS machine.
---
## Step 1: Build logoscore against this liblogos
Build the `logoscore` CLI from its published flake, but **override its
`logos-liblogos` input** so it links against the commit under test rather
than the latest release. The result is symlinked to `./logos/`.
> The override URL is what pins liblogos to a specific commit: the doc-test
> runner expands a release placeholder on it to a concrete ref. Locally that
> is this checkout's `HEAD` (see `run.sh`); in CI it is the commit being
> tested. With no pin it falls back to latest `master`.
### 1.1 Build the CLI with the liblogos override
```bash
nix build 'github:logos-co/logos-logoscore-cli' \
--override-input logos-liblogos 'github:logos-co/logos-liblogos' \
--out-link ./logos
```
The build produces `logos/bin/logoscore` plus bundled runtime libraries
and a `logos/modules/` directory containing the built-in
`capability_module` (required for the auth handshake when loading
modules). Because `follows` propagates the override, the whole
dependency closure — `logos_host`, `liblogos_core`, every module — is
rebuilt against this liblogos.
---
## Step 2: Build the lgpm package manager
`lgpm` installs `.lgx` packages into a modules directory and scans what is
installed. Build it from `logos-liblogos`' own `logos-package-manager`
input and link it as `./lgpm`.
### 2.1 Build lgpm
```bash
nix build 'github:logos-co/logos-package-manager#cli' -o lgpm
```
The executable is at `./lgpm/bin/lgpm`.
---
## Step 3: Build and install the accounts module
Clone [`logos-accounts-module`](https://github.com/logos-co/logos-accounts-module),
build its `.lgx` straight from its flake's `#lgx` output, and install it
into a local `./modules` directory with `lgpm`. Every module built with
[`logos-module-builder`](https://github.com/logos-co/logos-module-builder)
exposes a ready-to-install `#lgx`.
### 3.1 Clone the module
We clone over HTTPS so the step works in CI; over SSH the URL is
`git@github.com:logos-co/logos-accounts-module.git`.
```bash
git clone --depth 1 https://github.com/logos-co/logos-accounts-module.git
```
### 3.2 Build the module's .lgx
Build the `#lgx` output and link it as `./accounts-lgx`. (This compiles
the module and its SDK dependencies through Nix, so the first build is
slow.)
```bash
# From inside the clone this is simply: nix build '.#lgx'
nix build 'path:./logos-accounts-module#lgx' -o accounts-lgx
```
The `.lgx` package is now under `./accounts-lgx/`:
```bash
ls accounts-lgx/*.lgx
```
### 3.3 Seed the modules directory with the bundled capability module
`accounts_module` is loaded through the host's capability layer, so the
modules directory also needs the `capability_module` that ships with
`logoscore`. Copy it across first.
```bash
mkdir -p modules
cp -RL ./logos/modules/. ./modules/
```
### 3.4 Install the .lgx with lgpm
Install the freshly-built package into `./modules`. `accounts_module` is
a `core` module, so it goes to `--modules-dir`. The package is unsigned
(a local dev build), so we pass `--allow-unsigned`.
```bash
./lgpm/bin/lgpm --modules-dir ./modules --allow-unsigned install --file accounts-lgx/*.lgx
```
### 3.5 Confirm the install
Scan the directory and confirm the module landed:
```bash
./lgpm/bin/lgpm --modules-dir ./modules list
```
---
## Step 4: Run the daemon and call the module
Start `logoscore` in daemon mode pointed at `./modules`, then use the client
subcommands to load `accounts_module`, introspect it, and call one of its
methods. Daemon output is captured in `logs.txt`.
### 4.1 Start the daemon
Start logoscore in daemon mode in the background, capturing output to
`logs.txt`:
```bash
logoscore -D -m ./modules > logs.txt &
```
The `-D` flag starts the daemon. The client subcommands below connect to
this running process via the config written under `~/.logoscore/`.
```bash
sleep 3
```
### 4.2 Inspect the startup log
Review the daemon's startup output:
```bash
cat logs.txt
```
### 4.3 Check daemon status
Verify the daemon is running:
```bash
logoscore status
```
### 4.4 List discovered modules
`accounts_module` should be visible in the scan directory:
```bash
logoscore list-modules
```
### 4.5 Load the module
Load `accounts_module` into the running daemon:
```bash
logoscore load-module accounts_module
```
### 4.6 Confirm the module is loaded
Re-run `status`; the module that was `not_loaded` before now reports
`loaded`:
```bash
logoscore status
```
### 4.7 Introspect the module with module-info
`module-info` lists the `Q_INVOKABLE` methods the module exposes — the
same methods you can `call`:
```bash
logoscore module-info accounts_module
```
### 4.8 Call a method
Generate a fresh 12-word BIP-39 mnemonic. `createRandomMnemonic` takes
the word count and returns the phrase — a real round-trip through the
go-wallet-sdk C library wrapped by the module, dispatched over liblogos'
IPC:
```bash
logoscore call accounts_module createRandomMnemonic 12
```
### 4.9 Call a second method
`lengthToEntropyStrength` maps a mnemonic word count to its entropy
strength in bits — 12 words is 128 bits. This exercises an `int`
round-trip:
```bash
logoscore call accounts_module lengthToEntropyStrength 12
```
### 4.10 Stop the daemon
Shut the daemon down cleanly:
```bash
logoscore stop
```
The daemon removes its state file and exits.
```bash
sleep 2
```
### 4.11 Confirm the daemon has stopped
With no daemon running, the client reports `not_running` and exits
non-zero, so we add `|| true` to let the doc-test assert on the output:
```bash
logoscore status
```
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
#
# Execute the liblogos module-runtime doc-test end-to-end and regenerate its
# Markdown.
#
# The runner is the shared `doctest` CLI
# (https://github.com/logos-co/logos-doctest), invoked directly via its flake.
# `doctest run` executes every command in a temp directory (building logoscore
# against this liblogos, building lgpm, packaging/installing the accounts
# module, starting the daemon, calling methods) and asserts on the output;
# `doctest generate` renders the same spec to Markdown under outputs/;
# `doctest clean` strips build artifacts so only the generated docs remain.
#
# To run against a local logos-doctest checkout instead of the published flake,
# set DOCTEST, e.g.: DOCTEST="nix run path:../../logos-doctest --" ./run.sh
#
set -euo pipefail
# Run from this doctests/ directory regardless of where the script is invoked from.
cd "$(dirname "$0")"
# The doctest CLI. Override by exporting DOCTEST (space-separated command).
read -r -a DOCTEST <<< "${DOCTEST:-nix run github:logos-co/logos-doctest --}"
OUTPUT_DIR="./outputs"
SPEC="liblogos-module-runtime.test.yaml"
# Build the doc-test against THIS repo's current commit rather than the latest
# published flake. The spec overrides logoscore's `logos-liblogos` input with
# `github:logos-co/logos-liblogos{release}`, and the pin below makes {release}
# expand to $COMMIT — so the runtime is built against exactly what's checked out
# here. Override by exporting COMMIT (e.g. a tag), or set COMMIT="" to fall back
# to latest master.
#
# Note: nix fetches the commit from the GitHub remote, so $COMMIT must be pushed
# to logos-co/logos-liblogos. A local-only / uncommitted HEAD won't resolve;
# export COMMIT="" (or push first) in that case.
COMMIT="${COMMIT-$(git rev-parse HEAD)}"
RELEASE_FOR=()
if [ -n "${COMMIT}" ]; then
RELEASE_FOR=(--release-for "logos-liblogos=${COMMIT}")
echo "==> Pinning logos-liblogos to ${COMMIT}"
else
echo "==> COMMIT empty; building against latest logos-liblogos master"
fi
echo "==> Clearing previous ${OUTPUT_DIR}/"
# A prior run copies module artifacts out of the read-only nix store, so the
# directories land read-only (r-x) too. `rm -rf` can't delete files inside a
# directory it can't write to, so restore write permission first.
if [ -e "${OUTPUT_DIR}" ]; then
chmod -R u+w "${OUTPUT_DIR}" 2>/dev/null || true
fi
rm -rf "${OUTPUT_DIR}"
echo "==> Running ${SPEC} into ${OUTPUT_DIR}/"
# ${RELEASE_FOR[@]+...} guards the expansion so an empty array doesn't trip
# `set -u` on older bash (e.g. macOS's stock 3.2).
"${DOCTEST[@]}" run "${SPEC}" \
--verbose \
--continue-on-fail \
${RELEASE_FOR[@]+"${RELEASE_FOR[@]}"} \
--output-dir "${OUTPUT_DIR}/"
echo "==> Generating ${OUTPUT_DIR}/liblogos-module-runtime.md"
mkdir -p "${OUTPUT_DIR}"
"${DOCTEST[@]}" generate "${SPEC}" \
${RELEASE_FOR[@]+"${RELEASE_FOR[@]}"} \
-o "${OUTPUT_DIR}/liblogos-module-runtime.md"
if [ ! -d "${OUTPUT_DIR}" ]; then
echo "==> No ${OUTPUT_DIR}/ produced; nothing to clean."
exit 0
fi
echo "==> Cleaning build artifacts from ${OUTPUT_DIR}/"
"${DOCTEST[@]}" clean "${OUTPUT_DIR}" --verbose
echo "==> Done. Rendered doc is in ${OUTPUT_DIR}/liblogos-module-runtime.md"