| 188 | // it ourselves — this catches any process (Docker or not, e.g. a local Postgres on |
| 189 | // 5432), which is the actual failure mode `docker compose up` hits. |
| 190 | function isPortInUse({ host, port }: PublishedPort): Promise<boolean> { |
| 191 | return new Promise((resolve) => { |
| 192 | const server = net.createServer(); |
| 193 | server.once('error', (err: NodeJS.ErrnoException) => { |
| 194 | server.close(() => { /* noop */ }); |
| 195 | // EADDRINUSE = taken. Other errors (e.g. EACCES on privileged ports) aren't |
| 196 | // a "someone else has it" conflict we can meaningfully report, so treat as free. |
| 197 | resolve(err.code === 'EADDRINUSE'); |
| 198 | }); |
| 199 | server.once('listening', () => { |
| 200 | server.close(() => resolve(false)); |
| 201 | }); |
| 202 | if (host === '0.0.0.0') { |
| 203 | server.listen(port); |
| 204 | } else { |
| 205 | server.listen(port, host); |
| 206 | } |
| 207 | }); |
| 208 | } |
| 209 | |
| 210 | // Best-effort: maps a host port to the running Docker container(s) publishing it, so |
| 211 | // a conflict can name the offender. Returns an empty map if Docker isn't available. |