fix(handlers): allow legacy templates without runtime field #1810
@@ -636,6 +636,11 @@ def load_config(path: str) -> dict[str, Any]:
|
||||
dep by keeping the config shape constrained.
|
||||
"""
|
||||
try:
|
||||
# yaml is an optional dep; the canonical loader is used when available,
|
||||
# but the SOP runs on runners that may not have PyYAML installed. The
|
||||
# fallback _load_config_minimal covers the same config shape without
|
||||
# requiring the dep, so the ignore is safe: if yaml loads, we use it;
|
||||
# otherwise we fall back silently.
|
||||
import yaml # type: ignore[import-not-found]
|
||||
with open(path) as f:
|
||||
return yaml.safe_load(f)
|
||||
@@ -656,8 +661,14 @@ def _load_config_minimal(path: str) -> dict[str, Any]:
|
||||
return _parse_minimal_yaml(lines)
|
||||
|
||||
|
||||
def _parse_minimal_yaml(lines: list[str]) -> dict[str, Any]: # noqa: C901
|
||||
"""Hand-rolled subset parser. See _load_config_minimal docstring."""
|
||||
def _parse_minimal_yaml(lines: list[str]) -> dict[str, Any]:
|
||||
"""Hand-rolled subset parser. See _load_config_minimal docstring.
|
||||
|
||||
C901: function is necessarily long — it implements a finite-state YAML
|
||||
subset (scalars, maps, lists of maps at fixed depth). No utility refactors
|
||||
meaningfully reduce length without degrading readability. All branches
|
||||
are exhaustively tested in test_parse_minimal_yaml.py.
|
||||
"""
|
||||
# Strip comments + blank lines but preserve indentation.
|
||||
cleaned: list[tuple[int, str]] = []
|
||||
for raw in lines:
|
||||
@@ -1015,14 +1026,14 @@ def main(argv: list[str] | None = None) -> int:
|
||||
tid = client.resolve_team_id(args.owner, tn)
|
||||
if tid is None:
|
||||
# Try the list endpoint as a fallback.
|
||||
code, data = client._req( # noqa: SLF001
|
||||
code, data = client._req( # noqa: SLF001 # internal helper; called from loop in caller context
|
||||
"GET", f"/orgs/{args.owner}/teams"
|
||||
)
|
||||
if code == 200 and isinstance(data, list):
|
||||
for t in data:
|
||||
if t.get("name") == tn:
|
||||
tid = t.get("id")
|
||||
client._team_id_cache[(args.owner, tn)] = tid # noqa: SLF001
|
||||
client._team_id_cache[(args.owner, tn)] = tid # noqa: SLF001 # internal write-through cache
|
||||
break
|
||||
if tid is not None:
|
||||
team_ids.append(tid)
|
||||
|
||||
@@ -91,6 +91,10 @@ def _gitea_get(path: str, params: dict[str, str] | None = None) -> bytes | None:
|
||||
req.add_header("Authorization", f"token {token}")
|
||||
req.add_header("Accept", "application/json")
|
||||
try:
|
||||
# S310 (信任boundary): this function IS the outbound HTTP client for
|
||||
# Gitea API calls. The call is intentional and controlled — we build
|
||||
# the request ourselves and handle errors explicitly. Timeout=20s
|
||||
# prevents indefinite hangs.
|
||||
with urllib.request.urlopen(req, timeout=20) as resp: # noqa: S310
|
||||
return resp.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
|
||||
@@ -243,10 +243,12 @@ func (h *TemplatesHandler) List(c *gin.Context) {
|
||||
log.Printf("templates list: skip %s: yaml.Unmarshal: %v", id, err)
|
||||
return
|
||||
}
|
||||
runtime := strings.TrimSuffix(strings.TrimSpace(raw.Runtime), "-default")
|
||||
if _, ok := knownRuntimes[runtime]; !ok {
|
||||
log.Printf("templates list: skip %s: unsupported runtime %q", id, raw.Runtime)
|
||||
return
|
||||
if raw.Runtime != "" {
|
||||
runtime := strings.TrimSuffix(strings.TrimSpace(raw.Runtime), "-default")
|
||||
if _, ok := knownRuntimes[runtime]; !ok {
|
||||
log.Printf("templates list: skip %s: unsupported runtime %q", id, raw.Runtime)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Model comes from either top-level (legacy) or runtime_config.model (current).
|
||||
|
||||
@@ -677,7 +677,7 @@ skills: []
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if len(resp) != 1 || resp[0].Model != "anthropic:claude-sonnet-4-6" {
|
||||
t.Errorf("legacy top-level model not surfaced: %+v", resp)
|
||||
t.Fatalf("legacy top-level model not surfaced: %+v", resp)
|
||||
}
|
||||
if resp[0].Runtime != "claude-code" {
|
||||
t.Errorf("Runtime should be claude-code for legacy template, got %q", resp[0].Runtime)
|
||||
|
||||
Reference in New Issue
Block a user