Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ review can tell which standard a project was built from.

## Unreleased

- **Deploy: find out how the customer can build before building.** `agentbase-build-deploy` Step 3 now starts with
`scripts/detect_build_env.sh` (read-only; no login, no secret printed): which engine is installed and answering (Docker
Desktop, Rancher Desktop, OrbStack, Colima, Podman, nerdctl, Finch, buildah), `linux/amd64` support, git host and CI config
(GitHub Actions, GitLab CI, Jenkins, Bitbucket, Azure), `gh` / `glab` sign-in; it lists the ways to build and push and the
user picks. Tested here with Docker Desktop and stubs for a hung daemon, nerdctl and a GitLab remote; the nerdctl, Podman,
Finch and GitLab paths are not run against the registry.
- **Skill descriptions** of all `agentbase-build-*` skills now open with what the user asks or wants (and say to load the skill
before answering, even a short question); `Trigger:` and `DO NOT use` clauses are kept. Haiku 5.5 routing 79% → 95% on the
tuning set, Sonnet 5.5 unchanged (98%). The orchestrator says it replaces `/agentbase-wizard` even when that is installed
Expand Down
17 changes: 17 additions & 0 deletions skills/agentbase-build-deploy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ The scaffold commits one **non-secret** config file per environment: `src/backen

## Step 3 — Build the image

**First find out what the user can build with** — do not assume Docker. Run the read-only detector (it changes nothing, logs in nowhere and prints no secret):

```bash
bash <this-skill>/scripts/detect_build_env.sh <project-dir>
```

It reports the container engine that is installed *and answering* (Docker Desktop, Rancher Desktop, OrbStack, Colima, Podman, nerdctl, Finch, buildah), whether it can build `linux/amd64`, the git host and CI config (GitHub Actions, GitLab CI, Jenkins, Bitbucket, Azure), and whether `gh` / `glab` are signed in, then lists the ways to build and push. **Show that list and let the user choose** (a running engine → build locally; GitHub → the CI path below; nothing → install an engine or use CI).

| What the user has | Build and push |
|---|---|
| Docker, or Rancher Desktop in dockerd mode, OrbStack, Colima, Docker Desktop | The commands below; login with `cr.sh credentials docker-login` |
| Rancher Desktop in containerd mode (`nerdctl`), Podman, Finch | `<engine> build --platform linux/amd64 --build-arg AGENT_VERSION=<tag> -t <image>:<tag> src/backend`, then `<engine> login`, `<engine> push`. The Makefile targets call `docker`, so these are run by hand. `cr.sh credentials docker-login` only logs `docker` in: the user takes the secret from the Console (or `cr.sh credentials reset`) and enters it into the engine's own prompt, never into chat |
| GitHub | CI path (*CI/CD* below): no local engine needed |
| GitLab, Jenkins, Bitbucket, Azure | No template is shipped. The job needs an engine (or kaniko / buildah), `--platform linux/amd64`, and the registry user / secret as masked CI variables. Same Dockerfile and the same image name / tag rules |

"Rancher" can also mean the Rancher Kubernetes manager, which does not build images: ask what builds the image there. The nerdctl, Podman, Finch and GitLab rows are the documented commands of those tools and have **not been run against the AgentBase registry** by this repo; only the Docker path has.

```bash
make docker-build IMAGE=<registry>/<repo>/<project> TAG=<tag> # AGENT_VERSION=<tag> baked in
make docker-run IMAGE=... TAG=... # local smoke test: curl :8080/health
Expand Down
129 changes: 129 additions & 0 deletions skills/agentbase-build-deploy/scripts/detect_build_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env bash
# Read-only check of how this machine / project can BUILD and PUSH the agent image to the registry (vCR):
# which container engine is installed and running (Docker Desktop, Rancher Desktop, OrbStack, Colima, Podman, nerdctl,
# Finch), whether it can build linux/amd64, and which CI the project uses (GitHub Actions, GitLab CI, ...).
# Prints facts and the ways to build; changes nothing, logs in nowhere, prints no credential (only whether a CLI is
# signed in). Usage: bash detect_build_env.sh [project-dir] (default: current directory)
set -u
DIR="${1:-.}"
cd "$DIR" 2>/dev/null || { echo "ERROR: $DIR is not a directory" >&2; exit 1; }

have() { command -v "$1" >/dev/null 2>&1; }

# run a command with a time limit (macOS has no `timeout`): a stopped VM makes `docker info` hang. Output goes through a
# file, not a pipe, so a child that outlives the kill cannot keep the caller waiting
limited() {
local secs="$1"; shift
local out; out="$(mktemp)"
"$@" >"$out" 2>/dev/null </dev/null & local pid=$!
( sleep "$secs"; kill "$pid" 2>/dev/null ) >/dev/null 2>&1 & local guard=$!
wait "$pid" 2>/dev/null; local rc=$?
kill "$guard" 2>/dev/null; wait "$guard" 2>/dev/null
cat "$out"; rm -f "$out"
return $rc
}

ENGINES_READY=() # engines that can build now
echo "== Machine"
ARCH="$(uname -m)"
echo "os/arch: $(uname -s)/$ARCH"
[ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ] && echo "note: ARM host; AgentBase Runtime runs linux/amd64, so build with --platform linux/amd64 (emulated, slower)"

echo
echo "== Container engines"
if have docker; then
CTX="$(docker context show 2>/dev/null || echo '?')"
INFO="$(limited 15 docker info --format '{{.ServerVersion}}|{{.OperatingSystem}}|{{.Name}}' 2>/dev/null)"
if [ -n "$INFO" ]; then
SRV="${INFO%%|*}"; REST="${INFO#*|}"; OSN="${REST%%|*}"; HOSTN="${REST#*|}"
KIND="Docker Engine"
case "$CTX $OSN $HOSTN" in
*rancher*|*Rancher*|*lima-rancher*) KIND="Rancher Desktop (dockerd / moby mode)" ;;
*orbstack*|*OrbStack*) KIND="OrbStack" ;;
*colima*|*Colima*) KIND="Colima" ;;
*"Docker Desktop"*|*desktop-linux*|*docker-desktop*) KIND="Docker Desktop" ;;
*podman*|*Podman*) KIND="Podman (docker-compatible socket)" ;;
esac
echo "docker: running, $KIND, server $SRV, context '$CTX'"
ENGINES_READY+=("docker")
if docker buildx version >/dev/null 2>&1; then
PLAT="$(limited 15 docker buildx inspect 2>/dev/null | grep -i '^Platforms:' | head -1)"
echo "docker buildx: yes. ${PLAT:-Platforms: unknown}"
case "$PLAT" in *linux/amd64*) ;; *) [ "$ARCH" != "x86_64" ] && echo "warning: linux/amd64 not listed; building for the Runtime may fail (docker run --privileged --rm tonistiigi/binfmt --install amd64)" ;; esac
else
echo "docker buildx: not found (docker build --platform linux/amd64 still works with the classic builder on amd64 hosts)"
fi
else
echo "docker: CLI installed (context '$CTX') but the daemon does not answer within 15 s — start Docker Desktop / Rancher Desktop / OrbStack / Colima first"
fi
else
echo "docker: CLI not found"
fi

for e in nerdctl podman finch buildah; do
if have "$e"; then
case "$e" in
nerdctl) ok=0; limited 15 nerdctl info >/dev/null 2>&1 && ok=1; extra="(Rancher Desktop in containerd mode, Lima, Colima)" ;;
podman) ok=0; limited 20 podman info >/dev/null 2>&1 && ok=1; extra="(on macOS/Windows: podman machine start)" ;;
finch) ok=0; limited 15 finch info >/dev/null 2>&1 && ok=1; extra="(finch vm start)" ;;
buildah) ok=1; extra="(builds only; push with buildah push, no daemon needed)" ;;
esac
if [ "$ok" = 1 ]; then echo "$e: running $extra"; ENGINES_READY+=("$e"); else echo "$e: installed but not responding $extra"; fi
fi
done
have rdctl && echo "rdctl: Rancher Desktop is installed (container engine: $(rdctl list-settings 2>/dev/null | grep -i -m1 '\"name\"' | sed 's/.*: *//; s/[\",]//g' || echo unknown))"
[ ${#ENGINES_READY[@]} -eq 0 ] && echo "no container engine is ready on this machine"

echo
echo "== Project and CI"
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
URL="$(git remote get-url origin 2>/dev/null || true)"
# keep only the host: never echo a URL that may embed a token
HOST="$(printf '%s' "$URL" | sed -E 's#^[a-z+]+://([^@/]*@)?##; s#^[^@]+@##; s#[:/].*$##')"
CI_HOST="none"
case "$HOST" in
github.com|*.github.com|*github*) CI_HOST="GitHub" ;;
*gitlab*) CI_HOST="GitLab" ;;
*bitbucket*) CI_HOST="Bitbucket" ;;
dev.azure.com|*visualstudio.com) CI_HOST="Azure DevOps" ;;
"") CI_HOST="none" ;;
*) CI_HOST="other ($HOST)" ;;
esac
echo "git remote host: ${HOST:-no origin remote} → $CI_HOST"
else
echo "not a git repository"
CI_HOST="none"
fi
FOUND=()
for f in .github/workflows/*.yml .github/workflows/*.yaml; do [ -f "$f" ] && { FOUND+=("GitHub Actions (.github/workflows)"); break; }; done
[ -f .gitlab-ci.yml ] && FOUND+=("GitLab CI (.gitlab-ci.yml)")
[ -f Jenkinsfile ] && FOUND+=("Jenkins (Jenkinsfile)")
[ -f bitbucket-pipelines.yml ] && FOUND+=("Bitbucket Pipelines")
[ -f azure-pipelines.yml ] && FOUND+=("Azure Pipelines")
[ ${#FOUND[@]} -gt 0 ] && printf 'CI config in the project: %s\n' "$(IFS=';'; echo "${FOUND[*]}" | sed 's/;/; /g')" || echo "CI config in the project: none"
if have gh; then gh auth status >/dev/null 2>&1 && echo "gh: signed in" || echo "gh: installed, not signed in"; fi
if have glab; then glab auth status >/dev/null 2>&1 && echo "glab: signed in" || echo "glab: installed, not signed in"; fi
[ -f src/backend/Dockerfile ] && echo "Dockerfile: src/backend/Dockerfile (standard)" || { [ -f Dockerfile ] && echo "Dockerfile: ./Dockerfile" || echo "Dockerfile: not found"; }

echo
echo "== Ways to build and push (pick one with the user; never choose silently)"
N=0
for e in "${ENGINES_READY[@]+"${ENGINES_READY[@]}"}"; do
N=$((N+1))
case "$e" in
docker) echo "$N. LOCAL with docker: make docker-build IMAGE=… TAG=… ; bash <platform-scripts>/cr.sh credentials docker-login ; docker push …" ;;
nerdctl) echo "$N. LOCAL with nerdctl: nerdctl build --platform linux/amd64 -t … src/backend ; nerdctl login <registry> ; nerdctl push … (the Makefile targets call docker: run these by hand)" ;;
podman) echo "$N. LOCAL with podman: podman build --platform linux/amd64 -t … src/backend ; podman login <registry> ; podman push … (the Makefile targets call docker)" ;;
finch) echo "$N. LOCAL with finch: finch build --platform linux/amd64 -t … src/backend ; finch login <registry> ; finch push …" ;;
buildah) echo "$N. LOCAL with buildah: buildah bud --platform linux/amd64 -t … src/backend ; buildah login <registry> ; buildah push …" ;;
esac
done
case "$CI_HOST" in
GitHub) N=$((N+1)); echo "$N. CI on GitHub Actions: the scaffold's .github/workflows/ci.yml builds, pushes and deploys (see CI/CD in the skill); no local engine needed" ;;
GitLab) N=$((N+1)); echo "$N. CI on GitLab: the scaffold ships no GitLab template; a job needs docker (or kaniko / buildah) with the registry credentials as masked CI variables" ;;
esac
if [ "$N" -eq 0 ]; then
echo "none available. Install Docker Desktop, Rancher Desktop (dockerd mode), OrbStack, Colima or Podman, or push the project to GitHub and use the CI path."
fi
echo
echo "Registry login: this machine's engine and the CI runner each need credentials for the registry; fetch them with the platform scripts (cr.sh), never paste a secret into chat."