[Fix] freestyle-mock honors $PORT, drop server.listen string-patch (#1432)

## Summary

The multi-worker freestyle-mock rewrite
([#1430](https://github.com/hexclave/stack-auth/pull/1430)) hardcoded
`server.listen(8080)`, which collides with qstash inside the
local-emulator container. Supervisord sets `PORT=8180` for
freestyle-mock specifically to avoid this clash, but the new source
ignores `process.env.PORT`.

The local-emulator Dockerfile previously bridged this with a
`server.replace('server.listen(8080)', ...)` string-patch on the
embedded source. The new code is `server.listen(8080, () => { ... })` —
the literal `'server.listen(8080)'` substring no longer matches, so the
replace silently no-ops and freestyle-mock binds 8080. qstash then can't
start (`address already in use: 127.0.0.1:8080` → FATAL), the backend
(which depends on qstash) never comes up, and the emulator smoke test
times out.

Observed in [this
run](https://github.com/hexclave/stack-auth/actions/runs/25832479377):

```
smoke-test: FTL address already in use: 127.0.0.1:8080
smoke-test: WARN exited: qstash (exit status 1; not expected)
smoke-test: INFO gave up: qstash entered FATAL state, too many start retries too quickly
[603s] SMOKE TEST FAILED: backend /health?db=1 did not return 200 within 300s
```

## Changes

- `docker/dependencies/freestyle-mock/Dockerfile`: `server.listen(PORT)`
where `PORT = process.env.PORT || 8080`, plus the startup log reflects
the actual port.
- `docker/local-emulator/Dockerfile`: drop the now-redundant
string-replace for the listen call. The two remaining replaces
(`fs/promises` import + node_modules symlink) are unrelated and kept.

## Test plan

- [ ] QEMU emulator build workflow passes on this branch (smoke test
reaches healthy backend).
- [ ] Verify locally that supervisord's `PORT=8180` is honored by
freestyle-mock and qstash binds 8080 cleanly.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
  * Server listening port is now configurable via PORT (default 8080).
* Local emulator startup adjusted to better handle dependencies and
create a node_modules symlink for smoother local runs.
  * Seed/process transaction timeout increased to 90s for reliability.
* Local database statement timeout changed to 0 (no statement timeout).
* **CI**
  * Added step to enable and validate KVM access during emulator builds.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1432)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
BilalG1 2026-05-14 15:31:16 -07:00 committed by GitHub
parent 988505249b
commit 024da3cacb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 21 additions and 8 deletions

View File

@ -243,6 +243,19 @@ jobs:
echo "/opt/qemu/bin" >> "$GITHUB_PATH"
/opt/qemu/bin/qemu-system-x86_64 --version
- name: Enable KVM access
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm || true
ls -la /dev/kvm || echo "no /dev/kvm present"
if [ -w /dev/kvm ]; then
echo "KVM is writable — hardware acceleration will be used"
else
echo "WARNING: /dev/kvm is not writable — will fall back to TCG (very slow)"
fi
- uses: pnpm/action-setup@v4
with:
version: 10.23.0

View File

@ -1636,10 +1636,10 @@ async function seedDummySessionActivityEvents(options: SessionActivityEventSeedO
});
}, {
// Under cross-arch arm64 TCG in the emulator qcow2 build, this batch
// takes ~10s; Prisma's default is 5s. Production (KVM/native) runs it
// in well under 1s, so the looser bound only kicks in when the DB is
// genuinely slow.
timeout: 30_000,
// has been observed to take 40-50s; Prisma's default is 5s. Production
// (KVM/native) runs it in well under 1s, so the looser bound only kicks
// in when the DB is genuinely slow.
timeout: 90_000,
});
if (clickhouseClient && clickhouseRows.length > 0) {

View File

@ -380,8 +380,9 @@ const server = createServer(async (req, res) => {
}
});
server.listen(8080, () => {
console.log(`freestyle-mock listening on :8080 (worker pool size ${POOL_SIZE})`);
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`freestyle-mock listening on :${PORT} (worker pool size ${POOL_SIZE})`);
});
EOF

View File

@ -117,7 +117,6 @@ RUN node -e " \
fs.writeFileSync('package.json', pkgMatch[1]); \
const srvMatch = df.match(/cat <<'EOF' > server\\.mjs\\n([\\s\\S]*?)\\nEOF/); \
let server = srvMatch[1]; \
server = server.replace('server.listen(8080)', 'server.listen(process.env.PORT || 8080)'); \
server = server.replace( \
'from \"fs/promises\"', \
'from \"fs/promises\"; import { symlinkSync } from \"fs\"' \

View File

@ -25,7 +25,7 @@ command=/usr/lib/postgresql/16/bin/postgres
-c max_connections=500
-c shared_preload_libraries=pg_stat_statements
-c pg_stat_statements.track=all
-c statement_timeout=120s
-c statement_timeout=0
user=postgres
autostart=true
autorestart=true