feat(dev): add local dockerized ci workflow (#342)
This commit is contained in:
parent
f2c73bacf8
commit
b61d33aa1c
7 changed files with 259 additions and 17 deletions
|
|
@ -14,14 +14,18 @@ RUN apt-get update && apt-get install -y \
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
# Create dummy main.rs to build dependencies
|
# Create dummy main.rs to build dependencies
|
||||||
RUN mkdir src && echo "fn main() {}" > src/main.rs
|
RUN mkdir src && echo "fn main() {}" > src/main.rs
|
||||||
RUN cargo build --release --locked
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/git \
|
||||||
|
cargo build --release --locked
|
||||||
RUN rm -rf src
|
RUN rm -rf src
|
||||||
|
|
||||||
# 2. Copy source code
|
# 2. Copy source code
|
||||||
COPY . .
|
COPY . .
|
||||||
# Touch main.rs to force rebuild
|
# Touch main.rs to force rebuild
|
||||||
RUN touch src/main.rs
|
RUN touch src/main.rs
|
||||||
RUN cargo build --release --locked && \
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/git \
|
||||||
|
cargo build --release --locked && \
|
||||||
strip target/release/zeroclaw
|
strip target/release/zeroclaw
|
||||||
|
|
||||||
# ── Stage 2: Permissions & Config Prep ───────────────────────
|
# ── Stage 2: Permissions & Config Prep ───────────────────────
|
||||||
|
|
|
||||||
108
dev/README.md
108
dev/README.md
|
|
@ -5,13 +5,13 @@ A fully containerized development sandbox for ZeroClaw agents. This environment
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
- **`agent/`**: (Merged into root Dockerfile)
|
- **`agent/`**: (Merged into root Dockerfile)
|
||||||
- The development image is built from the root `Dockerfile` using the `dev` stage (`target: dev`).
|
- The development image is built from the root `Dockerfile` using the `dev` stage (`target: dev`).
|
||||||
- Based on `debian:bookworm-slim` (unlike production `distroless`).
|
- Based on `debian:bookworm-slim` (unlike production `distroless`).
|
||||||
- Includes `bash`, `curl`, and debug tools.
|
- Includes `bash`, `curl`, and debug tools.
|
||||||
- **`sandbox/`**: Dockerfile for the simulated user environment.
|
- **`sandbox/`**: Dockerfile for the simulated user environment.
|
||||||
- Based on `ubuntu:22.04`.
|
- Based on `ubuntu:22.04`.
|
||||||
- Pre-loaded with `git`, `python3`, `nodejs`, `npm`, `gcc`, `make`.
|
- Pre-loaded with `git`, `python3`, `nodejs`, `npm`, `gcc`, `make`.
|
||||||
- Simulates a real developer machine.
|
- Simulates a real developer machine.
|
||||||
- **`docker-compose.yml`**: Defines the services and `dev-net` network.
|
- **`docker-compose.yml`**: Defines the services and `dev-net` network.
|
||||||
- **`cli.sh`**: Helper script to manage the lifecycle.
|
- **`cli.sh`**: Helper script to manage the lifecycle.
|
||||||
|
|
||||||
|
|
@ -20,42 +20,53 @@ A fully containerized development sandbox for ZeroClaw agents. This environment
|
||||||
Run all commands from the repository root using the helper script:
|
Run all commands from the repository root using the helper script:
|
||||||
|
|
||||||
### 1. Start Environment
|
### 1. Start Environment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh up
|
./dev/cli.sh up
|
||||||
```
|
```
|
||||||
|
|
||||||
Builds the agent from source and starts both containers.
|
Builds the agent from source and starts both containers.
|
||||||
|
|
||||||
### 2. Enter Agent Container (`zeroclaw-dev`)
|
### 2. Enter Agent Container (`zeroclaw-dev`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh agent
|
./dev/cli.sh agent
|
||||||
```
|
```
|
||||||
|
|
||||||
Use this to run `zeroclaw` CLI commands manually, debug the binary, or check logs internally.
|
Use this to run `zeroclaw` CLI commands manually, debug the binary, or check logs internally.
|
||||||
|
|
||||||
- **Path**: `/zeroclaw-data`
|
- **Path**: `/zeroclaw-data`
|
||||||
- **User**: `nobody` (65534)
|
- **User**: `nobody` (65534)
|
||||||
|
|
||||||
### 3. Enter Sandbox (`sandbox`)
|
### 3. Enter Sandbox (`sandbox`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh shell
|
./dev/cli.sh shell
|
||||||
```
|
```
|
||||||
|
|
||||||
Use this to act as the "user" or "environment" the agent interacts with.
|
Use this to act as the "user" or "environment" the agent interacts with.
|
||||||
|
|
||||||
- **Path**: `/home/developer/workspace`
|
- **Path**: `/home/developer/workspace`
|
||||||
- **User**: `developer` (sudo-enabled)
|
- **User**: `developer` (sudo-enabled)
|
||||||
|
|
||||||
### 4. Development Cycle
|
### 4. Development Cycle
|
||||||
|
|
||||||
1. Make changes to Rust code in `src/`.
|
1. Make changes to Rust code in `src/`.
|
||||||
2. Rebuild the agent:
|
2. Rebuild the agent:
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh build
|
./dev/cli.sh build
|
||||||
```
|
```
|
||||||
3. Test changes inside the container:
|
3. Test changes inside the container:
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh agent
|
./dev/cli.sh agent
|
||||||
# inside container:
|
# inside container:
|
||||||
zeroclaw --version
|
zeroclaw --version
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Persistence & Shared Workspace
|
### 5. Persistence & Shared Workspace
|
||||||
|
|
||||||
The local `playground/` directory (in repo root) is mounted as the shared workspace:
|
The local `playground/` directory (in repo root) is mounted as the shared workspace:
|
||||||
|
|
||||||
- **Agent**: `/zeroclaw-data/workspace`
|
- **Agent**: `/zeroclaw-data/workspace`
|
||||||
- **Sandbox**: `/home/developer/workspace`
|
- **Sandbox**: `/home/developer/workspace`
|
||||||
|
|
||||||
|
|
@ -64,8 +75,77 @@ Files created by the agent are visible to the sandbox user, and vice versa.
|
||||||
The agent configuration lives in `target/.zeroclaw` (mounted to `/zeroclaw-data/.zeroclaw`), so settings persist across container rebuilds.
|
The agent configuration lives in `target/.zeroclaw` (mounted to `/zeroclaw-data/.zeroclaw`), so settings persist across container rebuilds.
|
||||||
|
|
||||||
### 6. Cleanup
|
### 6. Cleanup
|
||||||
|
|
||||||
Stop containers and remove volumes and generated config:
|
Stop containers and remove volumes and generated config:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dev/cli.sh clean
|
./dev/cli.sh clean
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** This removes `target/.zeroclaw` (config/DB) but leaves the `playground/` directory intact. To fully wipe everything, manually delete `playground/`.
|
**Note:** This removes `target/.zeroclaw` (config/DB) but leaves the `playground/` directory intact. To fully wipe everything, manually delete `playground/`.
|
||||||
|
|
||||||
|
## Local CI/CD (Docker-Only)
|
||||||
|
|
||||||
|
Use this when you want CI-style validation without relying on GitHub Actions and without running Rust toolchain commands on your host.
|
||||||
|
|
||||||
|
### 1. Build the local CI image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./dev/ci.sh build-image
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Run full local CI pipeline
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./dev/ci.sh all
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs inside a container:
|
||||||
|
|
||||||
|
- `cargo fmt --all -- --check`
|
||||||
|
- `cargo clippy --locked --all-targets -- -D clippy::correctness`
|
||||||
|
- `cargo test --locked --verbose`
|
||||||
|
- `cargo build --release --locked --verbose`
|
||||||
|
- `cargo deny check licenses sources`
|
||||||
|
- `cargo audit`
|
||||||
|
- Docker smoke build (`docker build --target dev ...` + `--version` check)
|
||||||
|
|
||||||
|
### 3. Run targeted stages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./dev/ci.sh lint
|
||||||
|
./dev/ci.sh test
|
||||||
|
./dev/ci.sh build
|
||||||
|
./dev/ci.sh deny
|
||||||
|
./dev/ci.sh audit
|
||||||
|
./dev/ci.sh security
|
||||||
|
./dev/ci.sh docker-smoke
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: local `deny` focuses on license/source policy; advisory scanning is handled by `audit`.
|
||||||
|
|
||||||
|
### 4. Enter CI container shell
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./dev/ci.sh shell
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Optional shortcut via existing dev CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh ci
|
||||||
|
./dev/cli.sh ci lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Isolation model
|
||||||
|
|
||||||
|
- Rust compilation, tests, and audit/deny tools run in `zeroclaw-local-ci` container.
|
||||||
|
- Your host filesystem is mounted at `/workspace`; no host Rust toolchain is required.
|
||||||
|
- Cargo build artifacts are written to container volume `/ci-target` (not your host `target/`).
|
||||||
|
- Docker smoke stage uses your Docker daemon to build image layers, but build steps execute in containers.
|
||||||
|
|
||||||
|
### Build cache notes
|
||||||
|
|
||||||
|
- Both `Dockerfile` and `dev/ci/Dockerfile` use BuildKit cache mounts for Cargo registry/git data.
|
||||||
|
- Local CI reuses named Docker volumes for Cargo registry/git and target outputs.
|
||||||
|
- The CI image keeps Rust toolchain defaults from `rust:1.92-slim` (no custom `CARGO_HOME`/`RUSTUP_HOME` overrides), preventing repeated toolchain bootstrapping on each run.
|
||||||
|
|
|
||||||
103
dev/ci.sh
Executable file
103
dev/ci.sh
Executable file
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ -f "dev/docker-compose.ci.yml" ]; then
|
||||||
|
COMPOSE_FILE="dev/docker-compose.ci.yml"
|
||||||
|
elif [ -f "docker-compose.ci.yml" ] && [ "$(basename "$(pwd)")" = "dev" ]; then
|
||||||
|
COMPOSE_FILE="docker-compose.ci.yml"
|
||||||
|
else
|
||||||
|
echo "❌ Run this script from repo root or dev/ directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_cmd=(docker compose -f "$COMPOSE_FILE")
|
||||||
|
|
||||||
|
run_in_ci() {
|
||||||
|
local cmd="$1"
|
||||||
|
"${compose_cmd[@]}" run --rm local-ci bash -c "$cmd"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
cat <<'EOF'
|
||||||
|
ZeroClaw Local CI in Docker
|
||||||
|
|
||||||
|
Usage: ./dev/ci.sh <command>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
build-image Build/update the local CI image
|
||||||
|
shell Open an interactive shell inside the CI container
|
||||||
|
lint Run rustfmt + clippy (container only)
|
||||||
|
test Run cargo test (container only)
|
||||||
|
build Run release build smoke check (container only)
|
||||||
|
audit Run cargo audit (container only)
|
||||||
|
deny Run cargo deny check (container only)
|
||||||
|
security Run cargo audit + cargo deny (container only)
|
||||||
|
docker-smoke Build and verify runtime image (host docker daemon)
|
||||||
|
all Run lint, test, build, security, docker-smoke
|
||||||
|
clean Remove local CI containers and volumes
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
build-image)
|
||||||
|
"${compose_cmd[@]}" build local-ci
|
||||||
|
;;
|
||||||
|
|
||||||
|
shell)
|
||||||
|
"${compose_cmd[@]}" run --rm local-ci bash
|
||||||
|
;;
|
||||||
|
|
||||||
|
lint)
|
||||||
|
run_in_ci "cargo fmt --all -- --check && cargo clippy --locked --all-targets -- -D clippy::correctness"
|
||||||
|
;;
|
||||||
|
|
||||||
|
test)
|
||||||
|
run_in_ci "cargo test --locked --verbose"
|
||||||
|
;;
|
||||||
|
|
||||||
|
build)
|
||||||
|
run_in_ci "cargo build --release --locked --verbose"
|
||||||
|
;;
|
||||||
|
|
||||||
|
audit)
|
||||||
|
run_in_ci "cargo audit"
|
||||||
|
;;
|
||||||
|
|
||||||
|
deny)
|
||||||
|
run_in_ci "cargo deny check licenses sources"
|
||||||
|
;;
|
||||||
|
|
||||||
|
security)
|
||||||
|
run_in_ci "cargo deny check licenses sources"
|
||||||
|
run_in_ci "cargo audit"
|
||||||
|
;;
|
||||||
|
|
||||||
|
docker-smoke)
|
||||||
|
docker build --target dev -t zeroclaw-local-smoke:latest .
|
||||||
|
docker run --rm zeroclaw-local-smoke:latest --version
|
||||||
|
;;
|
||||||
|
|
||||||
|
all)
|
||||||
|
run_in_ci "cargo fmt --all -- --check && cargo clippy --locked --all-targets -- -D clippy::correctness"
|
||||||
|
run_in_ci "cargo test --locked --verbose"
|
||||||
|
run_in_ci "cargo build --release --locked --verbose"
|
||||||
|
run_in_ci "cargo deny check licenses sources"
|
||||||
|
run_in_ci "cargo audit"
|
||||||
|
docker build --target dev -t zeroclaw-local-smoke:latest .
|
||||||
|
docker run --rm zeroclaw-local-smoke:latest --version
|
||||||
|
;;
|
||||||
|
|
||||||
|
clean)
|
||||||
|
"${compose_cmd[@]}" down -v --remove-orphans
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
22
dev/ci/Dockerfile
Normal file
22
dev/ci/Dockerfile
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
FROM rust:1.92-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN rustup toolchain install 1.92 --profile minimal --component rustfmt --component clippy
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/git \
|
||||||
|
cargo install --locked cargo-audit && \
|
||||||
|
cargo install --locked cargo-deny --version 0.18.5
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
CMD ["bash"]
|
||||||
10
dev/cli.sh
10
dev/cli.sh
|
|
@ -46,6 +46,7 @@ function print_help {
|
||||||
echo -e " ${GREEN}agent${NC} Enter Agent (ZeroClaw CLI)"
|
echo -e " ${GREEN}agent${NC} Enter Agent (ZeroClaw CLI)"
|
||||||
echo -e " ${GREEN}logs${NC} View logs"
|
echo -e " ${GREEN}logs${NC} View logs"
|
||||||
echo -e " ${GREEN}build${NC} Rebuild images"
|
echo -e " ${GREEN}build${NC} Rebuild images"
|
||||||
|
echo -e " ${GREEN}ci${NC} Run local CI checks in Docker (see ./dev/ci.sh)"
|
||||||
echo -e " ${GREEN}clean${NC} Stop and wipe workspace data"
|
echo -e " ${GREEN}clean${NC} Stop and wipe workspace data"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +95,15 @@ case "$1" in
|
||||||
echo -e "${GREEN}✅ Rebuild complete.${NC}"
|
echo -e "${GREEN}✅ Rebuild complete.${NC}"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
ci)
|
||||||
|
shift
|
||||||
|
if [ "$BASE_DIR" = "." ]; then
|
||||||
|
./ci.sh "${@:-all}"
|
||||||
|
else
|
||||||
|
./dev/ci.sh "${@:-all}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
clean)
|
clean)
|
||||||
echo -e "${RED}⚠️ WARNING: This will delete 'target/.zeroclaw' data and Docker volumes.${NC}"
|
echo -e "${RED}⚠️ WARNING: This will delete 'target/.zeroclaw' data and Docker volumes.${NC}"
|
||||||
read -p "Are you sure? (y/N) " -n 1 -r
|
read -p "Are you sure? (y/N) " -n 1 -r
|
||||||
|
|
|
||||||
23
dev/docker-compose.ci.yml
Normal file
23
dev/docker-compose.ci.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
name: zeroclaw-local-ci
|
||||||
|
|
||||||
|
services:
|
||||||
|
local-ci:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: dev/ci/Dockerfile
|
||||||
|
container_name: zeroclaw-local-ci
|
||||||
|
working_dir: /workspace
|
||||||
|
environment:
|
||||||
|
- CARGO_TERM_COLOR=always
|
||||||
|
- PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
- CARGO_TARGET_DIR=/ci-target
|
||||||
|
volumes:
|
||||||
|
- ..:/workspace
|
||||||
|
- cargo-registry:/usr/local/cargo/registry
|
||||||
|
- cargo-git:/usr/local/cargo/git
|
||||||
|
- ci-target:/ci-target
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
cargo-registry:
|
||||||
|
cargo-git:
|
||||||
|
ci-target:
|
||||||
|
|
@ -2,7 +2,6 @@ use super::traits::{Tool, ToolResult};
|
||||||
use crate::security::{AutonomyLevel, SecurityPolicy};
|
use crate::security::{AutonomyLevel, SecurityPolicy};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Git operations tool for structured repository management.
|
/// Git operations tool for structured repository management.
|
||||||
|
|
@ -556,6 +555,7 @@ impl Tool for GitOperationsTool {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::security::SecurityPolicy;
|
use crate::security::SecurityPolicy;
|
||||||
|
use std::path::Path;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn test_tool(dir: &Path) -> GitOperationsTool {
|
fn test_tool(dir: &Path) -> GitOperationsTool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue