Skip to content
Open
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
31 changes: 27 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@
# All defaults here work with `docker compose up` out of the box.
#
# Service ports (defaults):
# Postgres → localhost:5432
# Redis → localhost:6379
# Postgres → localhost:5432 (override with PGPORT + DATABASE_URL together)
# Redis → localhost:6379 (override with BUZZ_REDIS_HOST_PORT + REDIS_URL)
# Typesense → localhost:8108
# Adminer → localhost:8082 (DB browser UI)
# Adminer → localhost:8082 (override with BUZZ_ADMINER_HOST_PORT)
# MinIO/S3 → localhost:9000 (override with BUZZ_MINIO_HOST_PORT + BUZZ_S3_ENDPOINT)
# MinIO cons. → localhost:9001 (override with BUZZ_MINIO_CONSOLE_HOST_PORT)
#
# Note: If port 8082 conflicts, change the adminer port in docker-compose.yml
# If Homebrew (or another) service already owns one of these host ports, pick a
# free host port and keep the paired connection URL in sync, for example:
# PGPORT=55432
# DATABASE_URL=postgres://buzz:buzz_dev@localhost:55432/buzz
# BUZZ_REDIS_HOST_PORT=56379
# REDIS_URL=redis://localhost:56379
# BUZZ_MINIO_HOST_PORT=59000
# BUZZ_S3_ENDPOINT=http://localhost:59000
# BUZZ_MINIO_CONSOLE_HOST_PORT=59001
# BUZZ_ADMINER_HOST_PORT=58082
# =============================================================================

# -----------------------------------------------------------------------------
Expand All @@ -30,6 +41,8 @@ PGDATABASE=buzz
# -----------------------------------------------------------------------------
# Redis 7
# -----------------------------------------------------------------------------
# Host port published by docker-compose (container still listens on 6379).
# BUZZ_REDIS_HOST_PORT=6379
REDIS_URL=redis://localhost:6379

# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -76,6 +89,16 @@ RELAY_URL=ws://localhost:3000
# BUZZ_GIT_PACK_CACHE_MAX_BYTES=5368709120
# BUZZ_GIT_PACK_CACHE_MAX_CONCURRENT_POPULATIONS=2

# -----------------------------------------------------------------------------
# MinIO / S3 media storage
# -----------------------------------------------------------------------------
# Host ports published by docker-compose (container still listens on 9000/9001).
# If a local MinIO/S3 owns :9000, pick a free host port and keep BUZZ_S3_ENDPOINT
# in sync — the relay defaults to http://localhost:9000.
# BUZZ_MINIO_HOST_PORT=9000
# BUZZ_MINIO_CONSOLE_HOST_PORT=9001
# BUZZ_S3_ENDPOINT=http://localhost:9000

# -----------------------------------------------------------------------------
# Media Upload Admission
# -----------------------------------------------------------------------------
Expand Down
11 changes: 10 additions & 1 deletion crates/buzz-relay/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,16 @@ async fn main() -> anyhow::Result<()> {
.git_store
.run_conformance_probe(cfg)
.await
.map_err(|e| anyhow::anyhow!("git conformance probe failed: {e}"))?;
.map_err(|e| {
anyhow::anyhow!(
"git conformance probe failed: {e}. \
Object stores that do not honor S3 If-Match / If-None-Match \
(notably GCS via the S3-interop XML API) cannot satisfy the \
A3 pointer-CAS gate. Use a CAS-capable backend (MinIO, AWS S3), \
or set BUZZ_GIT_CONFORMANCE_PROBE=false to skip admission \
(git ref updates will not be linearizable)."
)
})?;
tracing::info!(
race_width = report.race_width,
race_rounds = report.race_rounds,
Expand Down
1 change: 1 addition & 0 deletions deploy/charts/buzz/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ spec:
- { name: BUZZ_GIT_PACK_CACHE_MAX_CONCURRENT_POPULATIONS, value: {{ .Values.git.packCacheMaxConcurrentPopulations | quote }} }
- { name: BUZZ_GIT_MAX_REPOS_PER_PUBKEY, value: {{ .Values.git.maxReposPerPubkey | quote }} }
- { name: BUZZ_GIT_MAX_CONCURRENT_OPS, value: {{ .Values.git.maxConcurrentOps | quote }} }
- { name: BUZZ_GIT_CONFORMANCE_PROBE, value: {{ .Values.git.conformanceProbe | quote }} }

# ── S3 (non-secret) ──────────────────────────────────────
{{- $s3Endpoint := include "buzz.s3Endpoint" . }}
Expand Down
4 changes: 4 additions & 0 deletions deploy/charts/buzz/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ git:
packCacheVolumeSize: 7Gi # per-pod emptyDir; includes cold-population staging
maxReposPerPubkey: 100
maxConcurrentOps: 20
# Startup A3 probe (S3 conditional-write CAS). Default true. Set false only
# for backends that cannot honor If-Match (e.g. GCS S3-interop) — see #2470.
# Skipping means git pointer updates are not linearizable.
conformanceProbe: true

# ── Migrations ───────────────────────────────────────────────────────────────
# Relay runs sqlx migrations at startup via BUZZ_AUTO_MIGRATE=true.
Expand Down
41 changes: 36 additions & 5 deletions desktop/src-tauri/src/managed_agents/runtime_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,21 @@ pub fn list_managed_agent_runtimes(
.managed_agent_processes
.lock()
.map_err(|e| e.to_string())?;
let exited_keys: Vec<_> = runtimes
// Reap exited children the same way as `sync_managed_agent_processes` so
// unexpected harness deaths set `last_error` / `last_exit_code` (and the
// Agents UI can show them) instead of silently clearing the runtime
// (#2453). Capture wait results before removing map entries.
let exited: Vec<_> = runtimes
.iter_mut()
.filter_map(|(key, runtime)| match runtime.child.try_wait() {
Ok(Some(_)) | Err(_) => Some(key.clone()),
Ok(Some(status)) => Some((key.clone(), Ok(status), runtime.log_path.clone())),
Err(error) => Some((key.clone(), Err(error), runtime.log_path.clone())),
Ok(None) => None,
})
.collect();
let records_changed = !exited_keys.is_empty();
let records_changed = !exited.is_empty();
let mut statuses = Vec::new();
for key in exited_keys {
for (key, wait_result, log_path) in exited {
runtimes.remove(&key);
super::remove_agent_runtime_receipt(&app, &key);
state.clear_agent_session_cache(&key);
Expand All @@ -179,6 +184,28 @@ pub fn list_managed_agent_runtimes(
{
record.updated_at = crate::util::now_iso();
record.last_stopped_at = Some(record.updated_at.clone());
match wait_result {
Ok(status) => {
record.last_exit_code = status.code();
if status.success() {
// Stopped without our stop command — still a signal.
record.last_error = Some("agent runtime exited unexpectedly".to_string());
record.last_error_code = None;
} else {
let log_err = super::meaningful_agent_error_from_log(&log_path)
.unwrap_or_else(|| super::storage::AgentLogError {
message: format!("harness exited with status {status}"),
code: None,
});
record.last_error = Some(log_err.message);
record.last_error_code = log_err.code;
}
}
Err(error) => {
record.last_error = Some(format!("failed to inspect process state: {error}"));
record.last_error_code = None;
}
}
let status = status_for_with(
&app,
record,
Expand All @@ -194,6 +221,9 @@ pub fn list_managed_agent_runtimes(
statuses.push(status);
}
}
if records_changed {
let _ = app.emit("agents-data-changed", ());
}
statuses.extend(runtimes.iter().filter_map(|(key, runtime)| {
let record = records
.iter()
Expand All @@ -212,7 +242,8 @@ pub fn list_managed_agent_runtimes(
}));
drop(runtimes);
// Records are only mutated above when a runtime exited — skip the store
// rewrite on the common nothing-changed poll.
// rewrite on the common nothing-changed poll. (`agents-data-changed` is
// emitted earlier so the Agents list refreshes alongside status events.)
if records_changed {
save_managed_agents(&app, &records)?;
}
Expand Down
15 changes: 10 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ services:
POSTGRES_DB: buzz
PGDATA: /var/lib/postgresql/data
ports:
- "5432:5432"
# Host port follows PGPORT so local Homebrew Postgres can keep :5432 (#2479).
- "${PGPORT:-5432}:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
Expand All @@ -34,7 +35,8 @@ services:
image: redis:7-alpine
container_name: buzz-redis
ports:
- "6379:6379"
# Host port follows BUZZ_REDIS_HOST_PORT so local Redis can keep :6379 (#2479).
- "${BUZZ_REDIS_HOST_PORT:-6379}:6379"
networks:
- buzz-net
healthcheck:
Expand All @@ -56,7 +58,8 @@ services:
image: adminer:latest
container_name: buzz-adminer
ports:
- "8082:8080"
# Host port follows BUZZ_ADMINER_HOST_PORT so a local :8082 can be freed (#2479).
- "${BUZZ_ADMINER_HOST_PORT:-8082}:8080"
networks:
- buzz-net
depends_on:
Expand Down Expand Up @@ -108,8 +111,10 @@ services:
MINIO_ROOT_USER: buzz_dev
MINIO_ROOT_PASSWORD: buzz_dev_secret
ports:
- "9000:9000"
- "9001:9001"
# Host ports follow BUZZ_MINIO_HOST_PORT / BUZZ_MINIO_CONSOLE_HOST_PORT so a
# local MinIO/S3 can keep :9000/:9001. Keep BUZZ_S3_ENDPOINT in sync (#2479).
- "${BUZZ_MINIO_HOST_PORT:-9000}:9000"
- "${BUZZ_MINIO_CONSOLE_HOST_PORT:-9001}:9001"
volumes:
- minio-data:/data
networks:
Expand Down
8 changes: 5 additions & 3 deletions scripts/dev-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ fail_if_local_redis_blocks_compose() {
if docker ps --format '{{.Names}}' | grep -qx 'buzz-redis'; then
return
fi
# Match the compose host publish port (see docker-compose.yml / #2479).
local redis_host_port="${BUZZ_REDIS_HOST_PORT:-6379}"
local redis_pids
redis_pids=$(lsof -nP -iTCP:6379 -sTCP:LISTEN 2>/dev/null | awk 'NR > 1 && $1 == "redis-ser" {print $2}' | sort -u | tr '
redis_pids=$(lsof -nP -iTCP:"${redis_host_port}" -sTCP:LISTEN 2>/dev/null | awk 'NR > 1 && $1 == "redis-ser" {print $2}' | sort -u | tr '
' ' ' || true)
if [[ -n "${redis_pids}" ]]; then
error "Local Redis is already listening on port 6379 (pid(s): ${redis_pids}). Stop it before running setup: brew services stop redis"
error "Local Redis is already listening on port ${redis_host_port} (pid(s): ${redis_pids}). Stop it, or set BUZZ_REDIS_HOST_PORT / REDIS_URL to a free host port before running setup."
exit 1
fi
}
Expand Down Expand Up @@ -187,7 +189,7 @@ echo -e "${GREEN}=======================================================${NC}"
echo ""
echo -e " ${BLUE}Postgres${NC} ${DATABASE_URL}"
echo -e " ${BLUE}Redis${NC} ${REDIS_URL}"
echo -e " ${BLUE}Adminer${NC} http://localhost:8082 (DB browser)"
echo -e " ${BLUE}Adminer${NC} http://localhost:${BUZZ_ADMINER_HOST_PORT:-8082} (DB browser)"
echo -e " ${BLUE}Keycloak${NC} http://localhost:8180 (admin / admin — local OAuth testing)"
echo ""
echo -e " ${YELLOW}Next steps:${NC}"
Expand Down