Skip to content

Commit 8409951

Browse files
committed
fix(fork): VPS deploy skips docs-only commits and builds once per burst of merges
Closes #29. A range that changes only docs, Markdown, workflows, desktop, mobile, marketing or test files moves the running checkout without a build. A build starts only after main has not moved for 10 minutes. The busy-agent check runs at most every 5 minutes and stops at the first busy project. The service runs at low CPU, IO and process priority with MemoryHigh=6G.
1 parent 72f0366 commit 8409951

3 files changed

Lines changed: 50 additions & 8 deletions

File tree

‎docs/fork.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ The client setting `forkChangelogEnabled` turns on a changelog of the fork. It i
136136

137137
The VPS server follows fork main. The timer `t3-deploy.timer` runs `scripts/fork-vps/deploy.sh` one minute after each run ends.
138138

139-
- When main has a new commit, the script builds it while the old server keeps running. It builds each commit once.
140-
- It switches only when no agent turn runs. A server restart cancels running turns. T3 resumes them only when "Recover interrupted agents" or "Continue threads after restarts" is on. While agents run, the next run tries again.
139+
- When the new commits change only paths that do not reach the server build, the script moves the running checkout to the new commit without a build or a restart. These paths are `docs`, `*.md`, `.github`, `apps/desktop`, `apps/mobile`, `apps/marketing` and test files.
140+
- Otherwise it waits until main has not moved for 10 minutes, so a burst of merges gets one build. Then it builds main while the old server keeps running. It builds each commit once.
141+
- It switches only when no agent turn runs. A server restart cancels running turns. T3 resumes them only when "Recover interrupted agents" or "Continue threads after restarts" is on. While agents run, it checks again after 5 minutes, because each check starts one node process per project.
142+
- `t3-deploy.service` runs at a low CPU, IO and process priority. Above 6 GB of memory, the kernel slows the build instead of taking memory from the other apps on the VPS.
141143
- `/opt/t3code` is a link to `/opt/t3code-a` or `/opt/t3code-b`. The script builds in the other folder, moves the link and restarts `t3-server`.
142144
- If the new server does not answer, the script moves the link back.
143145

@@ -149,6 +151,8 @@ git -C /opt/t3code-a worktree add --detach /opt/t3code-b HEAD
149151
cp /opt/t3code/scripts/fork-vps/t3-deploy.{service,timer} /etc/systemd/system/ && systemctl daemon-reload && systemctl enable --now t3-deploy.timer
150152
```
151153

154+
A change to `t3-deploy.service` or `t3-deploy.timer` reaches the VPS only when you run the `cp` and `systemctl daemon-reload` again.
155+
152156
To read the result, run `journalctl -u t3-deploy.service -n 50`.
153157

154158
## Recover interrupted agents

‎scripts/fork-vps/deploy.sh‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ set -euo pipefail
1111
readonly LINK=/opt/t3code
1212
readonly BUILT_MARKER=.t3-deploy-built-sha
1313
readonly FAILED_MARKER=.t3-deploy-failed-sha
14+
readonly BUSY_MARKER=.t3-deploy-busy-checked
15+
# A build costs about 10 CPU minutes on a box that also serves production apps.
16+
# A burst of merges gets one build: the script builds only when main has not moved for this long.
17+
readonly QUIET_SECONDS=600
18+
# The busy check starts one node process per project, about 6 CPU seconds each.
19+
readonly BUSY_CHECK_MINUTES=5
20+
# Paths that do not reach the server build. A commit range that changes only these
21+
# moves the running checkout forward without a build or a restart.
22+
readonly NO_BUILD_PATHS=(
23+
':(exclude)docs' ':(exclude)*.md' ':(exclude).github' ':(exclude)apps/desktop'
24+
':(exclude)apps/mobile' ':(exclude)apps/marketing' ':(exclude)*.test.ts' ':(exclude)*.test.tsx'
25+
)
1426
export PATH="/opt/node/bin:$PATH"
1527

1628
log() { echo "t3-deploy: $*"; }
@@ -24,18 +36,30 @@ esac
2436

2537
git -C "$active" fetch --quiet origin main
2638
sha="$(git -C "$active" rev-parse FETCH_HEAD)"
27-
if [[ "$(git -C "$active" rev-parse HEAD)" == "$sha" ]]; then
39+
running="$(git -C "$active" rev-parse HEAD)"
40+
if [[ "$running" == "$sha" ]]; then
41+
exit 0
42+
fi
43+
44+
if [[ -z "$(git -C "$active" diff --name-only "$running" "$sha" -- . "${NO_BUILD_PATHS[@]}")" ]]; then
45+
git -C "$active" checkout --quiet --detach "$sha"
46+
log "${sha:0:12} changes nothing that the server builds; the running server moves to it without a build"
2847
exit 0
2948
fi
3049

3150
# A thread in one of these states has a live provider process that a restart kills.
51+
# Prints the first busy thread and stops there.
3252
busy_threads() {
3353
local tool=("node" "$active/apps/server/dist/bin.mjs" "tool" "call")
34-
local project
54+
local project busy
3555
for project in $(cd /root/Github && "${tool[@]}" t3_project_list | jq -r '.projects[].id'); do
36-
(cd /root/Github && "${tool[@]}" t3_thread_list --input "$(jq -nc --arg p "$project" \
37-
'{projectId: $p, statuses: ["preparing", "queued", "starting", "running"], limit: 1}')") |
38-
jq -r '.threads[].threadId'
56+
busy="$(cd /root/Github && "${tool[@]}" t3_thread_list --input "$(jq -nc --arg p "$project" \
57+
'{projectId: $p, statuses: ["preparing", "queued", "starting", "running"], limit: 1}')" |
58+
jq -r '.threads[].threadId')"
59+
if [[ -n "$busy" ]]; then
60+
echo "$busy"
61+
return
62+
fi
3963
done
4064
}
4165

@@ -45,6 +69,10 @@ if [[ "$(cat "$target/$FAILED_MARKER" 2>/dev/null)" == "$sha" ]]; then
4569
exit 0
4670
fi
4771
if [[ "$(cat "$target/$BUILT_MARKER" 2>/dev/null)" != "$sha" ]]; then
72+
quiet_for=$(($(date +%s) - $(git -C "$active" log -1 --format=%ct "$sha")))
73+
if ((quiet_for < QUIET_SECONDS)); then
74+
exit 0
75+
fi
4876
log "building ${sha:0:12} in $target"
4977
rm -f "$target/$BUILT_MARKER"
5078
git -C "$target" fetch --quiet origin main
@@ -57,8 +85,12 @@ if [[ "$(cat "$target/$BUILT_MARKER" 2>/dev/null)" != "$sha" ]]; then
5785
echo "$sha" >"$target/$BUILT_MARKER"
5886
fi
5987

88+
if [[ -n "$(find "$target/$BUSY_MARKER" -mmin "-$BUSY_CHECK_MINUTES" 2>/dev/null)" ]]; then
89+
exit 0
90+
fi
6091
if [[ -n "$(busy_threads)" ]]; then
61-
log "${sha:0:12} is built; agents are running, so the switch waits for the next run"
92+
touch "$target/$BUSY_MARKER"
93+
log "${sha:0:12} is built; agents are running, so the switch waits $BUSY_CHECK_MINUTES minutes"
6294
exit 0
6395
fi
6496

‎scripts/fork-vps/t3-deploy.service‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@ Type=oneshot
77
Environment=HOME=/root
88
# A stuck build must not block the next timer runs.
99
TimeoutStartSec=45min
10+
# Production apps on the same box come first. Above MemoryHigh the kernel slows the
11+
# build down instead of letting it take the memory of the other apps.
12+
CPUWeight=10
13+
IOWeight=10
14+
Nice=10
15+
MemoryHigh=6G
1016
ExecStart=/opt/t3code/scripts/fork-vps/deploy.sh

0 commit comments

Comments
 (0)