From d50a7fd049fb49aca4298126bf8fe54937bc4681 Mon Sep 17 00:00:00 2001 From: hibna Date: Sun, 2 Aug 2026 23:13:55 +0300 Subject: [PATCH] Install the docker CLI in jobs that build images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runner executes jobs inside a container that ships no docker client, so the publish job died on its first command: /var/run/act/workflow/1: line 3: docker: command not found act_runner does mount the host's socket into job containers, so only the client is missing. Both image jobs now fetch the static binary when it is absent and then check that the socket answers, because "no client" and "no daemon" are different problems and the log should say which one it hit. The docker build-test job needed the same treatment — it is skipped on tags, so it had never reached that command either. Verified in a container without a docker client: with the socket mounted the step installs the client and builds the web image; without it the step fails with the runner-configuration message instead of a confusing connection error. --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a570d17..d864566 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,8 @@ env: # Cargo refuses to even parse. Keep this in step with the toolchain # pinned in apps/daemon/Dockerfile. RUST_TOOLCHAIN: "1.97" + # Static client only; the daemon comes from the socket the runner mounts. + DOCKER_CLI_VERSION: "29.7.1" jobs: # --- Lint + TypeScript Check --- @@ -97,6 +99,26 @@ jobs: steps: - uses: actions/checkout@v4 + # The runner executes jobs inside a container that has no docker CLI, + # while act_runner mounts the host's socket at /var/run/docker.sock. + # Install just the client when it is missing, then prove the socket is + # actually reachable — the two failure modes look nothing alike and the + # message should say which one happened. + - name: Ensure docker CLI + run: | + if ! command -v docker >/dev/null 2>&1; then + url="https://download.docker.com/linux/static/stable/$(uname -m)/docker-${DOCKER_CLI_VERSION}.tgz" + if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o /tmp/docker.tgz + else wget -qO /tmp/docker.tgz "$url"; fi + tar -xzf /tmp/docker.tgz -C /usr/local/bin --strip-components=1 docker/docker + fi + docker --version + docker version >/dev/null 2>&1 || { + echo "The docker socket is not reachable from this job." + echo "act_runner must mount it: leave container.docker_host empty in its config.yaml." + exit 1 + } + - name: Build API image run: docker build -f apps/api/Dockerfile -t gamepanel-api:ci . @@ -127,6 +149,26 @@ jobs: steps: - uses: actions/checkout@v4 + # The runner executes jobs inside a container that has no docker CLI, + # while act_runner mounts the host's socket at /var/run/docker.sock. + # Install just the client when it is missing, then prove the socket is + # actually reachable — the two failure modes look nothing alike and the + # message should say which one happened. + - name: Ensure docker CLI + run: | + if ! command -v docker >/dev/null 2>&1; then + url="https://download.docker.com/linux/static/stable/$(uname -m)/docker-${DOCKER_CLI_VERSION}.tgz" + if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o /tmp/docker.tgz + else wget -qO /tmp/docker.tgz "$url"; fi + tar -xzf /tmp/docker.tgz -C /usr/local/bin --strip-components=1 docker/docker + fi + docker --version + docker version >/dev/null 2>&1 || { + echo "The docker socket is not reachable from this job." + echo "act_runner must mount it: leave container.docker_host empty in its config.yaml." + exit 1 + } + - name: Registry login run: | printf '%s' "${{ secrets.REGISTRY_TOKEN }}" |