Add `pull_policy: build` to every agent service emitted by the generator that shares `docker/agents/Dockerfile` as its build context. Without it, `docker compose up -d --force-recreate agents-<name>` reuses the cached `disinto/agents:latest` image and silently keeps running stale `docker/agents/entrypoint.sh` code even after the repo is updated. This masked PR #864 (and likely earlier merges) — the fix landed on disk but never reached the container. #853 already paired `build:` with `image:` on hired-agent stanzas, which was enough for first-time ups but not for re-ups. `pull_policy: build` tells Compose to rebuild the image on every up; BuildKit's layer cache makes the no-change case near-instant, and the change case picks up the new source automatically. This covers: - TOML-driven `agents-<name>` hired via `disinto hire-an-agent` — primary target of the issue. - Legacy `agents-llama` and `agents-llama-all` stanzas — same Dockerfile, same staleness problem. `bin/disinto up` already passed `--build`, so operators on the supported UX path were already covered; this closes the gap for the direct `docker compose` path the issue explicitly names in its acceptance. Regression test added to `tests/lib-generators.bats` to pin the directive alongside the existing #853 build/image invariants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
161 lines
5.8 KiB
Bash
161 lines
5.8 KiB
Bash
#!/usr/bin/env bats
|
|
# =============================================================================
|
|
# tests/lib-generators.bats — Regression guard for the #849 fix.
|
|
#
|
|
# Before #849, `_generate_local_model_services` emitted the forge-user env
|
|
# variable keyed by service name (`FORGE_BOT_USER_${service_name^^}`), so for
|
|
# an `[agents.llama]` block with `forge_user = "dev-qwen"` the compose file
|
|
# contained `FORGE_BOT_USER_LLAMA: "dev-qwen"`. That suffix diverges from the
|
|
# `FORGE_TOKEN_<FORGE_USER>` / `FORGE_PASS_<FORGE_USER>` convention that the
|
|
# same block uses two lines above, and it doesn't even round-trip through a
|
|
# dash-containing service name (`dev-qwen` → `DEV-QWEN`, which is not a valid
|
|
# shell identifier — see #852).
|
|
#
|
|
# The fix keys on `$user_upper` (already computed from `forge_user` via
|
|
# `tr 'a-z-' 'A-Z_'`), yielding `FORGE_BOT_USER_DEV_QWEN: "dev-qwen"`.
|
|
# =============================================================================
|
|
|
|
setup() {
|
|
ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
export FACTORY_ROOT="${BATS_TEST_TMPDIR}/factory"
|
|
mkdir -p "${FACTORY_ROOT}/projects"
|
|
|
|
# Minimal compose skeleton that `_generate_local_model_services` can splice into.
|
|
# It only needs a `volumes:` marker line and nothing below it that would be
|
|
# re-read after the splice.
|
|
cat > "${FACTORY_ROOT}/docker-compose.yml" <<'EOF'
|
|
services:
|
|
agents:
|
|
image: placeholder
|
|
|
|
volumes:
|
|
agent-data:
|
|
EOF
|
|
}
|
|
|
|
@test "local-model agent service emits FORGE_BOT_USER keyed by forge_user (#849)" {
|
|
cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF'
|
|
name = "test"
|
|
repo = "test-owner/test-repo"
|
|
forge_url = "http://localhost:3000"
|
|
|
|
[agents.llama]
|
|
base_url = "http://10.10.10.1:8081"
|
|
model = "qwen"
|
|
api_key = "sk-no-key-required"
|
|
roles = ["dev"]
|
|
forge_user = "dev-qwen"
|
|
compact_pct = 60
|
|
EOF
|
|
|
|
run bash -c "
|
|
set -euo pipefail
|
|
source '${ROOT}/lib/generators.sh'
|
|
_generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml'
|
|
cat '${FACTORY_ROOT}/docker-compose.yml'
|
|
"
|
|
|
|
[ "$status" -eq 0 ]
|
|
# New, forge_user-keyed suffix is present with the right value.
|
|
[[ "$output" == *'FORGE_BOT_USER_DEV_QWEN: "dev-qwen"'* ]]
|
|
# Legacy service-name-keyed suffix must not be emitted.
|
|
[[ "$output" != *'FORGE_BOT_USER_LLAMA'* ]]
|
|
}
|
|
|
|
@test "local-model agent service emits local image ref + build: fallback (#853)" {
|
|
# Before #853 the generator emitted `image: ghcr.io/disinto/agents:<tag>` for
|
|
# every hired agent. The ghcr image isn't publicly pullable and the running
|
|
# deployment has no credentials, so `docker compose up` failed with `denied`.
|
|
# The fix: emit the registry-less local name (matches `disinto init --build`
|
|
# and the legacy agents-llama stanza) plus a build: directive so hosts
|
|
# without a pre-built image can rebuild locally.
|
|
cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF'
|
|
name = "test"
|
|
repo = "test-owner/test-repo"
|
|
forge_url = "http://localhost:3000"
|
|
|
|
[agents.dev-qwen2]
|
|
base_url = "http://10.10.10.1:8081"
|
|
model = "qwen"
|
|
api_key = "sk-no-key-required"
|
|
roles = ["dev"]
|
|
forge_user = "dev-qwen2"
|
|
EOF
|
|
|
|
run bash -c "
|
|
set -euo pipefail
|
|
source '${ROOT}/lib/generators.sh'
|
|
_generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml'
|
|
cat '${FACTORY_ROOT}/docker-compose.yml'
|
|
"
|
|
|
|
[ "$status" -eq 0 ]
|
|
# Local image ref — no ghcr prefix.
|
|
[[ "$output" == *'image: disinto/agents:${DISINTO_IMAGE_TAG:-latest}'* ]]
|
|
[[ "$output" != *'image: ghcr.io/disinto/agents'* ]]
|
|
# build: fallback so hosts without a pre-built image can rebuild.
|
|
[[ "$output" == *'dockerfile: docker/agents/Dockerfile'* ]]
|
|
}
|
|
|
|
@test "local-model agent service emits pull_policy: build so docker compose up rebuilds on source change (#887)" {
|
|
# Without pull_policy: build, `docker compose up -d --force-recreate` reuses
|
|
# the cached `disinto/agents:latest` image and silently runs stale
|
|
# docker/agents/entrypoint.sh even after the repo is updated. `pull_policy:
|
|
# build` forces a rebuild on every up; BuildKit layer cache makes unchanged
|
|
# rebuilds near-instant. The alternative was requiring every operator to
|
|
# remember `--build` on every invocation, which was the bug that prompted
|
|
# #887 (2h of debugging a fix that was merged but never reached the container).
|
|
cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF'
|
|
name = "test"
|
|
repo = "test-owner/test-repo"
|
|
forge_url = "http://localhost:3000"
|
|
|
|
[agents.dev-qwen2]
|
|
base_url = "http://10.10.10.1:8081"
|
|
model = "qwen"
|
|
api_key = "sk-no-key-required"
|
|
roles = ["dev"]
|
|
forge_user = "dev-qwen2"
|
|
EOF
|
|
|
|
run bash -c "
|
|
set -euo pipefail
|
|
source '${ROOT}/lib/generators.sh'
|
|
_generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml'
|
|
cat '${FACTORY_ROOT}/docker-compose.yml'
|
|
"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *'pull_policy: build'* ]]
|
|
}
|
|
|
|
@test "local-model agent service keys FORGE_BOT_USER to forge_user even when it differs from service name (#849)" {
|
|
# Exercise the case the issue calls out: two agents in the same factory
|
|
# whose service names are identical (`[agents.llama]`) but whose
|
|
# forge_users diverge would previously both have emitted
|
|
# `FORGE_BOT_USER_LLAMA`. With the fix each emission carries its own
|
|
# forge_user-derived suffix.
|
|
cat > "${FACTORY_ROOT}/projects/a.toml" <<'EOF'
|
|
name = "a"
|
|
repo = "a/a"
|
|
forge_url = "http://localhost:3000"
|
|
|
|
[agents.dev]
|
|
base_url = "http://10.10.10.1:8081"
|
|
model = "qwen"
|
|
api_key = "sk-no-key-required"
|
|
roles = ["dev"]
|
|
forge_user = "review-qwen"
|
|
EOF
|
|
|
|
run bash -c "
|
|
set -euo pipefail
|
|
source '${ROOT}/lib/generators.sh'
|
|
_generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml'
|
|
cat '${FACTORY_ROOT}/docker-compose.yml'
|
|
"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *'FORGE_BOT_USER_REVIEW_QWEN: "review-qwen"'* ]]
|
|
[[ "$output" != *'FORGE_BOT_USER_DEV:'* ]]
|
|
}
|