mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-21 18:43:22 +00:00
ci: start the app job's test backend again in the Claude jobs, as the app points at its directory
The app is built in the app job with that job's backend's directory address in DIRECTORY_SERVICE_LINK, and since master moved Claude into its own job, that job started a new backend with another address. The app job now uploads its stopped backend's work dir (without the binaries), and the Claude job restarts it with a new backend.sh resume: the same servers' keys and databases, so the same addresses. The relay, directory, names server and contacts start through the functions start uses, split out of it. The iOS Claude job's smoke test also gets BACKEND_DIR, as the app job's does. [claude-test] [ci-backend]
This commit is contained in:
@@ -313,9 +313,25 @@ jobs:
|
||||
scripts/ci/desktop-linux/smoke-test.sh apps/multiplatform/release/main/app/simplex "$RUNNER_TEMP/smoke-test" ${cases:-}
|
||||
|
||||
- name: Stop test backend
|
||||
id: stop-backend
|
||||
if: ${{ !cancelled() && steps.backend.outcome != 'skipped' }}
|
||||
run: scripts/ci/desktop-linux/backend.sh stop "$RUNNER_TEMP/backend"
|
||||
|
||||
# the app points at this backend's directory, so the Claude job starts this backend again rather than a new one
|
||||
- name: Pack backend state
|
||||
if: ${{ !cancelled() && steps.backend.outcome == 'success' && steps.stop-backend.outcome == 'success' && needs.plan.outputs.claude-backend == 'yes' }}
|
||||
run: tar -czf "$RUNNER_TEMP/backend-state.tar.gz" --exclude=./bin -C "$RUNNER_TEMP/backend" .
|
||||
|
||||
- name: Upload backend state
|
||||
if: ${{ !cancelled() && steps.backend.outcome == 'success' && steps.stop-backend.outcome == 'success' && needs.plan.outputs.claude-backend == 'yes' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: desktop-backend-state
|
||||
path: ${{ runner.temp }}/backend-state.tar.gz
|
||||
compression-level: 0
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload smoke test results
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
@@ -396,11 +412,21 @@ jobs:
|
||||
run-id: ${{ needs.plan.outputs.backend-run || github.run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Start test backend
|
||||
- name: Download backend state
|
||||
if: needs.plan.outputs.claude-backend == 'yes'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: desktop-backend-state
|
||||
path: ${{ runner.temp }}
|
||||
|
||||
- name: Start the app job's test backend again
|
||||
id: backend
|
||||
if: needs.plan.outputs.claude-backend == 'yes'
|
||||
timeout-minutes: 30
|
||||
run: scripts/ci/desktop-linux/backend.sh start ~/backend/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/backend"
|
||||
tar -xzf "$RUNNER_TEMP/backend-state.tar.gz" -C "$RUNNER_TEMP/backend"
|
||||
scripts/ci/desktop-linux/backend.sh resume ~/backend/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
||||
|
||||
# ui goes on every shell's PATH, where Claude's Bash tool finds it whatever environment it builds
|
||||
- name: Install Claude Code and the ui command
|
||||
|
||||
@@ -295,9 +295,25 @@ jobs:
|
||||
run: scripts/ci/ios-sim/smoke-test.sh "$RUNNER_TEMP/DerivedData/Build/Products/Debug-iphonesimulator/SimpleX.app" "$RUNNER_TEMP/smoke-test"
|
||||
|
||||
- name: Stop test backend
|
||||
id: stop-backend
|
||||
if: ${{ !cancelled() && env.START_BACKEND == 'true' }}
|
||||
run: scripts/ci/ios-sim/backend.sh stop "$RUNNER_TEMP/backend"
|
||||
|
||||
# the app points at this backend's directory, so the Claude job starts this backend again rather than a new one
|
||||
- name: Pack backend state
|
||||
if: ${{ !cancelled() && steps.stop-backend.outcome == 'success' && needs.plan.outputs.claude-backend == 'yes' }}
|
||||
run: tar -czf "$RUNNER_TEMP/backend-state.tar.gz" --exclude=./bin -C "$RUNNER_TEMP/backend" .
|
||||
|
||||
- name: Upload backend state
|
||||
if: ${{ !cancelled() && steps.stop-backend.outcome == 'success' && needs.plan.outputs.claude-backend == 'yes' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ios-sim-backend-state
|
||||
path: ${{ runner.temp }}/backend-state.tar.gz
|
||||
compression-level: 0
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload smoke test results
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
@@ -368,16 +384,28 @@ jobs:
|
||||
run-id: ${{ needs.plan.outputs.backend-run || github.run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Start test backend
|
||||
- name: Download backend state
|
||||
if: needs.plan.outputs.claude-backend == 'yes'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ios-sim-backend-state
|
||||
path: ${{ runner.temp }}
|
||||
|
||||
- name: Start the app job's test backend again
|
||||
id: backend
|
||||
if: needs.plan.outputs.claude-backend == 'yes'
|
||||
timeout-minutes: 30
|
||||
run: scripts/ci/ios-sim/backend.sh start ~/Downloads/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/backend"
|
||||
tar -xzf "$RUNNER_TEMP/backend-state.tar.gz" -C "$RUNNER_TEMP/backend"
|
||||
scripts/ci/ios-sim/backend.sh resume ~/Downloads/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
||||
|
||||
- name: Install AXe
|
||||
run: scripts/ci/ios-sim/install-axe.sh
|
||||
|
||||
- name: Run smoke test
|
||||
env:
|
||||
BACKEND_DIR: ${{ steps.backend.outcome == 'success' && format('{0}/backend', runner.temp) || '' }}
|
||||
run: scripts/ci/ios-sim/smoke-test.sh "$RUNNER_TEMP/SimpleX.app" "$RUNNER_TEMP/claude-test/smoke-test"
|
||||
|
||||
- name: Install Claude Code
|
||||
|
||||
@@ -25,7 +25,7 @@ Queueing for a runner adds from under a minute to 20 minutes. Results are artifa
|
||||
|
||||
- `ui`: the UI driver. Compose for Desktop has no accessibility tree on Linux, so it finds text on screenshots by OCR (tesseract) and acts with xdotool on an Xvfb display. `ui` alone prints its commands: start (with app settings), shot, text, find, wait, click, click-text, scroll, type, key, avatar (a profile image's colour), ink (a text's height), do (several in a row), stop. `screen-text.py`, `screen-colour.py` and `screen-ink.py` are its helpers.
|
||||
- `smoke-test.sh <app dir> <out dir> [start|onboarding|setup|backend|search]`: the scripted cases; the default is onboarding and both layouts. `setup` also creates a local group and makes the test SMP server the app's only names server; `backend` does that, connects to the seeded name, and checks both layouts; `search`, which the workflow runs when the backend started, also searches in both layouts: "Search in Directory" for "ci seed", and the full search for the name `seeded` with Enter (the local chat, the Names part, the Directory part, in order), and with the toolbar at the bottom the full search in the New chat sheet, checking headers, rows, avatars, counts and the cards hidden while results show. It then joins `ci_channel` from its Directory row and checks, in both layouts, the note "All results on this page are already in your chats" when the typed `ci_channel` finds only the listed chat, and, with "seeded notes" open and `ci seed` typed, `ci_channel` as its chat's row in the Directory part (OCR reads no `#` before its name and no "Join channel" next to it; `#ci_group` keeps "Join group") that opens the channel without an alert; and the same row in the New chat sheet. The search case makes 129 checks (106 before the known-chat ones).
|
||||
- `backend.sh start <tar.gz> <dir>` / `stop <dir>`: the test backend, below.
|
||||
- `backend.sh start <tar.gz> <dir>` / `resume <tar.gz> <dir>` / `stop <dir>`: the test backend, below; `resume` starts the same backend again from a stopped work dir, for the Claude job.
|
||||
- `claude-test.sh`, `claude-test.md` (the prompt), `claude-search.md` (the prompt when the backend runs), `claude-scope` (whether the prompt uses the test backend), `claude-guard.py`, `claude-usage.py`: the Claude job.
|
||||
- `build-core.sh`, `install-core.sh`, `build-backend.sh`, `earlier-run.sh`, `release-app.sh`, `test-summary.py`: builds, reuse and reports.
|
||||
|
||||
@@ -41,7 +41,7 @@ It writes the directory's address to `<dir>/directory-link` and the SMP server's
|
||||
|
||||
## The Claude job
|
||||
|
||||
It needs the GitHub environment `app-sim-claude` with the secret `CLAUDE_CODE_OAUTH_TOKEN`, a Claude subscription token. Which branches may use it is decided only by that environment's branch rule in the repository settings: on a branch the rule doesn't allow, a requested Claude run fails at the environment check. It runs once the app job has passed, on that job's distributable, one per branch at a time. The token goes only to the step that runs `claude`; `claude-test.sh` passes it to the `claude` process alone, and every `ui` call unsets it too. The job starts the test backend when `claude-scope` says `backend: yes`, as it does on this branch; a run that asks for Claude must then ask for the backend too, or the plan job fails. With the backend it sets the app up at the chat list (`smoke-test.sh … setup`) and gives Claude `claude-search.md`, the online search test, in place of the onboarding path in `claude-test.md`: the directory and full searches in both layouts (parts A and B), then part C, which joins `ci_channel` from its Directory row and checks that later searches show it as a chat (the note for a page of chats already listed, its chat row in the Directory part of the chat list and of New chat, opening without an alert). The job installs Claude Code at the version the iOS workflow pins too, and runs `claude-haiku-5-5` with `--max-turns 120`, `--max-budget-usd 3` and the iOS sessions' other flags (`--restricted`, `--permission-prompts none`, `--effort high`). Its Bash runs only single `ui` commands (`claude-guard.py`, a PreToolUse hook: Claude Code runs read-only commands without asking in every permission mode). Before anything the session wrote is printed or uploaded, `claude-test.sh` checks it for the token; if it's there, the output, transcript, Claude Code's stderr and the guard log are deleted and the job fails. It passes when Claude's final message ends with `CLAUDE-TEST: PASS`; `report.md`, the transcript, the guard's log and `claude-usage.json` are in `desktop-claude-test`.
|
||||
It needs the GitHub environment `app-sim-claude` with the secret `CLAUDE_CODE_OAUTH_TOKEN`, a Claude subscription token. Which branches may use it is decided only by that environment's branch rule in the repository settings: on a branch the rule doesn't allow, a requested Claude run fails at the environment check. It runs once the app job has passed, on that job's distributable, one per branch at a time. The token goes only to the step that runs `claude`; `claude-test.sh` passes it to the `claude` process alone, and every `ui` call unsets it too. The job starts the test backend when `claude-scope` says `backend: yes`, as it does on this branch; a run that asks for Claude must then ask for the backend too, or the plan job fails. The app job built the distributable pointing at its own backend's directory, which a new backend would not have, so the app job saves that backend's stopped work dir (the artifact `desktop-backend-state`, without the binaries) and the Claude job starts it again with `backend.sh resume`: the same servers' keys and databases, so the same addresses. With the backend it sets the app up at the chat list (`smoke-test.sh … setup`) and gives Claude `claude-search.md`, the online search test, in place of the onboarding path in `claude-test.md`: the directory and full searches in both layouts (parts A and B), then part C, which joins `ci_channel` from its Directory row and checks that later searches show it as a chat (the note for a page of chats already listed, its chat row in the Directory part of the chat list and of New chat, opening without an alert). The job installs Claude Code at the version the iOS workflow pins too, and runs `claude-haiku-5-5` with `--max-turns 120`, `--max-budget-usd 3` and the iOS sessions' other flags (`--restricted`, `--permission-prompts none`, `--effort high`). Its Bash runs only single `ui` commands (`claude-guard.py`, a PreToolUse hook: Claude Code runs read-only commands without asking in every permission mode). Before anything the session wrote is printed or uploaded, `claude-test.sh` checks it for the token; if it's there, the output, transcript, Claude Code's stderr and the guard log are deleted and the job fails. It passes when Claude's final message ends with `CLAUDE-TEST: PASS`; `report.md`, the transcript, the guard's log and `claude-usage.json` are in `desktop-claude-test`.
|
||||
|
||||
## Cost
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
# Usage:
|
||||
# scripts/ci/desktop-linux/backend.sh start <backend .tar.gz> <work dir> writes the directory's short address to <work dir>/directory-link
|
||||
# and the names server's address to <work dir>/names-server
|
||||
# scripts/ci/desktop-linux/backend.sh resume <backend .tar.gz> <work dir> starts again, on another runner, what start left stopped in
|
||||
# <work dir>, with the same addresses
|
||||
# scripts/ci/desktop-linux/backend.sh stop <work dir> stops the servers, prints their logs
|
||||
# The relay, the directory, the SMP server and the resolver keep running between start and stop.
|
||||
|
||||
@@ -90,9 +92,13 @@ avatar() {
|
||||
echo "$uri"
|
||||
}
|
||||
|
||||
start_relay() {
|
||||
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null > "$work/relay.log" 2>&1 &
|
||||
run_relay() {
|
||||
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null >> "$work/relay.log" 2>&1 &
|
||||
echo $! > "$work/relay.pid"
|
||||
}
|
||||
|
||||
start_relay() {
|
||||
run_relay
|
||||
local deadline=$((SECONDS + 120))
|
||||
until grep -qE '^https://[^ ]+/r#' "$work/relay.log"; do
|
||||
if ! kill -0 "$(cat "$work/relay.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
||||
@@ -124,16 +130,20 @@ create_directory() {
|
||||
}
|
||||
|
||||
start_directory() {
|
||||
local pid deadline=$((SECONDS + 60))
|
||||
# -y: the service adds its own tables to the database the CLI created, and would otherwise ask on stdin
|
||||
nohup "$bin/simplex-directory-service" -d "$work/directory" -y --super-users "$1" < /dev/null >> "$work/directory.log" 2>&1 &
|
||||
pid=$!
|
||||
echo "$pid" > "$work/directory.pid"
|
||||
# its output is block-buffered, so it shows that its chat has started by connecting to its servers
|
||||
echo $! > "$work/directory.pid"
|
||||
wait_connected directory
|
||||
}
|
||||
|
||||
# A chat process shows that its chat has started by connecting to its servers: its output is block-buffered.
|
||||
wait_connected() {
|
||||
local pid deadline=$((SECONDS + 60))
|
||||
pid=$(cat "$work/$1.pid")
|
||||
until grep -q "pid=$pid," <<< "$(ss -Htnp state established)"; do
|
||||
if ! kill -0 "$pid" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
||||
echo "error: the directory service did not start"
|
||||
cat "$work/directory.log"
|
||||
echo "error: $1 did not start"
|
||||
cat "$work/$1.log"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
@@ -214,10 +224,8 @@ seed_channel() {
|
||||
|
||||
# An SMP server on 127.0.0.1 whose [NAMES] section asks the local resolver, which answers from names.json.
|
||||
start_names_server() {
|
||||
local cfg=$work/smp/etc logs=$work/smp/var ini deadline
|
||||
local cfg=$work/smp/etc logs=$work/smp/var ini
|
||||
echo '{}' > "$work/names.json"
|
||||
nohup python3 "$here/names-resolver.py" "$resolver_port" "$work/names.json" < /dev/null > "$work/resolver.log" 2>&1 &
|
||||
echo $! > "$work/resolver.pid"
|
||||
SMP_SERVER_CFG_PATH=$cfg SMP_SERVER_LOG_PATH=$logs "$bin/smp-server" init -y --ip 127.0.0.1 --no-password --disable-web
|
||||
ini=$cfg/smp-server.ini
|
||||
# only 5223: 443 needs root
|
||||
@@ -230,7 +238,17 @@ start_names_server() {
|
||||
grep -E '^port|^\[NAMES\]|enable|resolver' "$ini"
|
||||
return 1
|
||||
fi
|
||||
SMP_SERVER_CFG_PATH=$cfg SMP_SERVER_LOG_PATH=$logs nohup "$bin/smp-server" start < /dev/null > "$work/smp-server.log" 2>&1 &
|
||||
run_names_server
|
||||
names_server="smp://$(cat "$cfg/fingerprint")@127.0.0.1"
|
||||
echo "$names_server" > "$work/names-server"
|
||||
echo "names server: $names_server"
|
||||
}
|
||||
|
||||
run_names_server() {
|
||||
local deadline
|
||||
nohup python3 "$here/names-resolver.py" "$resolver_port" "$work/names.json" < /dev/null >> "$work/resolver.log" 2>&1 &
|
||||
echo $! > "$work/resolver.pid"
|
||||
SMP_SERVER_CFG_PATH=$work/smp/etc SMP_SERVER_LOG_PATH=$work/smp/var nohup "$bin/smp-server" start < /dev/null >> "$work/smp-server.log" 2>&1 &
|
||||
echo $! > "$work/smp-server.pid"
|
||||
deadline=$((SECONDS + 60))
|
||||
until (exec 3<> /dev/tcp/127.0.0.1/5223) 2> /dev/null; do
|
||||
@@ -241,9 +259,6 @@ start_names_server() {
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
names_server="smp://$(cat "$cfg/fingerprint")@127.0.0.1"
|
||||
echo "$names_server" > "$work/names-server"
|
||||
echo "names server: $names_server"
|
||||
}
|
||||
|
||||
# A CLI client started with -s uses only that server and, once its user exists, gives it every role, names included.
|
||||
@@ -371,6 +386,18 @@ start() {
|
||||
seed_name
|
||||
}
|
||||
|
||||
# The servers' keys and the clients' databases stay in the work dir, so its addresses are the same again.
|
||||
resume() {
|
||||
bin=$work/bin
|
||||
mkdir -p "$bin"
|
||||
tar -C "$bin" -xzf "$1"
|
||||
run_relay
|
||||
wait_connected relay
|
||||
start_directory "$(db_query directory "SELECT contact_id FROM contacts WHERE local_display_name = '$admin'"):$admin"
|
||||
run_names_server
|
||||
echo "resumed: directory $(cat "$work/directory-link"), names server $(cat "$work/names-server")"
|
||||
}
|
||||
|
||||
stop() {
|
||||
local p
|
||||
for p in directory relay smp-server; do
|
||||
@@ -395,12 +422,16 @@ case "${1:-}" in
|
||||
mkdir -p "$work"
|
||||
start "$2"
|
||||
;;
|
||||
resume)
|
||||
work=$3
|
||||
resume "$2"
|
||||
;;
|
||||
stop)
|
||||
work=$2
|
||||
stop
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 start <backend .tar.gz> <work dir> | stop <work dir>"
|
||||
echo "usage: $0 start <backend .tar.gz> <work dir> | resume <backend .tar.gz> <work dir> | stop <work dir>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -22,7 +22,7 @@ The macOS jobs run on `macos-26`, the image with Xcode 26 and the iPhone 17 simu
|
||||
|
||||
## The Claude session
|
||||
|
||||
It runs in its own job, `claude`, once the app job has passed: the job installs that job's build (the artifact `ios-sim-app`, kept a day) in a new simulator, runs the smoke test to reach the sessions' starting state, then one session per layout listed in `claude-scope`, with Claude Code installed from npm at the version the desktop workflow pins too. Only this job references the environment `app-sim-claude`, which holds the secret `CLAUDE_CODE_OAUTH_TOKEN` (a Claude subscription token) and admits the branches its rule allows; one runs per branch at a time. Only the Claude step gets the token, and `claude-test.sh` passes it to the `claude` processes alone. The job checks out without persisting its GitHub token and installs a pinned AXe (`install-axe.sh`), since it holds the Claude token. It starts the test backend when `claude-scope` says `backend: yes`; a run that asks for Claude must then ask for the backend too, or the plan job fails. On master it says `no`, since the smoke plan doesn't use the backend.
|
||||
It runs in its own job, `claude`, once the app job has passed: the job installs that job's build (the artifact `ios-sim-app`, kept a day) in a new simulator, runs the smoke test to reach the sessions' starting state, then one session per layout listed in `claude-scope`, with Claude Code installed from npm at the version the desktop workflow pins too. Only this job references the environment `app-sim-claude`, which holds the secret `CLAUDE_CODE_OAUTH_TOKEN` (a Claude subscription token) and admits the branches its rule allows; one runs per branch at a time. Only the Claude step gets the token, and `claude-test.sh` passes it to the `claude` processes alone. The job checks out without persisting its GitHub token and installs a pinned AXe (`install-axe.sh`), since it holds the Claude token. It starts the test backend when `claude-scope` says `backend: yes`; a run that asks for Claude must then ask for the backend too, or the plan job fails. On master it says `no`, since the smoke plan doesn't use the backend. The app job built the app pointing at its own backend's directory, which a new backend would not have, so the app job saves that backend's stopped work dir (the artifact `ios-sim-backend-state`, without the binaries) and the Claude job starts it again with `backend.sh resume`: the same servers' keys and databases, so the same addresses.
|
||||
|
||||
Each session's final message ends with `CLAUDE-TEST: PASS`, or `CLAUDE-TEST: FAIL` and the failed cases; the step fails unless every session passes.
|
||||
|
||||
@@ -49,7 +49,7 @@ Haiku 5.5 drives the app well: the smoke plan takes a few dozen turns and cents;
|
||||
|
||||
With the backend, the workflow points the app at the test directory before building it (`DIRECTORY_SERVICE_LINK` in `apps/ios/Shared/Model/DirectorySearch.swift`, the only change it makes to app sources), and the smoke test goes on to search it: the Directory header above the results and their avatars, in both layouts; once the profile has joined `ci_cards`, the note "All results on this page are already in your chats" when that channel, listed as a chat, is the page's only result; the chat list's cards shown while typing and hidden under the results, in both layouts; `ci_cards` found by `ci cards` (which the local filter doesn't match) shown as its chat row, with no `#ci_cards` or "Join channel", and opening the channel, on the chat list in both layouts and in New chat; and "Show more" disabled while its page loads.
|
||||
|
||||
It writes what it seeded to `<work dir>/seed.md` (the Claude sessions get it) and logs to `<work dir>/*.log`; `stop`, `directory-stop`, `directory-start` and `listings` are its other commands. The app reaches it through the simulator, which shares the runner's network; pointing the app at the names server is a few steps in Settings, described in `claude-prompt.md`.
|
||||
It writes what it seeded to `<work dir>/seed.md` (the Claude sessions get it) and logs to `<work dir>/*.log`; `resume` (the same backend again from a stopped work dir, for the Claude job), `stop`, `directory-stop`, `directory-start` and `listings` are its other commands. The app reaches it through the simulator, which shares the runner's network; pointing the app at the names server is a few steps in Settings, described in `claude-prompt.md`.
|
||||
|
||||
## Other files
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
# scripts/ci/ios-sim/backend.sh start <backend zip> <work dir> writes the directory's short address to <work dir>/directory-link,
|
||||
# the names server's address to <work dir>/names-server
|
||||
# and what was seeded to <work dir>/seed.md
|
||||
# scripts/ci/ios-sim/backend.sh resume <backend zip> <work dir> starts again, on another runner, what start left stopped in <work dir>,
|
||||
# with the same addresses
|
||||
# scripts/ci/ios-sim/backend.sh stop <work dir> stops everything it started, prints the logs
|
||||
# scripts/ci/ios-sim/backend.sh directory-stop <work dir> stops the directory only
|
||||
# scripts/ci/ios-sim/backend.sh directory-start <work dir> starts it again
|
||||
@@ -83,9 +85,13 @@ listings_query="SELECT gp.display_name AS name, gp.short_descr AS description, g
|
||||
WHERE r.group_reg_status = 'active'
|
||||
ORDER BY g.summary_current_members_count DESC, r.group_id"
|
||||
|
||||
start_relay() {
|
||||
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null > "$work/relay.log" 2>&1 &
|
||||
run_relay() {
|
||||
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null >> "$work/relay.log" 2>&1 &
|
||||
echo $! > "$work/relay.pid"
|
||||
}
|
||||
|
||||
start_relay() {
|
||||
run_relay
|
||||
local deadline=$((SECONDS + 120))
|
||||
until grep -qE '^https://[^ ]+/r#' "$work/relay.log"; do
|
||||
if ! kill -0 "$(cat "$work/relay.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
||||
@@ -110,17 +116,21 @@ create_directory() {
|
||||
}
|
||||
|
||||
start_directory() {
|
||||
local pid deadline=$((SECONDS + 60))
|
||||
# -y: the service adds its own tables to the database the CLI created, and would otherwise ask on stdin
|
||||
nohup "$bin/simplex-directory-service" -d "$work/directory" -y --super-users "$1" < /dev/null >> "$work/directory.log" 2>&1 &
|
||||
pid=$!
|
||||
echo "$pid" > "$work/directory.pid"
|
||||
echo $! > "$work/directory.pid"
|
||||
echo "$1" > "$work/super-user"
|
||||
# its output is block-buffered, so it shows that its chat has started by connecting to its servers
|
||||
wait_connected directory
|
||||
}
|
||||
|
||||
# A chat process shows that its chat has started by connecting to its servers: its output is block-buffered.
|
||||
wait_connected() {
|
||||
local pid deadline=$((SECONDS + 60))
|
||||
pid=$(cat "$work/$1.pid")
|
||||
until lsof -a -p "$pid" -iTCP -sTCP:ESTABLISHED > /dev/null 2>&1; do
|
||||
if ! kill -0 "$pid" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
||||
echo "error: the directory service did not start"
|
||||
cat "$work/directory.log"
|
||||
echo "error: $1 did not start"
|
||||
cat "$work/$1.log"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
@@ -253,8 +263,6 @@ seed_avatars() {
|
||||
# A local SMP server whose [NAMES] section points at names-resolver.py, which answers from names.json.
|
||||
start_names() {
|
||||
echo "{}" > "$work/names.json"
|
||||
nohup python3 "$(dirname "$0")/names-resolver.py" "$names_resolver_port" "$work/names.json" < /dev/null > "$work/names-resolver.log" 2>&1 &
|
||||
echo $! > "$work/names-resolver.pid"
|
||||
export SMP_SERVER_CFG_PATH=$work/smp-server/etc SMP_SERVER_LOG_PATH=$work/smp-server/log
|
||||
"$bin/smp-server" init -y --disable-store-log --no-password --fqdn localhost --disable-web > "$work/out" 2>&1 || {
|
||||
cat "$work/out"
|
||||
@@ -268,7 +276,16 @@ start_names() {
|
||||
-e 's/^port = 5223,443$/port = 5223/' \
|
||||
"$SMP_SERVER_CFG_PATH/smp-server.ini"
|
||||
grep -E '^(enable: on|resolver_endpoint|port = )' "$SMP_SERVER_CFG_PATH/smp-server.ini"
|
||||
nohup "$bin/smp-server" start < /dev/null > "$work/smp-server.log" 2>&1 &
|
||||
run_names
|
||||
echo "$names_server" > "$work/names-server"
|
||||
echo "names server: $names_server"
|
||||
}
|
||||
|
||||
run_names() {
|
||||
nohup python3 "$(dirname "$0")/names-resolver.py" "$names_resolver_port" "$work/names.json" < /dev/null >> "$work/names-resolver.log" 2>&1 &
|
||||
echo $! > "$work/names-resolver.pid"
|
||||
export SMP_SERVER_CFG_PATH=$work/smp-server/etc SMP_SERVER_LOG_PATH=$work/smp-server/log
|
||||
nohup "$bin/smp-server" start < /dev/null >> "$work/smp-server.log" 2>&1 &
|
||||
echo $! > "$work/smp-server.pid"
|
||||
local deadline=$((SECONDS + 60))
|
||||
until nc -z 127.0.0.1 5223 2> /dev/null; do
|
||||
@@ -279,8 +296,6 @@ start_names() {
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "$names_server" > "$work/names-server"
|
||||
echo "names server: $names_server"
|
||||
}
|
||||
|
||||
register_name() {
|
||||
@@ -302,8 +317,13 @@ start_contact() {
|
||||
register_name "$name" "$address" ""
|
||||
named "$db" "$display_name" 5 "/_set domain 1 $name"
|
||||
expect_out "^new contact address set$"
|
||||
run_contact "$db" "$display_name"
|
||||
}
|
||||
|
||||
run_contact() {
|
||||
local db=$1 display_name=$2
|
||||
# --headless is for relays only; a command with a long -t keeps the client online, accepting contacts, for 6 hours
|
||||
nohup "$bin/simplex-chat" -d "$work/$db" -e "/auto_accept on" -t 21600 --user-display-name "$display_name" < /dev/null > "$work/$db.log" 2>&1 &
|
||||
nohup "$bin/simplex-chat" -d "$work/$db" -e "/auto_accept on" -t 21600 --user-display-name "$display_name" < /dev/null >> "$work/$db.log" 2>&1 &
|
||||
echo $! > "$work/$db.pid"
|
||||
}
|
||||
|
||||
@@ -494,6 +514,20 @@ start() {
|
||||
probe_pages "$avatar_search_text" 6
|
||||
}
|
||||
|
||||
# The servers' keys and the clients' databases stay in the work dir, so its addresses are the same again.
|
||||
resume() {
|
||||
bin=$work/bin
|
||||
mkdir -p "$bin"
|
||||
unzip -o -q "$1" -d "$bin"
|
||||
run_names
|
||||
run_relay
|
||||
wait_connected relay
|
||||
start_directory "$(cat "$work/super-user")"
|
||||
run_contact ci_alice cialice
|
||||
run_contact ci_robert Robert
|
||||
echo "resumed: directory $(cat "$work/directory-link"), names server $(cat "$work/names-server")"
|
||||
}
|
||||
|
||||
stop() {
|
||||
local p processes="directory relay ci_alice ci_robert smp-server"
|
||||
for p in $processes; do
|
||||
@@ -518,6 +552,10 @@ case "${1:-}" in
|
||||
mkdir -p "$work"
|
||||
start "$2"
|
||||
;;
|
||||
resume)
|
||||
work=$3
|
||||
resume "$2"
|
||||
;;
|
||||
stop)
|
||||
work=$2
|
||||
stop
|
||||
@@ -536,7 +574,7 @@ case "${1:-}" in
|
||||
sqlite3 -markdown "$work/directory_chat.db" "$listings_query"
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 start <backend zip> <work dir> | stop <work dir> | directory-stop <work dir> | directory-start <work dir> | listings <work dir>"
|
||||
echo "usage: $0 start <backend zip> <work dir> | resume <backend zip> <work dir> | stop <work dir> | directory-stop <work dir> | directory-start <work dir> | listings <work dir>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user