# Build & deployment > Normative requirements: [SPEC.md §10](../SPEC.md#10-build--deployment-must) Every agent ships three deploy scripts under `scripts/` that wrap the same build → push → apply → roll-out pipeline at three layers of fidelity. ## `.image-version` A single-line file at the agent root: ``` 1.0.3 ``` Conventions: - Semver only. - **MUST** match `OTEL_RESOURCE_ATTRIBUTES`' `service.version` value at all times. - Bumped whenever a code change ships. The patch number is auto-incremented by `scripts/deploy.sh` when no `--tag` is given. - Pure image-version-only commits are reserved for *redeploy reruns* (e.g. rebuilding without code changes to refresh dependencies). They **SHOULD** be rare. ## ACR All images are pushed to: ``` bstagecjotdevacr.azurecr.io/: bstagecjotdevacr.azurecr.io/:latest ``` Both tags are pushed every build so that pinning to `:latest` works for sandbox/dev clusters and pinning to `:` works for prod clusters. ## `scripts/deploy.sh` The fast path. Used for routine redeploys. ```bash #!/bin/bash set -euo pipefail SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) ACR_NAME="bstagecjotdevacr" APP_NAME="" NAMESPACE="agents" VERSION_FILE="${SCRIPT_DIR}/../.image-version" TAG="" while [[ "$#" -gt 0 ]]; do case $1 in --tag) TAG="$2"; shift ;; *) echo "unknown arg: $1"; exit 2 ;; esac shift done if [ -z "$TAG" ]; then CURRENT=$(cat "$VERSION_FILE" 2>/dev/null || echo "1.0.0") IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" PATCH=$((PATCH + 1)) TAG="${MAJOR}.${MINOR}.${PATCH}" echo "$TAG" > "$VERSION_FILE" fi IMAGE="${ACR_NAME}.azurecr.io/${APP_NAME}:${TAG}" echo "Building ${IMAGE} ..." az acr build --registry "$ACR_NAME" \ --image "${APP_NAME}:${TAG}" \ --image "${APP_NAME}:latest" \ "${SCRIPT_DIR}/.." echo "Applying manifests ..." kubectl apply -f "${SCRIPT_DIR}/../k8s/configmap.yaml" kubectl apply -f "${SCRIPT_DIR}/../k8s/deployment.yaml" kubectl -n "$NAMESPACE" rollout status "deployment/${APP_NAME}" --timeout=180s ``` Behaviour: - Auto-bumps the patch on `.image-version` if `--tag` is not given. - Rebuilds and re-pushes both `:` and `:latest`. - Applies `configmap.yaml` and `deployment.yaml` in order. - Blocks until the rollout completes (or fails) within 180 s. ## `scripts/full-deploy.sh` The "first time" path. Adds preflight + post-deploy smoke. Preflight (refuse to continue if any check fails): - `kubectl` available and pointed at a cluster. - `az` available and logged in. - Target namespace exists. - ServiceAccount `agents-sa` exists in the namespace. - SecretProviderClass `agents-kv-spc` exists in the namespace. After `rollout status` succeeds: - `kubectl exec deployment/ -- curl -fs http://localhost:/health` - `kubectl exec deployment/ -- curl -fs http://localhost:/.well-known/agent.json | jq -e '.name'` Failures abort with a non-zero exit code so CI can block. ## `scripts/deploy-local.sh` For dev loop on a workstation without cluster access. - Builds the image locally with `docker` or `podman`. - Reads `.env.local` (sibling of `.env.local.example`). - Runs the container with `-p :` and `--env-file`. Useful for iterating on prompt changes without touching the cluster. ## CI integration The platform's GitHub Actions / Gitea Actions runners invoke `scripts/deploy.sh --tag $GITHUB_SHA` (or similar) when an agent's directory changes on the deploy branch. Pull requests trigger a build-only run that pushes a `:` tag without applying manifests. ## Rolling back There is no automated rollback. To roll back: ```bash kubectl -n agents set image deployment/ \ =bstagecjotdevacr.azurecr.io/: kubectl -n agents rollout status deployment/ --timeout=180s ``` If the rollback ships a different `service.version`, also revert `OTEL_RESOURCE_ATTRIBUTES` in the ConfigMap or the dashboards will misattribute. Prefer a forward-fix (new patch version) over a rollback whenever possible. ## Re-registration after redeploy The agent re-registers with the gateway on every pod start. No manual step is required after a rollout. If the gateway pod is also restarting at the same time, expect 1–2 `WARNING`s in the agent log before registration succeeds — see [registration.md](registration.md#failure-modes--resolution).