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 ./
|
||||
# Create dummy main.rs to build dependencies
|
||||
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
|
||||
|
||||
# 2. Copy source code
|
||||
COPY . .
|
||||
# Touch main.rs to force rebuild
|
||||
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
|
||||
|
||||
# ── 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
|
||||
|
||||
- **`agent/`**: (Merged into root Dockerfile)
|
||||
- The development image is built from the root `Dockerfile` using the `dev` stage (`target: dev`).
|
||||
- Based on `debian:bookworm-slim` (unlike production `distroless`).
|
||||
- Includes `bash`, `curl`, and debug tools.
|
||||
- The development image is built from the root `Dockerfile` using the `dev` stage (`target: dev`).
|
||||
- Based on `debian:bookworm-slim` (unlike production `distroless`).
|
||||
- Includes `bash`, `curl`, and debug tools.
|
||||
- **`sandbox/`**: Dockerfile for the simulated user environment.
|
||||
- Based on `ubuntu:22.04`.
|
||||
- Pre-loaded with `git`, `python3`, `nodejs`, `npm`, `gcc`, `make`.
|
||||
- Simulates a real developer machine.
|
||||
- Based on `ubuntu:22.04`.
|
||||
- Pre-loaded with `git`, `python3`, `nodejs`, `npm`, `gcc`, `make`.
|
||||
- Simulates a real developer machine.
|
||||
- **`docker-compose.yml`**: Defines the services and `dev-net` network.
|
||||
- **`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:
|
||||
|
||||
### 1. Start Environment
|
||||
|
||||
```bash
|
||||
./dev/cli.sh up
|
||||
```
|
||||
|
||||
Builds the agent from source and starts both containers.
|
||||
|
||||
### 2. Enter Agent Container (`zeroclaw-dev`)
|
||||
|
||||
```bash
|
||||
./dev/cli.sh agent
|
||||
```
|
||||
|
||||
Use this to run `zeroclaw` CLI commands manually, debug the binary, or check logs internally.
|
||||
|
||||
- **Path**: `/zeroclaw-data`
|
||||
- **User**: `nobody` (65534)
|
||||
|
||||
### 3. Enter Sandbox (`sandbox`)
|
||||
|
||||
```bash
|
||||
./dev/cli.sh shell
|
||||
```
|
||||
|
||||
Use this to act as the "user" or "environment" the agent interacts with.
|
||||
|
||||
- **Path**: `/home/developer/workspace`
|
||||
- **User**: `developer` (sudo-enabled)
|
||||
|
||||
### 4. Development Cycle
|
||||
|
||||
1. Make changes to Rust code in `src/`.
|
||||
2. Rebuild the agent:
|
||||
```bash
|
||||
./dev/cli.sh build
|
||||
```
|
||||
```bash
|
||||
./dev/cli.sh build
|
||||
```
|
||||
3. Test changes inside the container:
|
||||
```bash
|
||||
./dev/cli.sh agent
|
||||
# inside container:
|
||||
zeroclaw --version
|
||||
```
|
||||
```bash
|
||||
./dev/cli.sh agent
|
||||
# inside container:
|
||||
zeroclaw --version
|
||||
```
|
||||
|
||||
### 5. Persistence & Shared Workspace
|
||||
|
||||
The local `playground/` directory (in repo root) is mounted as the shared workspace:
|
||||
|
||||
- **Agent**: `/zeroclaw-data/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.
|
||||
|
||||
### 6. Cleanup
|
||||
|
||||
Stop containers and remove volumes and generated config:
|
||||
|
||||
```bash
|
||||
./dev/cli.sh clean
|
||||
```
|
||||
|
||||
**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}logs${NC} View logs"
|
||||
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"
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +95,15 @@ case "$1" in
|
|||
echo -e "${GREEN}✅ Rebuild complete.${NC}"
|
||||
;;
|
||||
|
||||
ci)
|
||||
shift
|
||||
if [ "$BASE_DIR" = "." ]; then
|
||||
./ci.sh "${@:-all}"
|
||||
else
|
||||
./dev/ci.sh "${@:-all}"
|
||||
fi
|
||||
;;
|
||||
|
||||
clean)
|
||||
echo -e "${RED}⚠️ WARNING: This will delete 'target/.zeroclaw' data and Docker volumes.${NC}"
|
||||
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 async_trait::async_trait;
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Git operations tool for structured repository management.
|
||||
|
|
@ -556,6 +555,7 @@ impl Tool for GitOperationsTool {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::security::SecurityPolicy;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn test_tool(dir: &Path) -> GitOperationsTool {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue