claudecord 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claudecord-0.2.1/.dockerignore +7 -0
- claudecord-0.2.1/.env.example +22 -0
- claudecord-0.2.1/.github/CODEOWNERS +1 -0
- claudecord-0.2.1/.github/ISSUE_TEMPLATE/bug_report.yml +41 -0
- claudecord-0.2.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- claudecord-0.2.1/.github/ISSUE_TEMPLATE/feature_request.yml +19 -0
- claudecord-0.2.1/.github/dependabot.yml +10 -0
- claudecord-0.2.1/.github/pull_request_template.md +23 -0
- claudecord-0.2.1/.github/workflows/deploy.yml +45 -0
- claudecord-0.2.1/.github/workflows/docker.yml +26 -0
- claudecord-0.2.1/.github/workflows/load.yml +25 -0
- claudecord-0.2.1/.github/workflows/pages.yml +28 -0
- claudecord-0.2.1/.github/workflows/pr-tests.yml +36 -0
- claudecord-0.2.1/.github/workflows/race.yml +47 -0
- claudecord-0.2.1/.github/workflows/release.yml +172 -0
- claudecord-0.2.1/.github/workflows/rust.yml +84 -0
- claudecord-0.2.1/.github/workflows/uptime.yml +23 -0
- claudecord-0.2.1/.github/workflows/web.yml +27 -0
- claudecord-0.2.1/.gitignore +19 -0
- claudecord-0.2.1/CODEMAP.md +219 -0
- claudecord-0.2.1/CONTRIBUTING.md +40 -0
- claudecord-0.2.1/Cargo.lock +2855 -0
- claudecord-0.2.1/Cargo.toml +38 -0
- claudecord-0.2.1/Dockerfile +36 -0
- claudecord-0.2.1/PKG-INFO +271 -0
- claudecord-0.2.1/README.md +256 -0
- claudecord-0.2.1/SECURITY.md +20 -0
- claudecord-0.2.1/deploy/baremetal/Caddyfile +6 -0
- claudecord-0.2.1/deploy/baremetal/bootstrap.sh +127 -0
- claudecord-0.2.1/deploy/baremetal/claudecord-hub.service +31 -0
- claudecord-0.2.1/deploy/compose.yml +43 -0
- claudecord-0.2.1/dist/claudecord-0.2.1-py3-none-macosx_11_0_arm64.whl +0 -0
- claudecord-0.2.1/examples/briefs.rs +30 -0
- claudecord-0.2.1/examples/replay.rs +251 -0
- claudecord-0.2.1/init.md +77 -0
- claudecord-0.2.1/npm/claudecord/bin/claudecord.js +40 -0
- claudecord-0.2.1/npm/claudecord/package.json +11 -0
- claudecord-0.2.1/pyproject.toml +27 -0
- claudecord-0.2.1/scripts/check.sh +37 -0
- claudecord-0.2.1/scripts/codemap.py +56 -0
- claudecord-0.2.1/scripts/cost/baseline.json +49 -0
- claudecord-0.2.1/scripts/cost/benchmark.py +82 -0
- claudecord-0.2.1/scripts/cost/coalescing.py +37 -0
- claudecord-0.2.1/scripts/cost/overhead.py +11 -0
- claudecord-0.2.1/scripts/cost/team_benchmark.py +87 -0
- claudecord-0.2.1/scripts/load/hub.js +89 -0
- claudecord-0.2.1/scripts/load/profile.sh +32 -0
- claudecord-0.2.1/scripts/load/smoke.sh +13 -0
- claudecord-0.2.1/scripts/package_npm.py +63 -0
- claudecord-0.2.1/scripts/publish_npm.sh +12 -0
- claudecord-0.2.1/scripts/release.sh +28 -0
- claudecord-0.2.1/scripts/smoke.sh +41 -0
- claudecord-0.2.1/scripts/test_packaging.py +105 -0
- claudecord-0.2.1/scripts/tests_gate.py +53 -0
- claudecord-0.2.1/scripts/tsan_filter.py +52 -0
- claudecord-0.2.1/site/index.html +40 -0
- claudecord-0.2.1/src/agents/adapters.rs +322 -0
- claudecord-0.2.1/src/agents/mod.rs +5 -0
- claudecord-0.2.1/src/agents/text.rs +126 -0
- claudecord-0.2.1/src/cli/discord.rs +220 -0
- claudecord-0.2.1/src/cli/export.rs +55 -0
- claudecord-0.2.1/src/cli/hub.rs +428 -0
- claudecord-0.2.1/src/cli/machine.rs +651 -0
- claudecord-0.2.1/src/cli/mod.rs +189 -0
- claudecord-0.2.1/src/cli/storage.rs +209 -0
- claudecord-0.2.1/src/cli/uptime.rs +135 -0
- claudecord-0.2.1/src/cli/verbs.rs +114 -0
- claudecord-0.2.1/src/control/mod.rs +528 -0
- claudecord-0.2.1/src/control/registry.rs +258 -0
- claudecord-0.2.1/src/control/seal.rs +252 -0
- claudecord-0.2.1/src/device/config.rs +56 -0
- claudecord-0.2.1/src/device/daemon.rs +1493 -0
- claudecord-0.2.1/src/device/doctor.rs +105 -0
- claudecord-0.2.1/src/device/enroll.rs +157 -0
- claudecord-0.2.1/src/device/inject.rs +120 -0
- claudecord-0.2.1/src/device/ipc.rs +209 -0
- claudecord-0.2.1/src/device/link.rs +439 -0
- claudecord-0.2.1/src/device/logs.rs +179 -0
- claudecord-0.2.1/src/device/mod.rs +27 -0
- claudecord-0.2.1/src/device/pty.rs +173 -0
- claudecord-0.2.1/src/device/terminal.rs +194 -0
- claudecord-0.2.1/src/device/tmux.rs +361 -0
- claudecord-0.2.1/src/discord/api.rs +371 -0
- claudecord-0.2.1/src/discord/bridge.rs +862 -0
- claudecord-0.2.1/src/discord/commands.rs +363 -0
- claudecord-0.2.1/src/discord/fake.rs +340 -0
- claudecord-0.2.1/src/discord/gateway.rs +187 -0
- claudecord-0.2.1/src/discord/mod.rs +14 -0
- claudecord-0.2.1/src/discord/perms.rs +65 -0
- claudecord-0.2.1/src/export/mod.rs +64 -0
- claudecord-0.2.1/src/export/obsidian.rs +571 -0
- claudecord-0.2.1/src/health/mod.rs +97 -0
- claudecord-0.2.1/src/health/probes_core.rs +879 -0
- claudecord-0.2.1/src/health/probes_hosted.rs +167 -0
- claudecord-0.2.1/src/health/probes_live.rs +1239 -0
- claudecord-0.2.1/src/hub/access.rs +74 -0
- claudecord-0.2.1/src/hub/asks.rs +213 -0
- claudecord-0.2.1/src/hub/briefs.rs +35 -0
- claudecord-0.2.1/src/hub/controls.rs +242 -0
- claudecord-0.2.1/src/hub/core.rs +924 -0
- claudecord-0.2.1/src/hub/effects.rs +148 -0
- claudecord-0.2.1/src/hub/expiry.rs +109 -0
- claudecord-0.2.1/src/hub/files.rs +217 -0
- claudecord-0.2.1/src/hub/handoff.rs +368 -0
- claudecord-0.2.1/src/hub/mod.rs +42 -0
- claudecord-0.2.1/src/hub/model.rs +275 -0
- claudecord-0.2.1/src/hub/permissions.rs +356 -0
- claudecord-0.2.1/src/hub/routing.rs +482 -0
- claudecord-0.2.1/src/hub/snapshot.rs +346 -0
- claudecord-0.2.1/src/hub/tasks.rs +291 -0
- claudecord-0.2.1/src/hub/tracked.rs +230 -0
- claudecord-0.2.1/src/lib.rs +55 -0
- claudecord-0.2.1/src/log.rs +162 -0
- claudecord-0.2.1/src/main.rs +14 -0
- claudecord-0.2.1/src/metrics/events.rs +345 -0
- claudecord-0.2.1/src/metrics/mod.rs +301 -0
- claudecord-0.2.1/src/notify.rs +48 -0
- claudecord-0.2.1/src/protocol/mod.rs +571 -0
- claudecord-0.2.1/src/security/codes.rs +16 -0
- claudecord-0.2.1/src/security/env.rs +67 -0
- claudecord-0.2.1/src/security/limits.rs +82 -0
- claudecord-0.2.1/src/security/mod.rs +6 -0
- claudecord-0.2.1/src/security/redact.rs +165 -0
- claudecord-0.2.1/src/server/actor.rs +392 -0
- claudecord-0.2.1/src/server/app.rs +42 -0
- claudecord-0.2.1/src/server/bots.rs +473 -0
- claudecord-0.2.1/src/server/demo.rs +232 -0
- claudecord-0.2.1/src/server/disk.rs +175 -0
- claudecord-0.2.1/src/server/gateway.rs +788 -0
- claudecord-0.2.1/src/server/health.rs +303 -0
- claudecord-0.2.1/src/server/login.rs +304 -0
- claudecord-0.2.1/src/server/mod.rs +571 -0
- claudecord-0.2.1/src/server/session.rs +137 -0
- claudecord-0.2.1/src/server/web/app.css +60 -0
- claudecord-0.2.1/src/server/web/app.js +193 -0
- claudecord-0.2.1/src/server/web/index.html +61 -0
- claudecord-0.2.1/src/server/web.rs +249 -0
- claudecord-0.2.1/src/store/bucket.rs +232 -0
- claudecord-0.2.1/src/store/mod.rs +812 -0
- claudecord-0.2.1/src/store/uptime.rs +58 -0
- claudecord-0.2.1/src/sync.rs +17 -0
- claudecord-0.2.1/src/task.rs +110 -0
- claudecord-0.2.1/src/uptime/mod.rs +275 -0
- claudecord-0.2.1/testdata/conformance/adapter_argv.json +158 -0
- claudecord-0.2.1/testdata/conformance/adapter_detect.json +525 -0
- claudecord-0.2.1/testdata/conformance/agent_spec.json +198 -0
- claudecord-0.2.1/testdata/conformance/app_id.json +34 -0
- claudecord-0.2.1/testdata/conformance/bucket.json +62 -0
- claudecord-0.2.1/testdata/conformance/detect_limit.json +80 -0
- claudecord-0.2.1/testdata/conformance/env_scrub.json +66 -0
- claudecord-0.2.1/testdata/conformance/failure_limiter.json +56 -0
- claudecord-0.2.1/testdata/conformance/file_secrets.json +72 -0
- claudecord-0.2.1/testdata/conformance/format_deliveries.json +39 -0
- claudecord-0.2.1/testdata/conformance/hub_frame_spawn_strips_cwd.json +21 -0
- claudecord-0.2.1/testdata/conformance/hub_frames.json +130 -0
- claudecord-0.2.1/testdata/conformance/junk_frames.json +32 -0
- claudecord-0.2.1/testdata/conformance/metrics.json +227 -0
- claudecord-0.2.1/testdata/conformance/node_frames.json +638 -0
- claudecord-0.2.1/testdata/conformance/pair_code_format.json +27 -0
- claudecord-0.2.1/testdata/conformance/parse_menu.json +99 -0
- claudecord-0.2.1/testdata/conformance/permissions.json +3 -0
- claudecord-0.2.1/testdata/conformance/project_slug.json +46 -0
- claudecord-0.2.1/testdata/conformance/quote_body.json +22 -0
- claudecord-0.2.1/testdata/conformance/redact.json +199 -0
- claudecord-0.2.1/testdata/conformance/safe_name.json +36 -0
- claudecord-0.2.1/testdata/conformance/sensitive_path.json +114 -0
- claudecord-0.2.1/testdata/conformance/slug.json +74 -0
- claudecord-0.2.1/testdata/conformance/startup_choice.json +32 -0
- claudecord-0.2.1/testdata/conformance/strip_control.json +54 -0
- claudecord-0.2.1/tests/it/bucket.rs +405 -0
- claudecord-0.2.1/tests/it/conformance.rs +400 -0
- claudecord-0.2.1/tests/it/device_link.rs +344 -0
- claudecord-0.2.1/tests/it/device_pty.rs +190 -0
- claudecord-0.2.1/tests/it/device_tmux.rs +106 -0
- claudecord-0.2.1/tests/it/discord_bridge.rs +823 -0
- claudecord-0.2.1/tests/it/durability.rs +289 -0
- claudecord-0.2.1/tests/it/e2e.rs +1250 -0
- claudecord-0.2.1/tests/it/edge.rs +344 -0
- claudecord-0.2.1/tests/it/export.rs +573 -0
- claudecord-0.2.1/tests/it/gateway.rs +881 -0
- claudecord-0.2.1/tests/it/health.rs +60 -0
- claudecord-0.2.1/tests/it/hub.rs +2020 -0
- claudecord-0.2.1/tests/it/logs.rs +79 -0
- claudecord-0.2.1/tests/it/main.rs +27 -0
- claudecord-0.2.1/tests/it/races.rs +311 -0
- claudecord-0.2.1/tests/it/resilience.rs +359 -0
- claudecord-0.2.1/tests/it/robust.rs +484 -0
- claudecord-0.2.1/tests/it/server.rs +1145 -0
- claudecord-0.2.1/tests/it/store.rs +244 -0
- claudecord-0.2.1/tests/it/tiering.rs +227 -0
- claudecord-0.2.1/tests/it/token_budget.rs +285 -0
- claudecord-0.2.1/tests/it/uptime.rs +342 -0
- claudecord-0.2.1/tests/it/web_login.rs +528 -0
- claudecord-0.2.1/web/.gitignore +1 -0
- claudecord-0.2.1/web/dist/app.css +2 -0
- claudecord-0.2.1/web/dist/app.js +11 -0
- claudecord-0.2.1/web/dist/index.html +14 -0
- claudecord-0.2.1/web/index.html +13 -0
- claudecord-0.2.1/web/package-lock.json +1403 -0
- claudecord-0.2.1/web/package.json +25 -0
- claudecord-0.2.1/web/src/App.tsx +68 -0
- claudecord-0.2.1/web/src/api.ts +93 -0
- claudecord-0.2.1/web/src/hooks.ts +54 -0
- claudecord-0.2.1/web/src/main.tsx +13 -0
- claudecord-0.2.1/web/src/pages/Activate.tsx +93 -0
- claudecord-0.2.1/web/src/pages/Bots.tsx +46 -0
- claudecord-0.2.1/web/src/pages/Insights.tsx +250 -0
- claudecord-0.2.1/web/src/pages/Login.tsx +21 -0
- claudecord-0.2.1/web/src/pages/Machines.tsx +37 -0
- claudecord-0.2.1/web/src/pages/Overview.tsx +149 -0
- claudecord-0.2.1/web/src/pages/Setup.tsx +291 -0
- claudecord-0.2.1/web/src/styles.css +63 -0
- claudecord-0.2.1/web/tsconfig.json +17 -0
- claudecord-0.2.1/web/vite.config.ts +24 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Optional settings. Copy to .env if you like; claudecord does not read .env itself, so export these in your shell or put them in
|
|
2
|
+
# the systemd unit. NONE of these are secrets: the Discord token, the OAuth secret and bucket keys are never environment
|
|
3
|
+
# variables. They live in files in the hub's data folder (mode 0600), written by `claudecord discord set` and
|
|
4
|
+
# `claudecord storage ...`.
|
|
5
|
+
|
|
6
|
+
# How much is logged: error, warn, info (default) or debug.
|
|
7
|
+
# CLAUDECORD_LOG=info
|
|
8
|
+
|
|
9
|
+
# Where a machine keeps its login, daemon socket and per-agent logs (default: ~/.claudecord).
|
|
10
|
+
# CLAUDECORD_HOME=
|
|
11
|
+
|
|
12
|
+
# Machine placement: how many agents this machine should run at once, and labels such as gpu.
|
|
13
|
+
# CLAUDECORD_MAX_AGENTS=8
|
|
14
|
+
# CLAUDECORD_LABELS=gpu,fast
|
|
15
|
+
|
|
16
|
+
# How agents run: tmux (default where installed) or pty (the built-in terminal, used on native Windows).
|
|
17
|
+
# CLAUDECORD_TERMINAL=tmux
|
|
18
|
+
# The name of the private tmux server claudecord uses, so it never touches your own tmux.
|
|
19
|
+
# CLAUDECORD_TMUX_SOCKET=claudecord
|
|
20
|
+
|
|
21
|
+
# npm package only: run this program instead of the platform package's (for development).
|
|
22
|
+
# CLAUDECORD_BINARY=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @kanavdhanda
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something does not work
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened
|
|
9
|
+
description: And what you expected instead.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: repro
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to reproduce
|
|
16
|
+
placeholder: "1. Run ...\n2. Then ...\n3. See ..."
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: input
|
|
20
|
+
id: version
|
|
21
|
+
attributes:
|
|
22
|
+
label: Version
|
|
23
|
+
description: Output of `npx claudecord --version`, or the commit.
|
|
24
|
+
- type: dropdown
|
|
25
|
+
id: os
|
|
26
|
+
attributes:
|
|
27
|
+
label: Operating system
|
|
28
|
+
options: [macOS, Linux, Windows (WSL2), Windows (native)]
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
- type: dropdown
|
|
32
|
+
id: agent
|
|
33
|
+
attributes:
|
|
34
|
+
label: Agent
|
|
35
|
+
options: [Claude Code, agy, Codex, Not agent specific]
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: logs
|
|
38
|
+
attributes:
|
|
39
|
+
label: Logs
|
|
40
|
+
description: "Remove anything secret first. The daemon log is in ~/.claude-mesh/daemon.log."
|
|
41
|
+
render: shell
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What are you trying to do
|
|
9
|
+
description: The problem first, before the solution.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: idea
|
|
14
|
+
attributes:
|
|
15
|
+
label: What would you like to happen
|
|
16
|
+
- type: textarea
|
|
17
|
+
id: alternatives
|
|
18
|
+
attributes:
|
|
19
|
+
label: What have you tried or considered
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Opens pull requests for new versions of the Rust crates and the GitHub actions. Each one runs the full CI, so an update
|
|
2
|
+
# only merges when every test, the self-check and the smoke test still pass.
|
|
3
|
+
version: 2
|
|
4
|
+
updates:
|
|
5
|
+
- package-ecosystem: cargo
|
|
6
|
+
directory: /
|
|
7
|
+
schedule: { interval: weekly }
|
|
8
|
+
- package-ecosystem: github-actions
|
|
9
|
+
directory: /
|
|
10
|
+
schedule: { interval: weekly }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. -->
|
|
4
|
+
|
|
5
|
+
## Tests
|
|
6
|
+
|
|
7
|
+
<!-- Which test fails without this change? (The test bot comments if none was added.) -->
|
|
8
|
+
|
|
9
|
+
## Cost impact
|
|
10
|
+
|
|
11
|
+
<!-- Does this change how many agent turns or tokens a message costs? Write "none" if it does not touch delivery, briefs or
|
|
12
|
+
anything an agent reads, and say why. If it does, say what changes (more turns? a longer standing brief?) and show the
|
|
13
|
+
measurement (scripts/cost/ has the benchmarks; they spend real tokens, so run them yourself, CI does not). -->
|
|
14
|
+
|
|
15
|
+
Cost impact:
|
|
16
|
+
|
|
17
|
+
## Scale impact
|
|
18
|
+
|
|
19
|
+
<!-- Does this change how many users, machines or agents one hub can carry? Write "none" if not, and say why. If it adds work
|
|
20
|
+
per connection, per message or per agent (memory held, database writes, timers), say how much, and run
|
|
21
|
+
scripts/load/smoke.sh (or the load.yml workflow) to show the numbers. -->
|
|
22
|
+
|
|
23
|
+
Scale impact:
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: deploy
|
|
2
|
+
|
|
3
|
+
# Updates the hub on your server and checks it is ready. The server builds the image itself from a clone of this repository (nothing is
|
|
4
|
+
# published to a registry). If the new version does not become ready, the one that was running is put back. Run it by hand
|
|
5
|
+
# (Actions > deploy > Run workflow) with a git ref: a release tag such as v0.2.1, a branch, or a commit.
|
|
6
|
+
# Set up once: secrets DEPLOY_HOST, DEPLOY_USER, DEPLOY_SSH_KEY, DEPLOY_KNOWN_HOSTS; variables HUB_URL (https://claudecord.kdhanda.com) and
|
|
7
|
+
# DEPLOY_DIR (the clone of the repository on the server, such as /opt/claudeCord), with deploy/compose.yml already set up there.
|
|
8
|
+
on:
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
ref:
|
|
12
|
+
description: Git ref to run (v0.2.1, main, or a commit)
|
|
13
|
+
required: true
|
|
14
|
+
default: main
|
|
15
|
+
|
|
16
|
+
concurrency: deploy
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
deploy:
|
|
20
|
+
if: vars.DEPLOY_DIR != ''
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
environment: production
|
|
23
|
+
steps:
|
|
24
|
+
- name: Trust only the server we pinned
|
|
25
|
+
run: |
|
|
26
|
+
mkdir -p ~/.ssh
|
|
27
|
+
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id; chmod 600 ~/.ssh/id
|
|
28
|
+
echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
|
29
|
+
- name: Build on the server, restart, check, roll back on failure
|
|
30
|
+
env:
|
|
31
|
+
REF: ${{ inputs.ref }}
|
|
32
|
+
DIR: ${{ vars.DEPLOY_DIR }}
|
|
33
|
+
run: |
|
|
34
|
+
ssh_ () { ssh -i ~/.ssh/id -o StrictHostKeyChecking=yes "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" "$@"; }
|
|
35
|
+
# The running image is kept as claudecord:previous, and the commit it came from in .previous, so a failed deploy can put it back.
|
|
36
|
+
ssh_ "set -e; cd '$DIR' && git rev-parse HEAD > .previous && { docker tag claudecord:local claudecord:previous 2>/dev/null || true; } \
|
|
37
|
+
&& git fetch --tags origin && git checkout --detach '$REF' && cd deploy && docker compose up -d --build"
|
|
38
|
+
for i in $(seq 1 20); do
|
|
39
|
+
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "${{ vars.HUB_URL }}/readyz" || true)
|
|
40
|
+
[ "$code" = "200" ] && { echo "ready after $i checks"; exit 0; }
|
|
41
|
+
sleep 3
|
|
42
|
+
done
|
|
43
|
+
echo "the new version never became ready, putting the previous one back"
|
|
44
|
+
ssh_ "set -e; cd '$DIR' && git checkout --detach \$(cat .previous) && docker tag claudecord:previous claudecord:local && cd deploy && docker compose up -d --no-build"
|
|
45
|
+
exit 1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: docker
|
|
2
|
+
|
|
3
|
+
# Checks that the hub's container image still builds. Nothing is published anywhere: the image is built on the server itself
|
|
4
|
+
# (deploy/compose.yml), so a change to the Dockerfile or to what goes into it is caught here and not on the server.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
paths: [Dockerfile, .dockerignore, Cargo.toml, Cargo.lock, "src/**", "web/dist/**", "examples/**", .github/workflows/docker.yml]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
image:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: docker/setup-buildx-action@v3
|
|
21
|
+
- uses: docker/build-push-action@v6
|
|
22
|
+
with:
|
|
23
|
+
context: .
|
|
24
|
+
push: false
|
|
25
|
+
cache-from: type=gha
|
|
26
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: load
|
|
2
|
+
|
|
3
|
+
# Runs the k6 load test (users with three bots each, all talking at once) against a hub limited to the size of a free
|
|
4
|
+
# server. Run it by hand from the Actions tab; it is never run on a push because it takes a long time.
|
|
5
|
+
on:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
users:
|
|
9
|
+
description: Simulated users (each has three bots)
|
|
10
|
+
default: "10000"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
load:
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
profile: [oracle-arm, small-vps, oracle-micro]
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
# Long and heavy, so each size waits for your approval (Settings > Environments > load-test > Required reviewers).
|
|
20
|
+
environment: load-test
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: grafana/setup-k6-action@v1
|
|
24
|
+
- run: docker build -t claudecord .
|
|
25
|
+
- run: scripts/load/profile.sh ${{ matrix.profile }} ${{ inputs.users }} 60
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: pages
|
|
2
|
+
|
|
3
|
+
# Publishes the one-page site in site/ to GitHub Pages (free). Turn it on once: Settings > Pages > Source: GitHub Actions.
|
|
4
|
+
# The site is plain HTML with no scripts, no cookies and no analytics.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
paths: ["site/**"]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment:
|
|
20
|
+
name: github-pages
|
|
21
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/upload-pages-artifact@v3
|
|
25
|
+
with:
|
|
26
|
+
path: site
|
|
27
|
+
- id: deployment
|
|
28
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: pr-tests
|
|
2
|
+
|
|
3
|
+
# A bot that reads each pull request and comments when source changed without a test. It keeps one comment up to date
|
|
4
|
+
# instead of adding a new one each push, and fails the check so it is not missed.
|
|
5
|
+
on:
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
tests-came-with-the-change:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
- run: python3 scripts/tests_gate.py --self-check
|
|
20
|
+
- id: gate
|
|
21
|
+
run: |
|
|
22
|
+
set +e
|
|
23
|
+
python3 scripts/tests_gate.py "origin/${{ github.base_ref }}" > verdict.md
|
|
24
|
+
echo "status=$?" >> "$GITHUB_OUTPUT"
|
|
25
|
+
- uses: actions/github-script@v7
|
|
26
|
+
with:
|
|
27
|
+
script: |
|
|
28
|
+
const body = '<!-- tests-gate -->\n' + require('fs').readFileSync('verdict.md', 'utf8');
|
|
29
|
+
const { owner, repo } = context.repo;
|
|
30
|
+
const issue_number = context.issue.number;
|
|
31
|
+
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number });
|
|
32
|
+
const mine = comments.find((c) => c.body.startsWith('<!-- tests-gate -->'));
|
|
33
|
+
if (mine) await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body });
|
|
34
|
+
else await github.rest.issues.createComment({ owner, repo, issue_number, body });
|
|
35
|
+
- if: steps.gate.outputs.status != '0'
|
|
36
|
+
run: exit 1
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: race
|
|
2
|
+
|
|
3
|
+
# Looks for timing bugs: things that pass most of the time and fail when two actions land together.
|
|
4
|
+
# stress runs every test many times, with very few and very many threads, so order-dependent tests show themselves.
|
|
5
|
+
# sanitizer runs the concurrency tests under ThreadSanitizer, which reports data races in the program and its dependencies.
|
|
6
|
+
# The stress run happens every night; the sanitizer is slow, so it waits for your approval
|
|
7
|
+
# (Settings > Environments > sanitizers > Required reviewers) and is started by hand.
|
|
8
|
+
on:
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: "17 3 * * *"
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
stress:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
19
|
+
- uses: Swatinem/rust-cache@v2
|
|
20
|
+
- run: cargo test --no-run --release
|
|
21
|
+
- run: |
|
|
22
|
+
for i in 1 2 3; do
|
|
23
|
+
cargo test --release -- --test-threads=1 || exit 1
|
|
24
|
+
cargo test --release -- --test-threads=32 || exit 1
|
|
25
|
+
done
|
|
26
|
+
- run: 'for i in $(seq 50); do cargo test --release --test it races:: -q || exit 1; done'
|
|
27
|
+
|
|
28
|
+
sanitizer:
|
|
29
|
+
if: github.event_name == 'workflow_dispatch'
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment: sanitizers
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: dtolnay/rust-toolchain@nightly
|
|
35
|
+
with:
|
|
36
|
+
components: rust-src
|
|
37
|
+
# Only the concurrency tests that do not depend on timing run here: under the sanitizer everything is 10 to 20 times slower, which makes
|
|
38
|
+
# timeouts in the socket tests fail for no reason that matters.
|
|
39
|
+
# ThreadSanitizer cannot see through tokio and the bundled SQLite, so it reports races inside them. The filter fails the job only if a
|
|
40
|
+
# racing access is in claudeCord's own code, or a test fails (see scripts/tsan_filter.py).
|
|
41
|
+
- run: |
|
|
42
|
+
set +e
|
|
43
|
+
cargo test -Zbuild-std --target x86_64-unknown-linux-gnu --test it -- races:: durability:: edge:: tiering:: hub:: 2>&1 | tee tsan.log
|
|
44
|
+
python3 scripts/tsan_filter.py tsan.log
|
|
45
|
+
env:
|
|
46
|
+
RUSTFLAGS: -Zsanitizer=thread
|
|
47
|
+
RUSTDOCFLAGS: -Zsanitizer=thread
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Push a tag such as v0.2.0 to release. One tag builds the program for every platform and makes, from those same builds:
|
|
4
|
+
# - files on a draft GitHub release,
|
|
5
|
+
# - pip wheels (PyPI),
|
|
6
|
+
# - npm packages (one per platform, plus the main `claudecord` package).
|
|
7
|
+
# Publishing to PyPI and npm only happens when it is set up: PyPI uses trusted publishing (no secret to keep), npm uses the NPM_TOKEN
|
|
8
|
+
# repository secret (a granular token with read and write on all packages and "bypass two-factor" on, because no one is there to answer a passkey).
|
|
9
|
+
# Ship with scripts/release.sh X.Y.Z, which bumps the version, tags it and pushes; this file does the rest. Without them the release still builds and the draft release holds every file.
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags: ["v*"]
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
version:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
# A tag that does not match Cargo.toml would publish packages with the wrong number, so stop here.
|
|
24
|
+
- run: |
|
|
25
|
+
v=$(sed -n 's/^version *= *"\(.*\)"/\1/p' Cargo.toml | head -1)
|
|
26
|
+
if [ "${GITHUB_REF_TYPE}" = "tag" ] && [ "${GITHUB_REF_NAME}" != "v$v" ]; then
|
|
27
|
+
echo "tag ${GITHUB_REF_NAME} does not match Cargo.toml version $v"; exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Before anything is published, the packages are installed into clean folders and run on every platform.
|
|
31
|
+
verify-packaging:
|
|
32
|
+
needs: version
|
|
33
|
+
strategy:
|
|
34
|
+
fail-fast: false
|
|
35
|
+
matrix:
|
|
36
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
37
|
+
runs-on: ${{ matrix.os }}
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
41
|
+
- uses: Swatinem/rust-cache@v2
|
|
42
|
+
- uses: astral-sh/setup-uv@v7
|
|
43
|
+
- uses: actions/setup-node@v4
|
|
44
|
+
with:
|
|
45
|
+
node-version: 22
|
|
46
|
+
- run: python3 scripts/test_packaging.py
|
|
47
|
+
|
|
48
|
+
build:
|
|
49
|
+
needs: version
|
|
50
|
+
strategy:
|
|
51
|
+
fail-fast: false
|
|
52
|
+
matrix:
|
|
53
|
+
include:
|
|
54
|
+
- { os: ubuntu-latest, target: x86_64-unknown-linux-musl, name: linux-x64, ext: "" }
|
|
55
|
+
- { os: ubuntu-24.04-arm, target: aarch64-unknown-linux-musl, name: linux-arm64, ext: "" }
|
|
56
|
+
- { os: macos-latest, target: aarch64-apple-darwin, name: macos-arm64, ext: "" }
|
|
57
|
+
- { os: macos-15-intel, target: x86_64-apple-darwin, name: macos-x64, ext: "" }
|
|
58
|
+
- { os: windows-latest, target: x86_64-pc-windows-msvc, name: windows-x64, ext: ".exe" }
|
|
59
|
+
runs-on: ${{ matrix.os }}
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
63
|
+
with:
|
|
64
|
+
targets: ${{ matrix.target }}
|
|
65
|
+
- run: sudo apt-get update && sudo apt-get install -y musl-tools
|
|
66
|
+
if: contains(matrix.target, 'musl')
|
|
67
|
+
- run: cargo build --release --target ${{ matrix.target }} --bin claudecord
|
|
68
|
+
env:
|
|
69
|
+
# The address a downloaded program goes to when nothing else is said (src/device/enroll.rs).
|
|
70
|
+
CLAUDECORD_HUB: https://claudecord.kdhanda.com
|
|
71
|
+
- run: cp target/${{ matrix.target }}/release/claudecord${{ matrix.ext }} claudecord-${{ matrix.name }}${{ matrix.ext }}
|
|
72
|
+
shell: bash
|
|
73
|
+
- uses: actions/upload-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: bin-${{ matrix.name }}
|
|
76
|
+
path: claudecord-${{ matrix.name }}${{ matrix.ext }}
|
|
77
|
+
|
|
78
|
+
# Wheels for glibc Linux (almost every distribution) are built in maturin's manylinux container, where the C compiler the TLS library needs is
|
|
79
|
+
# present. The standalone programs further up are static musl builds, which is what npm and the release files use.
|
|
80
|
+
wheels:
|
|
81
|
+
needs: version
|
|
82
|
+
strategy:
|
|
83
|
+
fail-fast: false
|
|
84
|
+
matrix:
|
|
85
|
+
include:
|
|
86
|
+
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, extra: "" }
|
|
87
|
+
- { os: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu, extra: "" }
|
|
88
|
+
- { os: macos-latest, target: aarch64-apple-darwin, extra: "" }
|
|
89
|
+
- { os: macos-15-intel, target: x86_64-apple-darwin, extra: "" }
|
|
90
|
+
- { os: windows-latest, target: x86_64-pc-windows-msvc, extra: "" }
|
|
91
|
+
runs-on: ${{ matrix.os }}
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
- uses: PyO3/maturin-action@v1
|
|
95
|
+
with:
|
|
96
|
+
target: ${{ matrix.target }}
|
|
97
|
+
args: --release --out dist ${{ matrix.extra }}
|
|
98
|
+
- uses: actions/upload-artifact@v4
|
|
99
|
+
with:
|
|
100
|
+
name: wheel-${{ matrix.target }}
|
|
101
|
+
path: dist
|
|
102
|
+
|
|
103
|
+
sdist:
|
|
104
|
+
needs: version
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v4
|
|
108
|
+
- uses: PyO3/maturin-action@v1
|
|
109
|
+
with:
|
|
110
|
+
command: sdist
|
|
111
|
+
args: --out dist
|
|
112
|
+
- uses: actions/upload-artifact@v4
|
|
113
|
+
with:
|
|
114
|
+
name: sdist
|
|
115
|
+
path: dist
|
|
116
|
+
|
|
117
|
+
publish-pypi:
|
|
118
|
+
needs: [build, wheels, sdist, verify-packaging]
|
|
119
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
environment: pypi
|
|
122
|
+
permissions:
|
|
123
|
+
id-token: write
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/download-artifact@v4
|
|
126
|
+
with:
|
|
127
|
+
pattern: "{wheel-*,sdist}"
|
|
128
|
+
path: dist
|
|
129
|
+
merge-multiple: true
|
|
130
|
+
# Trusted publishing: PyPI is told once that this repository and workflow may publish, so there is no password to leak.
|
|
131
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
132
|
+
continue-on-error: true
|
|
133
|
+
|
|
134
|
+
publish-npm:
|
|
135
|
+
needs: [build, verify-packaging]
|
|
136
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
137
|
+
runs-on: ubuntu-latest
|
|
138
|
+
environment: npm
|
|
139
|
+
steps:
|
|
140
|
+
- uses: actions/checkout@v4
|
|
141
|
+
- uses: actions/setup-node@v4
|
|
142
|
+
with:
|
|
143
|
+
node-version: 22
|
|
144
|
+
registry-url: https://registry.npmjs.org
|
|
145
|
+
- uses: actions/download-artifact@v4
|
|
146
|
+
with:
|
|
147
|
+
pattern: "bin-*"
|
|
148
|
+
path: bins
|
|
149
|
+
merge-multiple: true
|
|
150
|
+
- run: python3 scripts/package_npm.py bins npm-out
|
|
151
|
+
- id: have
|
|
152
|
+
run: echo "token=${{ secrets.NPM_TOKEN != '' }}" >> "$GITHUB_OUTPUT"
|
|
153
|
+
- if: steps.have.outputs.token == 'true'
|
|
154
|
+
run: scripts/publish_npm.sh npm-out
|
|
155
|
+
env:
|
|
156
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
157
|
+
- if: steps.have.outputs.token != 'true'
|
|
158
|
+
run: echo "NPM_TOKEN is not set, so nothing was published to npm (the packages were built and checked)."
|
|
159
|
+
|
|
160
|
+
github-release:
|
|
161
|
+
needs: [build, wheels, sdist, publish-npm]
|
|
162
|
+
runs-on: ubuntu-latest
|
|
163
|
+
steps:
|
|
164
|
+
- uses: actions/download-artifact@v4
|
|
165
|
+
with:
|
|
166
|
+
path: files
|
|
167
|
+
merge-multiple: true
|
|
168
|
+
- uses: softprops/action-gh-release@v2
|
|
169
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
170
|
+
with:
|
|
171
|
+
generate_release_notes: true
|
|
172
|
+
files: files/*
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: rust
|
|
2
|
+
|
|
3
|
+
# One pipeline, in order: the checks on Linux (x86 and ARM) and macOS, native Windows, and a small k6 run, all at once; then packaging
|
|
4
|
+
# (the pip wheel and npm packages installed in clean folders and run) only once all of those have passed, because there is no point
|
|
5
|
+
# packaging a build that fails its tests. Docs-only changes skip it, and a newer push replaces a run still going.
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
paths-ignore: ["**.md", "site/**", "learnings/**", "deploy/**"]
|
|
11
|
+
pull_request:
|
|
12
|
+
paths-ignore: ["**.md", "site/**", "learnings/**", "deploy/**"]
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: rust-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
check:
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
|
|
24
|
+
runs-on: ${{ matrix.os }}
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
28
|
+
with:
|
|
29
|
+
components: rustfmt, clippy
|
|
30
|
+
- uses: Swatinem/rust-cache@v2
|
|
31
|
+
with:
|
|
32
|
+
cache-on-failure: true
|
|
33
|
+
- run: cargo fmt --check
|
|
34
|
+
- run: cargo clippy --all-targets -- -D warnings
|
|
35
|
+
# Every integration test is one program (tests/it), so it is compiled and linked once.
|
|
36
|
+
- run: cargo test
|
|
37
|
+
# The timing tests again, many times: a race that shows once in fifty runs is still a bug.
|
|
38
|
+
- run: 'for i in $(seq 20); do cargo test --test it races:: -q || exit 1; done'
|
|
39
|
+
- run: python3 scripts/codemap.py --check
|
|
40
|
+
# Health checks last: every feature alive, then the shipped binary used the way a person would.
|
|
41
|
+
- run: cargo run -q -- selftest
|
|
42
|
+
- run: scripts/smoke.sh
|
|
43
|
+
|
|
44
|
+
# Windows is built and tested natively. The stand-in-agent tests need a Unix shell and skip themselves there; the lints run
|
|
45
|
+
# on Linux and macOS, and the Windows-only code is compiled by the test build.
|
|
46
|
+
windows:
|
|
47
|
+
runs-on: windows-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
51
|
+
- uses: Swatinem/rust-cache@v2
|
|
52
|
+
with:
|
|
53
|
+
cache-on-failure: true
|
|
54
|
+
- run: cargo test
|
|
55
|
+
- run: cargo run -q -- selftest
|
|
56
|
+
|
|
57
|
+
# A small k6 run (200 users with three bots each, under a minute). The big runs are in load.yml and wait for approval.
|
|
58
|
+
load-smoke:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
63
|
+
- uses: Swatinem/rust-cache@v2
|
|
64
|
+
with:
|
|
65
|
+
cache-on-failure: true
|
|
66
|
+
- uses: grafana/setup-k6-action@v1
|
|
67
|
+
- run: cargo build --release --bin claudecord
|
|
68
|
+
- run: scripts/load/smoke.sh 200 target/release/claudecord
|
|
69
|
+
|
|
70
|
+
# Only once everything above has passed. macOS and Windows packaging are checked by release.yml before anything is published.
|
|
71
|
+
packaging:
|
|
72
|
+
needs: [check, windows, load-smoke]
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v4
|
|
76
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
77
|
+
- uses: Swatinem/rust-cache@v2
|
|
78
|
+
with:
|
|
79
|
+
cache-on-failure: true
|
|
80
|
+
- uses: astral-sh/setup-uv@v7
|
|
81
|
+
- uses: actions/setup-node@v4
|
|
82
|
+
with:
|
|
83
|
+
node-version: 22
|
|
84
|
+
- run: python3 scripts/test_packaging.py
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: uptime
|
|
2
|
+
|
|
3
|
+
# An outside check of the hub, from GitHub's network every 5 minutes, which is free. A failure here means the hub could not be
|
|
4
|
+
# reached from the internet: DNS, TLS, the host or the hub itself. GitHub emails the repository owner when a scheduled run
|
|
5
|
+
# fails. Set the repository variable HUB_URL (Settings > Secrets and variables > Actions > Variables) to turn it on.
|
|
6
|
+
on:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "*/5 * * * *"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
probe:
|
|
13
|
+
if: vars.HUB_URL != ''
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
# Three tries, ten seconds apart, so one dropped packet is not an outage (the same debounce the prober uses).
|
|
17
|
+
- run: |
|
|
18
|
+
for i in 1 2 3; do
|
|
19
|
+
code=$(curl -s -o body.json -w '%{http_code}' --max-time 10 "${{ vars.HUB_URL }}/readyz" || true)
|
|
20
|
+
[ "$code" = "200" ] && { echo "ready"; exit 0; }
|
|
21
|
+
echo "try $i: HTTP $code"; cat body.json 2>/dev/null; sleep 10
|
|
22
|
+
done
|
|
23
|
+
exit 1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: web
|
|
2
|
+
|
|
3
|
+
# The dashboard (web/) is built with Vite and the build is committed in web/dist, because the Rust program embeds it and `cargo install`
|
|
4
|
+
# must work without Node. This fails if the committed build is not what the source builds to, so the two cannot drift apart.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
paths: ["web/**"]
|
|
8
|
+
pull_request:
|
|
9
|
+
paths: ["web/**"]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
defaults:
|
|
15
|
+
run:
|
|
16
|
+
working-directory: web
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 22
|
|
22
|
+
cache: npm
|
|
23
|
+
cache-dependency-path: web/package-lock.json
|
|
24
|
+
- run: npm ci
|
|
25
|
+
- run: npm run build
|
|
26
|
+
- name: The committed build matches the source
|
|
27
|
+
run: git diff --exit-code -- dist
|