(
service_name: str,
timeout_seconds: int,
dry_run: bool,
database_endpoint: tuple[str, int] | None = None,
)
| 132 | |
| 133 | |
| 134 | def ensure_postgres_service( |
| 135 | service_name: str, |
| 136 | timeout_seconds: int, |
| 137 | dry_run: bool, |
| 138 | database_endpoint: tuple[str, int] | None = None, |
| 139 | ) -> None: |
| 140 | if dry_run: |
| 141 | log(f"[dry-run] 将执行: docker compose up -d {service_name}") |
| 142 | log(f"[dry-run] 将等待服务 {service_name} 进入 healthy/running") |
| 143 | return |
| 144 | |
| 145 | try: |
| 146 | run_command(["docker", "compose", "up", "-d", service_name], capture_output=True) |
| 147 | except LaunchError as exc: |
| 148 | if ( |
| 149 | database_endpoint |
| 150 | and "port is already allocated" in str(exc) |
| 151 | and can_connect_tcp(database_endpoint[0], database_endpoint[1]) |
| 152 | ): |
| 153 | log( |
| 154 | f"检测到数据库端口已被占用,但目标数据库已经可直连,沿用现有实例继续启动: {database_endpoint[0]}:{database_endpoint[1]}", |
| 155 | "WARN", |
| 156 | ) |
| 157 | return |
| 158 | raise |
| 159 | |
| 160 | container_id = run_command(["docker", "compose", "ps", "-q", service_name], capture_output=True).stdout.strip() |
| 161 | if not container_id: |
| 162 | raise LaunchError(f"未找到服务 {service_name} 对应的容器,请检查 docker-compose.yml。") |
| 163 | |
| 164 | deadline = time.time() + timeout_seconds |
| 165 | while time.time() < deadline: |
| 166 | status = run_command( |
| 167 | [ |
| 168 | "docker", |
| 169 | "inspect", |
| 170 | "--format", |
| 171 | "{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}", |
| 172 | container_id, |
| 173 | ], |
| 174 | capture_output=True, |
| 175 | ).stdout.strip() |
| 176 | if status in {"healthy", "running"}: |
| 177 | log(f"数据库容器已就绪: {service_name} ({status})") |
| 178 | return |
| 179 | time.sleep(2) |
| 180 | |
| 181 | raise LaunchError(f"等待数据库容器 {service_name} 就绪超时。") |
| 182 | |
| 183 | |
| 184 | def get_display_host(host: str) -> str: |
no test coverage detected