Skip to content
This repository was archived by the owner on Jun 26, 2026. It is now read-only.

Commit e575749

Browse files
matejvasekclaude
andcommitted
feat: better testing server setup for init.sh
- Stop operations use PID files (.dev-pids/) instead of pgrep/pkill, with recursive kill_tree to handle process trees (e.g. yarn->webpack) - Ports randomized via --randomize-ports flag (default ports unchanged) - Active ports written to .dev-env.json for agent and tooling discovery - Go backend auto-recompiles on .go/.mod/.sum changes via inotifywait with build-then-swap strategy (build to tmp, swap only on success) - start-console.sh accepts --backend-port, --plugin-port, --console-port, --cidfile CLI args; uses -p port mapping on all platforms - webpack.config.ts reads PLUGIN_PORT env var with fallback to 9001 - Agent docs (AGENTS.md, WORKFLOW.md, init-session) reference .dev-env.json and .dev-logs/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0c1376c commit e575749

9 files changed

Lines changed: 192 additions & 38 deletions

File tree

.claude/commands/init-session.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
allowed-tools: Bash(git log:*), Bash(pwd), Bash(./init.sh), Bash(yarn test*), Read
2+
allowed-tools: Bash(git log:*), Bash(pwd), Bash(./init.sh), Bash(yarn test*), Bash(cat .dev-env.json), Read
33
description: Run startup sequence (steps 1-6 from docs/WORKFLOW.md)
44
---
55

@@ -19,5 +19,6 @@ Execute the startup sequence from `docs/WORKFLOW.md`. AGENTS.md is always in con
1919
3. **Check struggles** — read `docs/agent-struggles.json`. If unresolved entries exist, present to user.
2020
4. **Pick feature** — read `docs/features.json`, find first `"passes": false` entry.
2121
5. **Start dev env** — run `./init.sh`.
22-
6. **Run tests** — run `yarn test` and verify app is healthy.
23-
7. **Wait** — tell the user you're oriented, report the picked feature and which step of the Feature Development Sequence you'd start at. When the user says to proceed, follow the Feature Development Sequence in `docs/WORKFLOW.md` step by step. Do NOT start any work autonomously.
22+
6. **Read ports** — read `.dev-env.json` and note the backend, plugin, and console ports.
23+
7. **Run tests** — run `yarn test` and verify app is healthy.
24+
8. **Wait** — tell the user you're oriented, report the picked feature and which step of the Feature Development Sequence you'd start at. When the user says to proceed, follow the Feature Development Sequence in `docs/WORKFLOW.md` step by step. Do NOT start any work autonomously.

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
/bin
2+
.dev-logs
3+
.dev-pids
4+
.dev-env.json

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ AGENTS.override.md
3535
# Local development configuration
3636
.dev
3737
.dev-logs
38+
.dev-pids
39+
.dev-env.json
3840

3941
# Yarn v4 (Berry)
4042
.yarn/*

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,7 @@ No em dashes (`—`). Use commas, periods, or parentheses instead.
3232
| `docs/potential-features.json` | Candidate features for future implementation |
3333
| `docs/claude-progress.txt` | Session handoff log — see [`references/claude-progress-readme.md`](docs/references/claude-progress-readme.md) |
3434
| `docs/agent-struggles.json` | Struggle log — see [`references/agent-struggles-readme.md`](docs/references/agent-struggles-readme.md) |
35+
| `.dev-env.json` | Dev server ports (backendPort, pluginPort, consolePort), written by init.sh |
36+
| `.dev-logs/` | Dev server log files (backend.log, webpack.log, console.log) |
3537
| `docs/references/ocp-plugin-guide.md` | OCP dynamic plugin mechanics, i18n, extension points |
3638
| `docs/references/commit-message-guide.md` | Git commit conventions and authorship rules |

docs/WORKFLOW.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ Every session, before doing any work:
99
3. Read `docs/agent-struggles.json` — if unresolved entries exist, present to user
1010
4. Read `docs/features.json` — pick first `"passes": false` entry
1111
5. Run `./init.sh` — start dev env
12-
6. Run tests — verify app is healthy
13-
7. If broken → fix first. If clean → start [Feature Development Sequence](#feature-development-sequence).
12+
6. Read `.dev-env.json` — note the dev server ports (backend, plugin, console)
13+
7. Run tests — verify app is healthy
14+
8. If broken → fix first. If clean → start [Feature Development Sequence](#feature-development-sequence).
1415

1516
## Feature Development Sequence
1617

docs/claude-progress.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
# Claude Progress Log
22
# Newest entries first. Agents: append your entry at the top after the header.
33

4+
---
5+
## 2026-05-15 | Session: Better testing server setup (init.sh)
6+
Worked on: Improve init.sh with PID files, port randomization, Go auto-recompile, agent docs
7+
Completed:
8+
- Stop operations use PID files (.dev-pids/) with recursive kill_tree instead of pgrep/pkill
9+
- Ports randomized via --randomize-ports flag (default ports unchanged for human testers)
10+
- Active ports written to .dev-env.json for agent and tooling discovery
11+
- Go backend auto-recompiles on .go/.mod/.sum changes via inotifywait (build-then-swap strategy)
12+
- start-console.sh accepts --backend-port, --plugin-port, --console-port, --cidfile CLI args
13+
- Dropped --network=host on Linux in favor of -p port mapping (enables console port randomization)
14+
- webpack.config.ts reads PLUGIN_PORT env var with fallback to 9001
15+
- Agent docs (AGENTS.md, WORKFLOW.md, init-session) reference .dev-env.json and .dev-logs/
16+
- Design spec and implementation plan written
17+
- 13 suites, 112 tests, all passing
18+
Left off: Branch improve-test-cluster, ready to push. Feature not yet marked as passes:true (needs manual testing confirmation).
19+
Blockers: None
20+
421
---
522
## 2026-05-13 | Session: Fix function list not updating on GitHub user switch
623
Worked on: Bug fix for PAT switching via Avatar button not refreshing the function list

init.sh

Lines changed: 134 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ BACKEND_PORT=8080
88
PLUGIN_PORT=9001
99
CONSOLE_PORT=9000
1010
TIMEOUT=60
11+
PID_DIR=".dev-pids"
1112

1213
wait_for_port() {
1314
local port=$1
@@ -25,37 +26,124 @@ wait_for_port() {
2526
done
2627
}
2728

29+
kill_tree() {
30+
local pid=$1
31+
local children
32+
children=$(pgrep -P "$pid" 2>/dev/null || true)
33+
for child in $children; do
34+
kill_tree "$child"
35+
done
36+
kill "$pid" 2>/dev/null || true
37+
}
38+
39+
stop_pid() {
40+
local pidfile="$PID_DIR/$1"
41+
local label=$2
42+
43+
if [ ! -f "$pidfile" ]; then
44+
return
45+
fi
46+
47+
local pid
48+
pid=$(cat "$pidfile")
49+
if kill -0 "$pid" 2>/dev/null; then
50+
kill_tree "$pid"
51+
while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done
52+
echo "Stopped $label (PID $pid)."
53+
fi
54+
rm -f "$pidfile"
55+
}
56+
57+
random_free_port() {
58+
local port
59+
while true; do
60+
port=$((RANDOM % 50001 + 10000))
61+
if ! bash -c "echo >/dev/tcp/localhost/$port" 2>/dev/null; then
62+
echo "$port"
63+
return
64+
fi
65+
done
66+
}
67+
68+
write_dev_env() {
69+
cat > .dev-env.json <<EOF
70+
{
71+
"backendPort": $BACKEND_PORT,
72+
"pluginPort": $PLUGIN_PORT,
73+
"consolePort": $CONSOLE_PORT
74+
}
75+
EOF
76+
}
77+
2878
start_backend() {
2979
echo "Building Go backend..."
3080
(cd backend && go build -buildvcs=false -o ../bin/backend .)
3181
echo "Starting Go backend..."
32-
./bin/backend --http-port "$BACKEND_PORT" >"$LOG_DIR/backend.log" 2>&1 &
82+
./bin/backend --http-port "$BACKEND_PORT" >>"$LOG_DIR/backend.log" 2>&1 &
83+
echo $! > "$PID_DIR/backend.pid"
3384
}
3485

35-
stop_backend() {
36-
if pgrep -f "bin/backend" >/dev/null 2>&1; then
37-
pkill -f "bin/backend" && echo "Stopped Go backend."
86+
start_backend_watcher() {
87+
if ! command -v inotifywait &>/dev/null; then
88+
echo "Warning: inotifywait not found. Install inotify-tools for auto-recompile."
89+
return
3890
fi
91+
92+
echo "Starting backend file watcher..."
93+
(
94+
while true; do
95+
inotifywait -r -e modify,create,delete,move --include '\.(go|mod|sum)$' backend/ >/dev/null 2>&1
96+
sleep 1 # debounce
97+
98+
echo "[watcher] Detected change, rebuilding backend..."
99+
if (cd backend && go build -buildvcs=false -o ../bin/backend-tmp .); then
100+
old_pid=$(cat "$PID_DIR/backend.pid" 2>/dev/null || true)
101+
if [ -n "$old_pid" ]; then
102+
kill "$old_pid" 2>/dev/null || true
103+
while kill -0 "$old_pid" 2>/dev/null; do sleep 0.1; done
104+
fi
105+
mv bin/backend-tmp bin/backend
106+
./bin/backend --http-port "$BACKEND_PORT" >>"$LOG_DIR/backend.log" 2>&1 &
107+
echo $! > "$PID_DIR/backend.pid"
108+
echo "[watcher] Backend restarted (PID $!)."
109+
else
110+
echo "[watcher] Build failed. Keeping current backend running."
111+
rm -f bin/backend-tmp
112+
fi
113+
done
114+
) >>"$LOG_DIR/backend.log" 2>&1 &
115+
echo $! > "$PID_DIR/backend-watcher.pid"
116+
}
117+
118+
stop_backend() {
119+
stop_pid "backend-watcher.pid" "backend watcher"
120+
stop_pid "backend.pid" "Go backend"
39121
}
40122

41123
stop_plugin() {
42-
if pgrep -f "webpack serve" >/dev/null 2>&1; then
43-
pkill -f "webpack serve" && echo "Stopped plugin dev server."
44-
fi
124+
stop_pid "webpack.pid" "plugin dev server"
45125
}
46126

47127
stop_console() {
48-
local container
49-
container=$(podman ps -q --filter ancestor="$CONSOLE_IMAGE" 2>/dev/null || true)
50-
if [ -n "$container" ]; then
51-
podman stop "$container" >/dev/null && echo "Stopped OpenShift console."
128+
local cidfile="$PID_DIR/console.cid"
129+
130+
if [ ! -f "$cidfile" ]; then
131+
return
52132
fi
133+
134+
local cid
135+
cid=$(cat "$cidfile")
136+
if podman stop "$cid" >/dev/null 2>&1; then
137+
echo "Stopped OpenShift console (container $cid)."
138+
fi
139+
rm -f "$cidfile"
53140
}
54141

55142
stop_dev() {
56143
stop_backend
57144
stop_plugin
58145
stop_console
146+
rm -f .dev-env.json
59147
}
60148

61149
check_prerequisites() {
@@ -79,12 +167,18 @@ install_dependencies() {
79167

80168
start_plugin() {
81169
echo "Starting plugin dev server..."
82-
yarn start >"$LOG_DIR/webpack.log" 2>&1 &
170+
PLUGIN_PORT="$PLUGIN_PORT" yarn start >"$LOG_DIR/webpack.log" 2>&1 &
171+
echo $! > "$PID_DIR/webpack.pid"
83172
}
84173

85174
start_console() {
86175
echo "Starting OpenShift console..."
87-
yarn start-console >"$LOG_DIR/console.log" 2>&1 &
176+
./start-console.sh \
177+
--backend-port "$BACKEND_PORT" \
178+
--plugin-port "$PLUGIN_PORT" \
179+
--console-port "$CONSOLE_PORT" \
180+
--cidfile "$PID_DIR/console.cid" \
181+
>"$LOG_DIR/console.log" 2>&1 &
88182
}
89183

90184
print_status() {
@@ -98,21 +192,42 @@ print_status() {
98192
}
99193

100194
main() {
101-
mkdir -p "$LOG_DIR" bin
195+
mkdir -p "$LOG_DIR" "$PID_DIR" bin
102196
check_prerequisites
103197
install_dependencies
104198
stop_dev
199+
write_dev_env
105200
start_backend
106201
wait_for_port "$BACKEND_PORT" "Go backend"
202+
start_backend_watcher
107203
start_plugin
108204
wait_for_port "$PLUGIN_PORT" "Plugin dev server"
109205
start_console
110206
wait_for_port "$CONSOLE_PORT" "OpenShift console"
111207
print_status
112208
}
113209

114-
if [ "${1:-}" = "--stop" ]; then
115-
stop_dev
116-
else
117-
main
118-
fi
210+
case "${1:-}" in
211+
--stop)
212+
stop_dev
213+
;;
214+
--randomize-ports)
215+
BACKEND_PORT=$(random_free_port)
216+
PLUGIN_PORT=$(random_free_port)
217+
while [ "$PLUGIN_PORT" -eq "$BACKEND_PORT" ]; do
218+
PLUGIN_PORT=$(random_free_port)
219+
done
220+
CONSOLE_PORT=$(random_free_port)
221+
while [ "$CONSOLE_PORT" -eq "$BACKEND_PORT" ] || [ "$CONSOLE_PORT" -eq "$PLUGIN_PORT" ]; do
222+
CONSOLE_PORT=$(random_free_port)
223+
done
224+
main
225+
;;
226+
"")
227+
main
228+
;;
229+
*)
230+
echo "Usage: $0 [--stop | --randomize-ports]"
231+
exit 1
232+
;;
233+
esac

start-console.sh

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,23 @@
22

33
set -euo pipefail
44

5+
# Parse CLI arguments (all optional, with defaults)
6+
BACKEND_PORT=8080
7+
PLUGIN_PORT=9001
8+
CIDFILE=""
9+
10+
while [[ $# -gt 0 ]]; do
11+
case $1 in
12+
--backend-port) BACKEND_PORT="$2"; shift 2 ;;
13+
--plugin-port) PLUGIN_PORT="$2"; shift 2 ;;
14+
--console-port) CONSOLE_PORT="$2"; shift 2 ;;
15+
--cidfile) CIDFILE="$2"; shift 2 ;;
16+
*) echo "Unknown argument: $1"; exit 1 ;;
17+
esac
18+
done
19+
520
CONSOLE_IMAGE=${CONSOLE_IMAGE:="quay.io/openshift/origin-console:latest"}
6-
CONSOLE_PORT=${CONSOLE_PORT:=9000}
21+
CONSOLE_PORT=${CONSOLE_PORT:-9000}
722
CONSOLE_IMAGE_PLATFORM=${CONSOLE_IMAGE_PLATFORM:="linux/amd64"}
823

924
# Plugin metadata is declared in package.json
@@ -42,22 +57,15 @@ echo "Console Platform: $CONSOLE_IMAGE_PLATFORM"
4257
# Prefer podman if installed. Otherwise, fall back to docker.
4358
if [ -x "$(command -v podman)" ]; then
4459
CONTAINER_CMD="podman"
45-
if [ "$(uname -s)" = "Linux" ]; then
46-
# Use host networking on Linux since host.containers.internal is unreachable in some environments.
47-
PLUGIN_HOST="localhost"
48-
CONTAINER_NETWORK_OPTS="--network=host"
49-
else
50-
PLUGIN_HOST="host.containers.internal"
51-
CONTAINER_NETWORK_OPTS="-p ${CONSOLE_PORT}:9000"
52-
fi
60+
PLUGIN_HOST="host.containers.internal"
5361
else
5462
CONTAINER_CMD="docker"
5563
PLUGIN_HOST="host.docker.internal"
56-
CONTAINER_NETWORK_OPTS="-p ${CONSOLE_PORT}:9000"
5764
fi
65+
CONTAINER_NETWORK_OPTS="-p ${CONSOLE_PORT}:9000"
5866

59-
BRIDGE_PLUGINS="${PLUGIN_NAME}=http://${PLUGIN_HOST}:9001"
60-
BRIDGE_PLUGIN_PROXY='{"services":[{"consoleAPIPath":"/api/proxy/plugin/'"${PLUGIN_NAME}"'/backend/","endpoint":"http://'"${PLUGIN_HOST}"':8080","authorize":false}]}'
67+
BRIDGE_PLUGINS="${PLUGIN_NAME}=http://${PLUGIN_HOST}:${PLUGIN_PORT}"
68+
BRIDGE_PLUGIN_PROXY='{"services":[{"consoleAPIPath":"/api/proxy/plugin/'"${PLUGIN_NAME}"'/backend/","endpoint":"http://'"${PLUGIN_HOST}"':'"${BACKEND_PORT}"'","authorize":false}]}'
6169

6270
# Allow browser to connect to GitHub API (CSP connect-src).
6371
# Production uses ConsolePlugin.spec.contentSecurityPolicy instead.
@@ -66,4 +74,9 @@ BRIDGE_CONTENT_SECURITY_POLICY="connect-src=https://api.github.com"
6674
echo "BRIDGE_PLUGINS=$BRIDGE_PLUGINS"
6775
echo "BRIDGE_PLUGIN_PROXY=$BRIDGE_PLUGIN_PROXY"
6876

69-
$CONTAINER_CMD run --pull always --platform $CONSOLE_IMAGE_PLATFORM --rm $CONTAINER_NETWORK_OPTS --env-file <(env | grep ^BRIDGE) $CONSOLE_IMAGE
77+
CIDFILE_OPTS=""
78+
if [ -n "$CIDFILE" ]; then
79+
CIDFILE_OPTS="--cidfile $CIDFILE"
80+
fi
81+
82+
$CONTAINER_CMD run --pull always --platform $CONSOLE_IMAGE_PLATFORM --rm $CIDFILE_OPTS $CONTAINER_NETWORK_OPTS --env-file <(env | grep ^BRIDGE) $CONSOLE_IMAGE

webpack.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const config: Configuration = {
6161
},
6262
devServer: {
6363
static: './dist',
64-
port: 9001,
64+
port: Number(process.env.PLUGIN_PORT) || 9001,
6565
// Allow Bridge running in a container to connect to the plugin dev server.
6666
allowedHosts: 'all',
6767
headers: {

0 commit comments

Comments
 (0)