Skip to content

Commit 64ff87f

Browse files
committed
test(e2e): avoid random port collisions
1 parent 4b7b10a commit 64ff87f

7 files changed

Lines changed: 63 additions & 8 deletions

File tree

scripts/e2e/_lib.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,59 @@ dg_ensure_node_gyp() {
134134
export PATH="$node_gyp_bin:$PATH"
135135
}
136136

137+
dg_pick_free_port() {
138+
local first_port="$1"
139+
local last_port="$2"
140+
local host="${3:-127.0.0.1}"
141+
142+
if ! command -v node >/dev/null 2>&1; then
143+
echo "e2e: node is required to pick a free TCP port" >&2
144+
return 1
145+
fi
146+
147+
node - "$first_port" "$last_port" "$host" <<'NODE'
148+
const net = require("node:net")
149+
150+
const [firstRaw, lastRaw, host] = process.argv.slice(2)
151+
const first = Number.parseInt(firstRaw, 10)
152+
const last = Number.parseInt(lastRaw, 10)
153+
154+
if (!Number.isInteger(first) || !Number.isInteger(last) || first < 1 || last > 65535 || first > last) {
155+
console.error(`e2e: invalid port range: ${firstRaw}-${lastRaw}`)
156+
process.exit(1)
157+
}
158+
159+
const canListen = (port) =>
160+
new Promise((resolve) => {
161+
const server = net.createServer()
162+
server.unref()
163+
server.once("error", () => resolve(false))
164+
server.listen({ host, port, exclusive: true }, () => {
165+
server.close(() => resolve(true))
166+
})
167+
})
168+
169+
;(async () => {
170+
const count = last - first + 1
171+
const start = Math.floor(Math.random() * count)
172+
173+
for (let offset = 0; offset < count; offset += 1) {
174+
const port = first + ((start + offset) % count)
175+
if (await canListen(port)) {
176+
console.log(port)
177+
return
178+
}
179+
}
180+
181+
console.error(`e2e: no free TCP port on ${host} in range ${first}-${last}`)
182+
process.exit(1)
183+
})().catch((error) => {
184+
console.error(error instanceof Error ? error.message : String(error))
185+
process.exit(1)
186+
})
187+
NODE
188+
}
189+
137190
dg_controller_container_name() {
138191
printf '%s\n' "${DOCKER_GIT_API_CONTAINER_NAME:-docker-git-api}"
139192
}

scripts/e2e/browser-command.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ BROWSER_STARTUP_ATTEMPTS="${DOCKER_GIT_E2E_BROWSER_STARTUP_ATTEMPTS:-240}"
2121
export DOCKER_GIT_PROJECTS_ROOT="$ROOT"
2222
export DOCKER_GIT_PROJECTS_ROOT_VOLUME="docker-git-e2e-browser-$RUN_ID-projects"
2323
export DOCKER_GIT_API_CONTAINER_NAME="docker-git-e2e-browser-$RUN_ID-api"
24-
export DOCKER_GIT_API_PORT="$(( (RANDOM % 1000) + 34000 ))"
25-
export DOCKER_GIT_WEB_PORT="$(( (RANDOM % 1000) + 41000 ))"
24+
export DOCKER_GIT_API_PORT="$(dg_pick_free_port 34000 34999)"
25+
export DOCKER_GIT_WEB_PORT="$(dg_pick_free_port 41000 41999)"
2626
export COMPOSE_PROJECT_NAME="docker-git-e2e-browser-$RUN_ID"
2727
export DOCKER_GIT_STATE_AUTO_SYNC=0
2828

scripts/e2e/clone-auto-open-ssh.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ OUT_DIR="$ROOT/e2e/clone-auto-open-ssh-$RUN_ID"
2828
CONTAINER_NAME="dg-e2e-clone-auto-ssh-$RUN_ID"
2929
SERVICE_NAME="dg-e2e-clone-auto-ssh-$RUN_ID"
3030
VOLUME_NAME="dg-e2e-clone-auto-ssh-$RUN_ID-home"
31-
SSH_PORT="$(( (RANDOM % 1000) + 24000 ))"
31+
SSH_PORT="$(dg_pick_free_port 24000 24999)"
3232
SSH_KEY="$ROOT/dev_ssh_key"
3333
SSH_PUB_KEY="$ROOT/dev_ssh_key.pub"
3434
CLONE_LOG="$ROOT/clone-auto-open-ssh.log"

scripts/e2e/clone-cache.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ run_clone_case() {
126126
local container_name="dg-e2e-cache-${case_name}-${RUN_ID}"
127127
local service_name="dg-e2e-cache-${case_name}-${RUN_ID}"
128128
local volume_name="dg-e2e-cache-${case_name}-${RUN_ID}-home"
129-
local ssh_port="$(( (RANDOM % 1000) + 22000 ))"
129+
local ssh_port
130+
ssh_port="$(dg_pick_free_port 22000 22999)"
130131
local log_path="$ROOT/clone-cache-${case_name}.log"
131132
local host_log_path="$ROOT/clone-cache-${case_name}-host.log"
132133

scripts/e2e/login-context.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ run_case() {
8585
local container_name="dg-e2e-login-${case_name}-${RUN_ID}"
8686
local service_name="dg-e2e-login-${case_name}-${RUN_ID}"
8787
local volume_name="dg-e2e-login-${case_name}-${RUN_ID}-home"
88-
local ssh_port="$(( (RANDOM % 1000) + 21000 ))"
88+
local ssh_port
89+
ssh_port="$(dg_pick_free_port 21000 21999)"
8990
local login_log="/tmp/docker-git-login-context-${RUN_ID}-${case_name}.log"
9091
local probe_script="$ROOT/login-context-${case_name}-probe.sh"
9192

scripts/e2e/opencode-autoconnect.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ OUT_DIR="$ROOT/e2e/opencode-autoconnect-$RUN_ID"
1919
CONTAINER_NAME="dg-e2e-opencode-$RUN_ID"
2020
SERVICE_NAME="dg-e2e-opencode-$RUN_ID"
2121
VOLUME_NAME="dg-e2e-opencode-$RUN_ID-home"
22-
SSH_PORT="$(( (RANDOM % 1000) + 20000 ))"
22+
SSH_PORT="$(dg_pick_free_port 20000 20999)"
2323

2424
export DOCKER_GIT_PROJECTS_ROOT="$ROOT"
2525
export DOCKER_GIT_PROJECTS_ROOT_VOLUME="docker-git-e2e-opencode-$RUN_ID-projects"
2626
export DOCKER_GIT_API_CONTAINER_NAME="docker-git-e2e-opencode-$RUN_ID-api"
27-
export DOCKER_GIT_API_PORT="$(( (RANDOM % 1000) + 34000 ))"
27+
export DOCKER_GIT_API_PORT="$(dg_pick_free_port 34000 34999)"
2828
export COMPOSE_PROJECT_NAME="docker-git-e2e-opencode-$RUN_ID"
2929
export DOCKER_GIT_STATE_AUTO_SYNC=0
3030

scripts/e2e/runtime-volumes-ssh.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ OUT_DIR="$ROOT/e2e/runtime-volumes-ssh-$RUN_ID"
2828
CONTAINER_NAME="dg-e2e-runtime-$RUN_ID"
2929
SERVICE_NAME="dg-e2e-runtime-$RUN_ID"
3030
VOLUME_NAME="dg-e2e-runtime-$RUN_ID-home"
31-
SSH_PORT="$(( (RANDOM % 1000) + 23000 ))"
31+
SSH_PORT="$(dg_pick_free_port 23000 23999)"
3232
SSH_KEY="$ROOT/dev_ssh_key"
3333
SSH_PUB_KEY="$ROOT/dev_ssh_key.pub"
3434
CLONE_LOG="$ROOT/clone.log"

0 commit comments

Comments
 (0)