(composeYaml: string)
| 146 | // of host ports it would publish. Sufficient for our generated compose file; not a |
| 147 | // full YAML parser. |
| 148 | function parsePublishedHostPorts(composeYaml: string): PublishedPort[] { |
| 149 | const ports: PublishedPort[] = []; |
| 150 | let blockIndent = -1; |
| 151 | for (const rawLine of composeYaml.split('\n')) { |
| 152 | const line = rawLine.replace(/\r$/, ''); |
| 153 | if (/^\s*$/.test(line)) { |
| 154 | continue; |
| 155 | } |
| 156 | const indent = line.length - line.trimStart().length; |
| 157 | const portsKey = line.match(/^(\s*)ports:\s*(#.*)?$/); |
| 158 | if (portsKey) { |
| 159 | blockIndent = portsKey[1].length; |
| 160 | continue; |
| 161 | } |
| 162 | if (blockIndent < 0) { |
| 163 | continue; |
| 164 | } |
| 165 | const item = line.match(/^\s*-\s*(.+?)\s*$/); |
| 166 | if (item && indent > blockIndent) { |
| 167 | const parsed = parseHostPortSpec(item[1]); |
| 168 | if (parsed) { |
| 169 | ports.push(parsed); |
| 170 | } |
| 171 | continue; |
| 172 | } |
| 173 | // Any non-list line (a sibling key or dedent) ends the ports block. |
| 174 | blockIndent = -1; |
| 175 | } |
| 176 | const seen = new Set<string>(); |
| 177 | return ports.filter((p) => { |
| 178 | const key = `${p.host}:${p.port}`; |
| 179 | if (seen.has(key)) { |
| 180 | return false; |
| 181 | } |
| 182 | seen.add(key); |
| 183 | return true; |
| 184 | }); |
| 185 | } |
| 186 | |
| 187 | // Authoritatively checks whether a host port is already bound by attempting to bind |
| 188 | // it ourselves — this catches any process (Docker or not, e.g. a local Postgres on |
no test coverage detected