fix(ci): harden community image smoke
CI / Rust Checks (push) Successful in 12m30s
CI / UI Checks (push) Successful in 5s
CI / Frontend E2E (push) Failing after 9m30s
CI / Community Image Smoke (push) Successful in 11m18s
CI / Deploy (push) Has been skipped

This commit is contained in:
2026-08-30 22:22:13 +03:00
parent ad8390e297
commit d92f817e73
10 changed files with 162 additions and 30 deletions
+48 -3
View File
@@ -134,6 +134,7 @@ class Client:
def __init__(self, base_url: str, timeout_seconds: int) -> None:
self.base_url = base_url.rstrip("/")
self.timeout_seconds = timeout_seconds
self.csrf_token: str | None = None
cookie_jar = http.cookiejar.CookieJar()
self.opener = urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(cookie_jar)
@@ -156,6 +157,21 @@ class Client:
request_headers = {"Accept": "application/json"}
if headers:
request_headers.update(headers)
if (
self.csrf_token
and method not in ("GET", "HEAD", "OPTIONS")
and (
path_or_url.startswith("/api/admin/")
or path_or_url.startswith("/api/auth/")
)
and path_or_url
not in (
"/api/auth/login",
"/api/auth/bootstrap/complete",
"/api/auth/session/csrf",
)
):
request_headers.setdefault("x-csrf-token", self.csrf_token)
if payload is not None:
data = json.dumps(payload).encode("utf-8")
request_headers["Content-Type"] = "application/json"
@@ -203,12 +219,21 @@ def admin_path(workspace_id: str, suffix: str) -> str:
def login(client: Client, email: str, password: str) -> None:
client.request_json(
response = client.request_json(
"POST",
"/api/auth/login",
{"email": email, "password": password},
expected=(200,),
)
session = require_object(response.body, "login")
csrf_token = session.get("csrf_token")
if (
not isinstance(csrf_token, str)
or not 32 <= len(csrf_token) <= 256
or not all(character.isalnum() or character in "-_." for character in csrf_token)
):
raise safe_error("login", "invalid_csrf_token")
client.csrf_token = csrf_token
def resolve_workspace(
@@ -326,6 +351,17 @@ def create_agent(client: Client, workspace_id: str, agent_slug: str) -> tuple[st
raise safe_error("agent_create", "invalid_response") from error
def agent_etag(client: Client, workspace_id: str, agent_id: str) -> str:
response = client.request_json(
"GET",
admin_path(workspace_id, f"/agents/{agent_id}"),
)
etag = response.headers.get("ETag") if response.headers is not None else None
if not isinstance(etag, str) or len(etag) > 128 or not etag.startswith('"') or not etag.endswith('"'):
raise safe_error("agent_precondition", "invalid_response")
return etag
def edit_and_archive_operation(
client: Client,
workspace_id: str,
@@ -377,11 +413,13 @@ def bind_and_publish_agent(
"enabled": True,
}
],
headers={"If-Match": agent_etag(client, workspace_id, agent_id)},
)
published = client.request_json(
"POST",
admin_path(workspace_id, f"/agents/{agent_id}/publish"),
{"version": agent_version},
headers={"If-Match": agent_etag(client, workspace_id, agent_id)},
).body
try:
published_version = int(published["published_version"])
@@ -436,8 +474,15 @@ def cleanup_smoke_assets(
if agent_id:
try:
client.request_json(
"DELETE",
admin_path(workspace_id, f"/agents/{agent_id}"),
"POST",
admin_path(workspace_id, f"/agents/{agent_id}/unpublish"),
headers={"If-Match": agent_etag(client, workspace_id, agent_id)},
expected=(200, 404),
)
client.request_json(
"POST",
admin_path(workspace_id, f"/agents/{agent_id}/archive"),
headers={"If-Match": agent_etag(client, workspace_id, agent_id)},
expected=(200, 404),
)
except SmokeError as error:
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
port="${1:?port is required}"
path="${2:?path is required}"
if [[ ! "$port" =~ ^[0-9]{1,5}$ ]] || (( port < 1 || port > 65535 )); then
exit 64
fi
if [[ "$path" != /* || "$path" == *$'\r'* || "$path" == *$'\n'* ]]; then
exit 64
fi
# Bash provides /dev/tcp without adding a network client package to the runtime
# image. Compose still applies its own five-second timeout to the whole probe.
exec 3<>"/dev/tcp/127.0.0.1/${port}"
printf 'GET %s HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n' "$path" >&3
IFS=$'\r' read -r -t 2 status <&3
case "$status" in
'HTTP/1.0 200 '*|'HTTP/1.1 200 '*) ;;
*) exit 1 ;;
esac