Compare commits

...

5 Commits

Author SHA1 Message Date
George Pickett
7b721ca85e fix: docker setup supports non-local prebuilt images safely (#7986) (thanks @ozbillwang) 2026-02-25 10:54:54 -08:00
Bill Wang
511433fc18 feature/OPENCLAW_IMAGE 2026-02-25 10:53:22 -08:00
Bill Wang
8cd86226a8 Update docker-setup.sh
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-25 10:53:22 -08:00
Bill Wang
fbb3c1aae2 feature/OPENCLAW_IMAGE 2026-02-25 10:53:22 -08:00
Bill Wang
26113f212c feature/OPENCLAW_IMAGE 2026-02-25 10:53:21 -08:00
4 changed files with 90 additions and 7 deletions

View File

@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Docker/Setup: when `OPENCLAW_IMAGE` is set to a non-local image, `docker-setup.sh` now pulls it instead of building locally, and fails fast if `OPENCLAW_DOCKER_APT_PACKAGES` is set in pull mode. (#7986) Thanks @ozbillwang.
- Security/Nextcloud Talk: reject unsigned webhook traffic before full body reads, reducing unauthenticated request-body exposure, with auth-order regression coverage. (#26118) Thanks @bmendonca3.
- Security/Nextcloud Talk: stop treating DM pairing-store entries as group allowlist senders, so group authorization remains bounded to configured group allowlists. (#26116) Thanks @bmendonca3.
- Security/IRC: keep pairing-store approvals DM-only and out of IRC group allowlist authorization, with policy regression tests for allowlist resolution. (#26112) Thanks @bmendonca3.

View File

@@ -247,12 +247,22 @@ upsert_env "$ENV_FILE" \
OPENCLAW_HOME_VOLUME \
OPENCLAW_DOCKER_APT_PACKAGES
echo "==> Building Docker image: $IMAGE_NAME"
docker build \
--build-arg "OPENCLAW_DOCKER_APT_PACKAGES=${OPENCLAW_DOCKER_APT_PACKAGES}" \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/Dockerfile" \
"$ROOT_DIR"
if [[ "$IMAGE_NAME" == "openclaw:local" ]]; then
echo "==> Building Docker image: $IMAGE_NAME"
docker build \
--build-arg "OPENCLAW_DOCKER_APT_PACKAGES=${OPENCLAW_DOCKER_APT_PACKAGES}" \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/Dockerfile" \
"$ROOT_DIR"
else
if [[ -n "$OPENCLAW_DOCKER_APT_PACKAGES" ]]; then
fail "OPENCLAW_DOCKER_APT_PACKAGES is build-only and cannot be used when OPENCLAW_IMAGE is not openclaw:local."
fi
echo "==> Pulling Docker image: $IMAGE_NAME"
if ! docker pull "$IMAGE_NAME"; then
fail "Failed to pull image $IMAGE_NAME. Check the image name and access permissions."
fi
fi
echo ""
echo "==> Onboarding (interactive)"

View File

@@ -40,7 +40,7 @@ From repo root:
This script:
- builds the gateway image
- builds the gateway image (default), or pulls a prebuilt image when `OPENCLAW_IMAGE` is set to a non-local image
- runs the onboarding wizard
- prints optional provider setup hints
- starts the gateway via Docker Compose
@@ -91,6 +91,21 @@ docker compose run --rm openclaw-cli onboard
docker compose up -d openclaw-gateway
```
### Use a prebuilt image (optional)
To skip local builds and use a prebuilt image, set `OPENCLAW_IMAGE` to any image
other than `openclaw:local` before running `docker-setup.sh`:
```bash
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:main"
./docker-setup.sh
```
Notes:
- `OPENCLAW_DOCKER_APT_PACKAGES` is build-only and cannot be combined with
non-local `OPENCLAW_IMAGE` values.
Note: run `docker compose ...` from the repo root. If you enabled
`OPENCLAW_EXTRA_MOUNTS` or `OPENCLAW_HOME_VOLUME`, the setup script writes
`docker-compose.extra.yml`; include it when running Compose elsewhere:
@@ -181,6 +196,7 @@ export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential"
Notes:
- This accepts a space-separated list of apt package names.
- This only works when `OPENCLAW_IMAGE=openclaw:local`.
- If you change `OPENCLAW_DOCKER_APT_PACKAGES`, rerun `docker-setup.sh` to rebuild
the image.

View File

@@ -25,6 +25,13 @@ if [[ "\${1:-}" == "build" ]]; then
echo "build $*" >>"$log"
exit 0
fi
if [[ "\${1:-}" == "pull" ]]; then
echo "pull $*" >>"$log"
if [[ "\${DOCKER_STUB_FAIL_PULL:-0}" == "1" ]]; then
exit 1
fi
exit 0
fi
if [[ "\${1:-}" == "compose" ]]; then
echo "compose $*" >>"$log"
exit 0
@@ -103,6 +110,10 @@ function runDockerSetup(
});
}
async function clearDockerLog(sandbox: DockerSetupSandbox) {
await writeFile(sandbox.logPath, "");
}
function resolveBashForCompatCheck(): string | null {
for (const candidate of ["/bin/bash", "bash"]) {
const probe = spawnSync(candidate, ["-c", "exit 0"], { encoding: "utf8" });
@@ -131,6 +142,7 @@ describe("docker-setup.sh", () => {
it("handles env defaults, home-volume mounts, and apt build args", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_DOCKER_APT_PACKAGES: "ffmpeg build-essential",
@@ -155,6 +167,7 @@ describe("docker-setup.sh", () => {
it("precreates config identity dir for CLI device auth writes", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const configDir = join(activeSandbox.rootDir, "config-identity");
const workspaceDir = join(activeSandbox.rootDir, "workspace-identity");
@@ -170,6 +183,7 @@ describe("docker-setup.sh", () => {
it("rejects injected multiline OPENCLAW_EXTRA_MOUNTS values", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_EXTRA_MOUNTS: "/tmp:/tmp\n evil-service:\n image: alpine",
@@ -181,6 +195,7 @@ describe("docker-setup.sh", () => {
it("rejects invalid OPENCLAW_EXTRA_MOUNTS mount format", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_EXTRA_MOUNTS: "bad mount spec",
@@ -192,6 +207,7 @@ describe("docker-setup.sh", () => {
it("rejects invalid OPENCLAW_HOME_VOLUME names", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_HOME_VOLUME: "bad name",
@@ -232,4 +248,44 @@ describe("docker-setup.sh", () => {
expect(compose).not.toContain("gateway-daemon");
expect(compose).toContain('"gateway"');
});
it("pulls non-local OPENCLAW_IMAGE values instead of building locally", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_IMAGE: "ghcr.io/openclaw/openclaw:main",
});
expect(result.status).toBe(0);
const log = await readFile(activeSandbox.logPath, "utf8");
expect(log).toContain("pull pull ghcr.io/openclaw/openclaw:main");
expect(log).not.toContain("build build");
});
it("fails fast when OPENCLAW_DOCKER_APT_PACKAGES is set with non-local OPENCLAW_IMAGE", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_IMAGE: "ghcr.io/openclaw/openclaw:main",
OPENCLAW_DOCKER_APT_PACKAGES: "ffmpeg",
});
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("OPENCLAW_DOCKER_APT_PACKAGES is build-only");
});
it("fails fast when docker pull fails", async () => {
const activeSandbox = requireSandbox(sandbox);
await clearDockerLog(activeSandbox);
const result = runDockerSetup(activeSandbox, {
OPENCLAW_IMAGE: "ghcr.io/openclaw/openclaw:missing",
DOCKER_STUB_FAIL_PULL: "1",
});
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("Failed to pull image ghcr.io/openclaw/openclaw:missing");
});
});