Repository navigation
Expand file tree
/
Copy pathtools
More file actions
executable file
·257 lines (227 loc) · 8.6 KB
/
Copy pathtools
File metadata and controls
executable file
·257 lines (227 loc) · 8.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
usage() {
cat <<EOF
Usage: ./tools <command>
Commands:
build Build Go binaries (the API server)
build-api Build the API server
test Run all tests
test-api Run API tests
test-web Run webapp tests
run-api Run the API server locally (sources .env.dev if present)
run-web Run the SPA dev server (vite on :5173, from webapp/)
dev [--no-tail] Boot postgres + api + vite together in one terminal
(postgres in docker, api+vite as logged background jobs).
--no-tail returns instead of tailing the logs.
dev-down Stop the dev stack (postgres container + background jobs)
docker-build Build Docker images
clean Remove build artifacts
check-deps Verify required tools are installed
EOF
}
# load_dev_env sources .env.dev into the current shell so api commands
# inherit LONGHOUSE_* without a docker-compose round-trip. Quiet no-op if
# the file is missing (lets `run-api` keep working from a clean checkout).
load_dev_env() {
local env_file="$SCRIPT_DIR/.env.dev"
if [ -f "$env_file" ]; then
set -a
# shellcheck disable=SC1090
source "$env_file"
set +a
fi
}
check_deps() {
local missing=()
command -v go >/dev/null 2>&1 || missing+=("go")
command -v docker >/dev/null 2>&1 || missing+=("docker")
command -v npm >/dev/null 2>&1 || missing+=("npm")
if [ ${#missing[@]} -gt 0 ]; then
echo "Missing required tools: ${missing[*]}"
exit 1
fi
echo "All dependencies present"
}
build_api() {
echo "Building API server..."
cd "$SCRIPT_DIR/api"
go build -o longhouse .
echo "Built: api/longhouse"
}
build() {
build_api
}
test_api() {
echo "Testing API..."
cd "$SCRIPT_DIR/api"
go test ./...
}
test_web() {
echo "Testing webapp..."
cd "$SCRIPT_DIR/webapp"
if [ ! -d node_modules ]; then
echo "▶ webapp/node_modules missing — running npm install"
npm install
fi
npm test
}
run_tests() {
test_api
test_web
}
run_api() {
load_dev_env
cd "$SCRIPT_DIR/api"
go run . serve "$@"
}
# run_web runs the SolidJS SPA (webapp/) dev server. The web tier is the SPA
# now; there is no longer a Go web binary.
run_web() {
cd "$SCRIPT_DIR/webapp"
npm run dev -- "$@"
}
# dev brings the whole local stack up in the foreground. Postgres runs
# detached in docker; api + vite stream their logs into ./logs/ and
# their PIDs land in ./logs/*.pid so dev-down can clean up. Ctrl-C
# leaves postgres up (cheap, persistent) — explicit dev-down kills it.
#
# When stdout isn't a tty (CI, background invocation, an agent backgrounding
# the script), the trailing `tail -f` of the log files is skipped so the
# script exits cleanly once the backgrounded api+vite are launched. Pass
# `--no-tail` to force that off even from a terminal.
dev() {
local follow_logs=1
if [ "${1:-}" = "--no-tail" ]; then
follow_logs=0
shift
elif [ ! -t 1 ]; then
# Non-interactive: nobody's reading the tail anyway, and parking
# here keeps a `tail -f` (plus its wrapper) alive forever after
# the caller detaches.
follow_logs=0
fi
load_dev_env
mkdir -p "$SCRIPT_DIR/logs"
# We don't require the docker-compose plugin — a single `docker run` for
# postgres is the entire infra for local dev. docker-compose.yml is kept
# as a reference but isn't on the critical path.
if docker ps --filter name=^longhouse-postgres-dev$ --format '{{.Names}}' | grep -q .; then
echo "▶ postgres (docker) — already running"
elif docker ps -a --filter name=^longhouse-postgres-dev$ --format '{{.Names}}' | grep -q .; then
echo "▶ postgres (docker) — restarting existing container"
docker start longhouse-postgres-dev >/dev/null
else
echo "▶ postgres (docker) — creating container"
docker run -d \
--name longhouse-postgres-dev \
-e POSTGRES_USER=longhouse \
-e POSTGRES_PASSWORD=devpass123 \
-e POSTGRES_DB=longhouse_db \
-p 5433:5432 \
-v longhouse_postgres_data:/var/lib/postgresql/data \
postgres:17 >/dev/null
fi
echo "▶ waiting for postgres to accept connections..."
local tries=0
until docker exec longhouse-postgres-dev pg_isready -U longhouse >/dev/null 2>&1; do
tries=$((tries + 1))
if [ "$tries" -ge 30 ]; then
echo "postgres never became ready; check 'docker logs longhouse-postgres-dev'"
exit 1
fi
sleep 1
done
# Enable job control so each backgrounded job below gets its own
# process group. Without `set -m`, this script (run non-interactively)
# leaves the subshell in our PGID, and `kill -- "-$pid"` in dev-down
# can't reach the grandchild `api`/`npm` binaries. They get reparented
# to init and survive tear-down — every `./tools dev` cycle was
# leaking one stale listener on :6080 because of that.
#
# The `exec` inside each subshell also helps: it replaces the subshell
# with the long-running process so `$!` IS that process's PID (instead
# of a wrapper shell that's a fragile signal hop away from it).
set -m
echo "▶ api → http://localhost:${LONGHOUSE_API_PORT:-6080} (logs: logs/api.log)"
( cd "$SCRIPT_DIR/api" && exec go run . serve ) >"$SCRIPT_DIR/logs/api.log" 2>&1 &
echo $! > "$SCRIPT_DIR/logs/api.pid"
echo "▶ vite → http://localhost:5173 (logs: logs/vite.log)"
( cd "$SCRIPT_DIR/webapp" && exec npm run dev ) >"$SCRIPT_DIR/logs/vite.log" 2>&1 </dev/null &
echo $! > "$SCRIPT_DIR/logs/vite.pid"
set +m
cat <<EOF
Dev stack up.
SPA http://localhost:5173
API http://localhost:${LONGHOUSE_API_PORT:-6080}
/dev-login http://localhost:5173/dev-login ← pick a user to sign in
Logs tail -f logs/api.log logs/vite.log
Tear down ./tools dev-down
Ctrl-C in this terminal leaves everything running in the background.
EOF
if [ "$follow_logs" = 1 ]; then
# Park here so the user can Ctrl-C to silence this tail; processes
# keep running because they were spawned in their own subshells.
tail -f "$SCRIPT_DIR/logs/api.log" "$SCRIPT_DIR/logs/vite.log"
fi
}
dev_down() {
for name in api vite; do
local pid_file="$SCRIPT_DIR/logs/${name}.pid"
if [ -f "$pid_file" ]; then
local pid
pid="$(cat "$pid_file")"
if kill -0 "$pid" 2>/dev/null; then
echo "▶ stopping $name (pid $pid)"
# Kill the process group so child go/node processes go too.
# Needs `set -m` in dev() so the job actually has its own
# group; without that the negative-pid form is a no-op.
kill -- "-$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true
fi
rm -f "$pid_file"
fi
done
# Belt-and-suspenders: kill anything still bound to the dev API/TCP
# ports. Earlier versions of this script lost track of grandchildren
# spawned by `go run`, so a fresh `./tools dev` would silently fail
# to bind and hand traffic to the stale instance. `fuser -k` is a
# no-op when nothing is listening, so it's safe on a clean down.
local api_port="${LONGHOUSE_API_PORT:-6080}"
if command -v fuser >/dev/null 2>&1; then
fuser -k -TERM "${api_port}/tcp" 2>/dev/null || true
fuser -k -TERM "6081/tcp" 2>/dev/null || true
fi
if docker ps --filter name=^longhouse-postgres-dev$ --format '{{.Names}}' | grep -q .; then
echo "▶ stopping postgres"
docker stop longhouse-postgres-dev >/dev/null
fi
echo "Dev stack down. (Container + volume preserved; 'docker rm longhouse-postgres-dev' to wipe.)"
}
docker_build() {
echo "Building Docker images..."
cd "$SCRIPT_DIR"
docker build -f Dockerfile.api -t longhouse/api:dev .
docker build -f Dockerfile.web -t longhouse/web:dev .
echo "Done"
}
clean() {
echo "Cleaning build artifacts..."
rm -f "$SCRIPT_DIR/api/longhouse"
echo "Done"
}
case "${1:-}" in
build) build ;;
build-api) build_api ;;
test) run_tests ;;
test-api) test_api ;;
test-web) test_web ;;
run-api) shift; run_api "$@" ;;
run-web) shift; run_web "$@" ;;
dev) shift; dev "$@" ;;
dev-down) dev_down ;;
docker-build) docker_build ;;
clean) clean ;;
check-deps) check_deps ;;
*) usage ;;
esac