Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/base-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ on:
- .github/workflows/base-image.yml
workflow_dispatch:

# One publication of `:latest` at a time, the newest queued last: two merges a
# minute apart started two builds, and the older one finished second and put
# its image back over the newer one.
concurrency:
group: publish-base-image
cancel-in-progress: false

permissions: {}

jobs:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ jobs:
docker:
needs: test
runs-on: ubuntu-latest
# Pushes `:latest` on main; two pushes racing would let the older image win.
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
packages: write
Expand Down
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,21 @@ scope. Add yours.
suite hung. The close had been the only thing ending it. → Before removing an
exit from a loop, find what else that exit was the bound for, and test the
loop with a medium that never waits.
- **`ls a b` used as "does either exist".** A probe for a library in `lib` or
`lib64` listed both; `ls` fails when either is missing, so a library in `lib`
alone was never seen and the rebuild it was meant to trigger never ran. The
end-to-end scenario caught it; the unit tests had not been written yet. → In
shell, test existence with `[ -e ]` per path, and write the unit test for a
probe with each of its branches before the scenario that relies on it.
- **A test edited from memory of it.** A replacement was written for a cache
test the agent had not opened, and matched nothing; the half of the edit that
did apply left the suite red for a reason that looked like the code's. →
Read the test before writing its replacement, every time.
- **A path read relative to the current directory in a script that is
sourced.** `start.sh` read `scripts/liboqs.lock` relative to wherever it had
`cd`'d, which under the test harness was `/usr/bin`; every stamp then looked
wrong. → A file that decides trust is read from the script's own tree
(`BASH_SOURCE`), never from the current directory.
- **A rate called "at rest" that was a mesh still settling.** About 30 packets
per second per node was published as idle traffic; it was taken 25 s after
start, with searches and handshakes still running, and the same mesh sent
Expand Down
20 changes: 20 additions & 0 deletions BUGSVULNS.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4143,3 +4143,23 @@ where a node holds no link (93 of 100 on average over four seeds, the same as
without, for 4 to 6 packets per second per node at rest instead of 2.4 to 4.4). And there is nothing larger than 240 nodes to measure on this
machine: the simulator needs more cores than the 8 it has once the mesh is
chatty, and at rest it is the first number to check again.

## 109 — medium — Two builds raced to publish the base image, and the older won

> **Status — fixed.** Tests: `tests/test_workflows_pinned.py`
> (`test_what_publishes_latest_does_it_one_run_at_a_time`).

**Where** — `.github/workflows/base-image.yml`; the `docker` job of `ci.yml`
pushes the application image's `:latest` the same way.

**What happens** — merging two PRs that both touched the base image a minute
apart started two builds. The one from the older commit finished second, so
`nmesh-base:latest` went back to a build from before liboqs was compiled from a
checked source, without `/opt/liboqs`; every application build after it failed
on the missing directory. Nothing in the image says which commit it came from,
so the only symptom was that later builds broke.

**Fixed** — both publishers run in a concurrency group that never cancels a run
in progress, so they finish in the order they were queued and the newest commit
publishes last. The base was republished from `main` by hand.

17 changes: 14 additions & 3 deletions Docs/Setup/guide
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,20 @@ recompiling. `start.sh` also looks at the prefixes `install.sh` points it at
(`OQS_REUSE_FROM`, a `:`-separated list): the installer's home, their old user
installation, `/opt/nmesh`.

Reuse is never decided on a version number or a file name: the wrapper actually
**loads** the candidate library and both required algorithms answer, or we
recompile. After the copy, the check is run again **at the destination**. A
Reuse is never decided on a version number or a file name: the candidate must
carry **`nmesh-liboqs.lock`**, the stamp `scripts/build_liboqs.sh` writes beside
every build it proved, matching the lock in this tree; it must belong to this
account or to root and be writable by no group and no one else (the stamp is a
file like any other); and the wrapper must actually **load** it with both
required algorithms answering. After the copy, the check is run again **at the
destination**.

A library already in the prefix with no current stamp — every one built before
the source was checked — is **rebuilt** at the next start: beside the old one,
swapped in only once the new one loads, so a node that cannot rebuild (no
network, no compiler) keeps what it has, says so, and tries again next time.
A library the system provides (a distribution's package, Homebrew) carries no
stamp and needs none. A
wrapper version bump changes the cache key and therefore forces a real rebuild —
wrapper and library must stay paired.

Expand Down
Loading
Loading