* fix(docker): install docker-ce-cli and compose plugin in image * fix(docker): point DOCKER_HOST to resolvable dind service alias * test(docker): cover docker-ce-cli install and dind DOCKER_HOST fix --------- Co-authored-by: opencode-agent <agent@opencode.local>
181 lines
7.1 KiB
Python
181 lines
7.1 KiB
Python
"""Tests for Dockerfile npm install line.
|
|
|
|
Covers issue #98 acceptance criteria:
|
|
- ``Dockerfile`` no longer installs ``@mathew-cf/opencode-memory`` — the
|
|
plugin was replaced by 5 TS tools (``.opencode/tools/memory-*.ts``) that
|
|
call ``python3 -m src.memory`` directly (PR #101). The npm package is
|
|
dead weight in the image and is removed from the global install step.
|
|
- ``opencode-ai`` and ``repomix`` remain (regression guard — must not be
|
|
dropped when editing the install line).
|
|
|
|
Checks are raw-text grep assertions (no Docker build needed), mirroring the
|
|
belt-and-suspenders style of ``test_docker_compose.py``.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
DOCKERFILE = REPO_ROOT / "Dockerfile"
|
|
|
|
REMOVED_PLUGIN = "@mathew-cf/opencode-memory"
|
|
KEPT_PACKAGES = ("opencode-ai", "repomix")
|
|
OPENCODE_AI = "opencode-ai"
|
|
|
|
|
|
def _dockerfile_text() -> str:
|
|
"""Read Dockerfile text (asserts the file exists)."""
|
|
assert DOCKERFILE.exists(), f"Dockerfile missing: {DOCKERFILE}"
|
|
return DOCKERFILE.read_text()
|
|
|
|
|
|
def _npm_install_lines(text: str) -> list[str]:
|
|
"""Return every ``npm install -g ...`` line from the Dockerfile."""
|
|
return [line for line in text.splitlines() if "npm install -g" in line]
|
|
|
|
|
|
# ── file exists ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_dockerfile_exists():
|
|
"""Dockerfile exists at repo root."""
|
|
assert DOCKERFILE.exists(), f"Dockerfile missing: {DOCKERFILE}"
|
|
|
|
|
|
# ── @mathew-cf/opencode-memory removed (issue #98 — plugin replaced by TS tools)
|
|
|
|
|
|
def test_memory_plugin_removed_from_npm_install():
|
|
"""Dockerfile does NOT install ``@mathew-cf/opencode-memory`` via ``npm install -g``.
|
|
|
|
The plugin was replaced by 5 TS tools (PR #101) that call
|
|
``python3 -m src.memory`` directly — the npm package is no longer needed
|
|
inside the container and is removed from the global install step.
|
|
"""
|
|
lines = _npm_install_lines(_dockerfile_text())
|
|
assert lines, "no `npm install -g` line found in Dockerfile"
|
|
assert not any(REMOVED_PLUGIN in line for line in lines), (
|
|
f"Dockerfile must NOT install {REMOVED_PLUGIN!r} via `npm install -g` — "
|
|
"plugin replaced by TS tools (PR #101, cleanup issue #98)"
|
|
)
|
|
|
|
|
|
def test_memory_plugin_removed_from_npm_install_raw():
|
|
"""Raw text check: the Dockerfile does NOT contain ``@mathew-cf/opencode-memory``.
|
|
|
|
Belt-and-suspenders alongside the parsed check — catches any lingering
|
|
reference to the removed plugin (multi-line install, comment, etc.).
|
|
"""
|
|
assert REMOVED_PLUGIN not in _dockerfile_text(), (
|
|
f"Dockerfile must NOT contain {REMOVED_PLUGIN!r} — plugin replaced by "
|
|
"TS tools (PR #101, cleanup issue #98)"
|
|
)
|
|
|
|
|
|
# ── opencode-ai still present (regression — must not drop the main package) ──
|
|
|
|
|
|
def test_opencode_ai_present():
|
|
"""Dockerfile still installs ``opencode-ai`` (regression guard).
|
|
|
|
Removing the memory plugin from the npm install line must not
|
|
accidentally drop the primary ``opencode-ai`` package.
|
|
"""
|
|
lines = _npm_install_lines(_dockerfile_text())
|
|
assert lines, "no `npm install -g` line found in Dockerfile"
|
|
assert any(OPENCODE_AI in line for line in lines), (
|
|
f"Dockerfile must still install {OPENCODE_AI!r} — dropping it breaks "
|
|
"the container entrypoint"
|
|
)
|
|
|
|
|
|
def test_opencode_ai_present_raw():
|
|
"""Raw text check: the Dockerfile contains ``opencode-ai``."""
|
|
assert OPENCODE_AI in _dockerfile_text(), f"Dockerfile must still contain {OPENCODE_AI!r}"
|
|
|
|
|
|
# ── single npm install line holds the kept packages ──────────────────────────
|
|
|
|
|
|
def test_single_npm_install_line_has_kept_packages():
|
|
"""One ``npm install -g`` line installs all kept packages.
|
|
|
|
Guards against splitting the install across multiple RUN layers (which
|
|
would waste image layers and obscure which package is missing).
|
|
"""
|
|
lines = _npm_install_lines(_dockerfile_text())
|
|
install_lines = [line for line in lines if OPENCODE_AI in line]
|
|
assert install_lines, f"no `npm install -g` line with {OPENCODE_AI!r}"
|
|
main_line = install_lines[0]
|
|
for pkg in KEPT_PACKAGES:
|
|
assert pkg in main_line, (
|
|
f"package {pkg!r} missing from the opencode-ai npm install line: {main_line!r}"
|
|
)
|
|
|
|
|
|
# ── docker CLI install (issue #222 — docker-ce-cli + compose plugin) ─────────
|
|
|
|
|
|
def test_docker_io_removed_from_apt_install():
|
|
"""Dockerfile does NOT install the ``docker.io`` Debian metapackage.
|
|
|
|
The ``docker.io`` package on trixie ships ``dockerd`` + ``docker-proxy`` +
|
|
``docker-init`` but NOT the ``docker`` CLI binary. The opencode container
|
|
delegates to the external DinD service, so running a local daemon is dead
|
|
weight — only the CLI is needed. ``docker-ce-cli`` (official Docker APT
|
|
repo) provides ``/usr/bin/docker`` and matches DinD 29.6.2 (API 1.55).
|
|
"""
|
|
content = _dockerfile_text()
|
|
assert "docker.io" not in content, (
|
|
"Dockerfile must NOT install `docker.io` — trixie ships no `docker` "
|
|
"CLI binary, only the daemon; install `docker-ce-cli` instead (issue #222)"
|
|
)
|
|
|
|
|
|
def test_docker_ce_cli_installed():
|
|
"""Dockerfile installs ``docker-ce-cli`` from the official Docker APT repo.
|
|
|
|
Provides ``/usr/bin/docker`` and matches DinD 29.6.2 (API 1.55). The
|
|
Debian ``docker.io`` package does NOT ship the CLI on trixie.
|
|
"""
|
|
content = _dockerfile_text()
|
|
assert "docker-ce-cli" in content, (
|
|
"Dockerfile must install `docker-ce-cli` so `which docker` resolves to "
|
|
"/usr/bin/docker and `docker version` reaches the DinD server (issue #222)"
|
|
)
|
|
|
|
|
|
def test_docker_compose_plugin_installed():
|
|
"""Dockerfile installs ``docker-compose-plugin`` (Compose v2 subcommand).
|
|
|
|
The old Python ``docker-compose`` (v1) is NOT compatible. ``docker
|
|
compose`` (subcommand) requires the plugin, bundled with the official
|
|
Docker APT repo alongside ``docker-ce-cli``.
|
|
"""
|
|
content = _dockerfile_text()
|
|
assert "docker-compose-plugin" in content, (
|
|
"Dockerfile must install `docker-compose-plugin` so `docker compose "
|
|
"version` prints v2.x (issue #222)"
|
|
)
|
|
|
|
|
|
def test_docker_apt_repo_added():
|
|
"""Dockerfile adds the official Docker APT repository (``download.docker.com``).
|
|
|
|
``docker-ce-cli`` / ``docker-compose-plugin`` are not in the Debian
|
|
trixie default repos — the official Docker repo is required to match the
|
|
DinD version (29.6.2, API 1.55).
|
|
"""
|
|
content = _dockerfile_text()
|
|
assert "download.docker.com/linux/debian" in content, (
|
|
"Dockerfile must add the official Docker APT repo (download.docker.com) "
|
|
"to install docker-ce-cli matching DinD 29.6.2 (issue #222)"
|
|
)
|
|
assert "triex" not in content.replace("trixie", ""), (
|
|
"Dockerfile Docker APT repo must target the trixie suite (matches base image)"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import pytest
|
|
|
|
pytest.main([__file__, "-v"])
|