fix: replace abandoned docker-autocompose with inline Python script
docker-autocompose 1.0.1 uses distutils (removed in Python 3.12) and the old docker SDK (docker.Client). Replace with a small inline script using docker>=7.0 + pyyaml, run via uv --with inline dependencies. Uses com.docker.compose.service label for correct service names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
88
bootstrap.sh
88
bootstrap.sh
@@ -45,6 +45,92 @@ command -v docker >/dev/null 2>&1 || { echo "ERROR: docker not found"; exit 1; }
|
|||||||
|
|
||||||
[ -f "${UV}" ] || { echo "ERROR: uv not found at ${UV}. Run setup.sh first."; exit 1; }
|
[ -f "${UV}" ] || { echo "ERROR: uv not found at ${UV}. Run setup.sh first."; exit 1; }
|
||||||
|
|
||||||
|
# ── Inline autocompose script (replaces abandoned docker-autocompose package) ──
|
||||||
|
# Uses modern docker SDK. Written to /tmp, run via uv (fetches deps on demand).
|
||||||
|
|
||||||
|
cat > /tmp/server-infra-autocompose.py << 'PYEOF'
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.10"
|
||||||
|
# dependencies = ["docker>=7.0", "pyyaml>=6.0"]
|
||||||
|
# ///
|
||||||
|
"""Generate a docker-compose services block from running containers."""
|
||||||
|
import sys
|
||||||
|
import docker
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def container_to_service(container):
|
||||||
|
attrs = container.attrs
|
||||||
|
config = attrs["Config"]
|
||||||
|
host_config = attrs["HostConfig"]
|
||||||
|
|
||||||
|
service = {"image": config["Image"]}
|
||||||
|
|
||||||
|
restart = (host_config.get("RestartPolicy") or {}).get("Name", "no")
|
||||||
|
if restart and restart not in ("no", ""):
|
||||||
|
service["restart"] = restart
|
||||||
|
|
||||||
|
env = config.get("Env") or []
|
||||||
|
if env:
|
||||||
|
service["environment"] = env
|
||||||
|
|
||||||
|
port_bindings = host_config.get("PortBindings") or {}
|
||||||
|
if port_bindings:
|
||||||
|
ports = []
|
||||||
|
for container_port, bindings in port_bindings.items():
|
||||||
|
for binding in (bindings or []):
|
||||||
|
host_ip = binding.get("HostIp", "")
|
||||||
|
host_port = binding.get("HostPort", "")
|
||||||
|
if host_ip and host_ip not in ("0.0.0.0", "::"):
|
||||||
|
ports.append(f"{host_ip}:{host_port}:{container_port}")
|
||||||
|
elif host_port:
|
||||||
|
ports.append(f"{host_port}:{container_port}")
|
||||||
|
else:
|
||||||
|
ports.append(container_port)
|
||||||
|
if ports:
|
||||||
|
service["ports"] = ports
|
||||||
|
|
||||||
|
volumes = []
|
||||||
|
for mount in attrs.get("Mounts") or []:
|
||||||
|
dst = mount["Destination"]
|
||||||
|
if mount["Type"] == "bind":
|
||||||
|
src = mount["Source"]
|
||||||
|
mode = mount.get("Mode", "rw")
|
||||||
|
volumes.append(f"{src}:{dst}" if mode == "rw" else f"{src}:{dst}:{mode}")
|
||||||
|
elif mount["Type"] == "volume":
|
||||||
|
name = mount.get("Name", "")
|
||||||
|
if name:
|
||||||
|
volumes.append(f"{name}:{dst}")
|
||||||
|
if volumes:
|
||||||
|
service["volumes"] = volumes
|
||||||
|
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: autocompose.py <container_name> [...]", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
services = {}
|
||||||
|
|
||||||
|
for name in sys.argv[1:]:
|
||||||
|
try:
|
||||||
|
container = client.containers.get(name)
|
||||||
|
labels = (container.attrs["Config"].get("Labels") or {})
|
||||||
|
service_name = labels.get("com.docker.compose.service", name)
|
||||||
|
services[service_name] = container_to_service(container)
|
||||||
|
except docker.errors.NotFound:
|
||||||
|
print(f"WARNING: Container {name} not found", file=sys.stderr)
|
||||||
|
|
||||||
|
print(yaml.dump({"services": services}, default_flow_style=False, sort_keys=False))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
PYEOF
|
||||||
|
|
||||||
# ── Recover compose files from running containers ──────────────────────────────
|
# ── Recover compose files from running containers ──────────────────────────────
|
||||||
|
|
||||||
recover_project() {
|
recover_project() {
|
||||||
@@ -67,7 +153,7 @@ recover_project() {
|
|||||||
|
|
||||||
log "Recovering: ${containers[*]}"
|
log "Recovering: ${containers[*]}"
|
||||||
sudo -u "${SERVICE_USER}" \
|
sudo -u "${SERVICE_USER}" \
|
||||||
"/home/${SERVICE_USER}/.local/bin/uvx" --from docker-autocompose autocompose "${containers[@]}" > "${outfile}"
|
"${UV}" run /tmp/server-infra-autocompose.py "${containers[@]}" > "${outfile}"
|
||||||
log " Saved to ${outfile}"
|
log " Saved to ${outfile}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user