feat: add Podman container-engine support alongside Docker

Goal

Add full Podman support as an alternative container engine alongside Docker, selected via [docker] engine = "podman". glci invokes the podman binary directly (no podman-docker shim) and works with no Docker installed.

How it works

glci drives a container engine by shelling out to its CLI (docker/podman) as a subprocess. This introduces a lightweight abstraction: an Engine value type + a process-wide defaultEngine + an engineCmd helper. The zero value is Docker, so every existing path is unchanged. Podman is CLI- and API-compatible with Docker, so gitlab-runner keeps executor = docker, talking to Podman's Docker-compatible socket mounted at /var/run/docker.sock.

Key divergences handled by the abstraction: the binary name, --context--connection, DOCKER_HOSTCONTAINER_HOST, docker context inspectpodman system connection list, and socket paths (rootless $XDG_RUNTIME_DIR/podman/podman.sock, rootful /run/podman/podman.sock).

Scope

  • Engine corepkg/daemon/engine.go: Engine type, parsers, engineCmd/engineCmdCtx, engineAvailable, ResolveEffectiveEngine (explicit config + docker→podman auto-fallback).
  • DockerHost + package funcs — engine-aware Command/buildEnv (strips both host env vars so none leaks across engines), detectPodmanSocket, InitEngine.
  • Config[docker] engine + per-runner [runners.<name>] engine, with validation and merge. Orthogonal to [defaults] executor, which stays docker under Podman (gitlab-runner has no podman executor).
  • Provisioning/routing — engine resolved once at daemon startup and threaded per runner; the daemon reports its active engine via the health endpoint so CLI inspection commands stay consistent with it.
  • CLIglci doctor reports the active engine and verifies the Podman API socket; inspection commands (ps, system, registry) route through the resolved engine.
  • Documentation — config-files, docker-network, cli-reference, architecture, the glci skill, README, and troubleshooting.

Docker-in-Docker

Rootful Podman DinD works (privileged defaults on); rootless Podman DinD is best-effort — documented as a known limitation.

Verification

  • go build ./..., go vet ./..., make test, go test -race (daemon/cmd/config) — green.
  • make test-e2e-parse, make test-e2e-golden — golden fixtures unchanged (engine is a daemon-execution field, not a pipeline-parse field).
  • CLI integration (doctor/system) — green.
  • The unit job was run locally via glci before pushing — pipeline PASSED.
  • Full fork MR pipeline is green (unit, unit:race, e2e:golden, comprehensive, all integration tiers incl. multi-daemon).

Review

Ran a multi-agent post-implementation review (correctness, performance, security, docs, concurrency, CLI-UX). All findings addressed: CLI↔️daemon engine consistency (via the health endpoint), remote Podman socket resolution, doctor Podman-socket verification, bounded availability-probe timeout, and engine-aware user-facing labels/logs.

Note

Podman was not exercised on a real host in this environment — a smoke test with podman system service (glci doctor → run a simple pipeline → podman ps) is recommended before merge.

Merge request reports

Loading
Loading