mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-16 12:05:48 -07:00
Stabilize job environment variable injection
This commit is contained in:
@@ -70,19 +70,37 @@ def _ps_literal(value: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _build_wrapped_script(content: str, env_map: Dict[str, str], timeout_seconds: int) -> str:
|
def _build_wrapped_script(content: str, env_map: Dict[str, str], timeout_seconds: int) -> str:
|
||||||
inner_lines: List[str] = []
|
def _env_assignment_lines(lines: List[str]) -> None:
|
||||||
for key, value in (env_map or {}).items():
|
for key, value in (env_map or {}).items():
|
||||||
if not key:
|
if not key:
|
||||||
continue
|
continue
|
||||||
value_literal = _ps_literal(value)
|
value_literal = _ps_literal(value)
|
||||||
key_literal = _ps_literal(key)
|
key_literal = _ps_literal(key)
|
||||||
inner_lines.append(
|
env_path_literal = f"[string]::Format('Env:{{0}}', {key_literal})"
|
||||||
f"[System.Environment]::SetEnvironmentVariable({key_literal}, {value_literal}, 'Process')"
|
lines.append(
|
||||||
|
f"try {{ [System.Environment]::SetEnvironmentVariable({key_literal}, {value_literal}, 'Process') }} catch {{}}"
|
||||||
)
|
)
|
||||||
inner_lines.append(f"$Env:{key} = {value_literal}")
|
lines.append(
|
||||||
|
"try { Set-Item -LiteralPath (" + env_path_literal + ") -Value " + value_literal +
|
||||||
|
" -ErrorAction Stop } catch { try { New-Item -Path (" + env_path_literal + ") -Value " +
|
||||||
|
value_literal + " -Force | Out-Null } catch {} }"
|
||||||
|
)
|
||||||
|
|
||||||
|
prelude_lines: List[str] = []
|
||||||
|
_env_assignment_lines(prelude_lines)
|
||||||
|
|
||||||
|
inner_lines: List[str] = []
|
||||||
|
_env_assignment_lines(inner_lines)
|
||||||
inner_lines.append(content or "")
|
inner_lines.append(content or "")
|
||||||
|
|
||||||
|
prelude = "\n".join(prelude_lines)
|
||||||
inner = "\n".join(line for line in inner_lines if line is not None)
|
inner = "\n".join(line for line in inner_lines if line is not None)
|
||||||
script_block = "$__BorealisScript = {\n" + inner + "\n}\n"
|
|
||||||
|
pieces: List[str] = []
|
||||||
|
if prelude:
|
||||||
|
pieces.append(prelude)
|
||||||
|
pieces.append("$__BorealisScript = {\n" + inner + "\n}\n")
|
||||||
|
script_block = "\n".join(pieces)
|
||||||
if timeout_seconds and timeout_seconds > 0:
|
if timeout_seconds and timeout_seconds > 0:
|
||||||
block = (
|
block = (
|
||||||
"$job = Start-Job -ScriptBlock $__BorealisScript\n"
|
"$job = Start-Job -ScriptBlock $__BorealisScript\n"
|
||||||
|
|||||||
@@ -72,19 +72,37 @@ def _ps_literal(value: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _build_wrapped_script(content: str, env_map: Dict[str, str], timeout_seconds: int) -> str:
|
def _build_wrapped_script(content: str, env_map: Dict[str, str], timeout_seconds: int) -> str:
|
||||||
inner_lines: List[str] = []
|
def _env_assignment_lines(lines: List[str]) -> None:
|
||||||
for key, value in (env_map or {}).items():
|
for key, value in (env_map or {}).items():
|
||||||
if not key:
|
if not key:
|
||||||
continue
|
continue
|
||||||
value_literal = _ps_literal(value)
|
value_literal = _ps_literal(value)
|
||||||
key_literal = _ps_literal(key)
|
key_literal = _ps_literal(key)
|
||||||
inner_lines.append(
|
env_path_literal = f"[string]::Format('Env:{{0}}', {key_literal})"
|
||||||
f"[System.Environment]::SetEnvironmentVariable({key_literal}, {value_literal}, 'Process')"
|
lines.append(
|
||||||
|
f"try {{ [System.Environment]::SetEnvironmentVariable({key_literal}, {value_literal}, 'Process') }} catch {{}}"
|
||||||
)
|
)
|
||||||
inner_lines.append(f"$Env:{key} = {value_literal}")
|
lines.append(
|
||||||
|
"try { Set-Item -LiteralPath (" + env_path_literal + ") -Value " + value_literal +
|
||||||
|
" -ErrorAction Stop } catch { try { New-Item -Path (" + env_path_literal + ") -Value " +
|
||||||
|
value_literal + " -Force | Out-Null } catch {} }"
|
||||||
|
)
|
||||||
|
|
||||||
|
prelude_lines: List[str] = []
|
||||||
|
_env_assignment_lines(prelude_lines)
|
||||||
|
|
||||||
|
inner_lines: List[str] = []
|
||||||
|
_env_assignment_lines(inner_lines)
|
||||||
inner_lines.append(content or "")
|
inner_lines.append(content or "")
|
||||||
|
|
||||||
|
prelude = "\n".join(prelude_lines)
|
||||||
inner = "\n".join(line for line in inner_lines if line is not None)
|
inner = "\n".join(line for line in inner_lines if line is not None)
|
||||||
script_block = "$__BorealisScript = {\n" + inner + "\n}\n"
|
|
||||||
|
pieces: List[str] = []
|
||||||
|
if prelude:
|
||||||
|
pieces.append(prelude)
|
||||||
|
pieces.append("$__BorealisScript = {\n" + inner + "\n}\n")
|
||||||
|
script_block = "\n".join(pieces)
|
||||||
if timeout_seconds and timeout_seconds > 0:
|
if timeout_seconds and timeout_seconds > 0:
|
||||||
block = (
|
block = (
|
||||||
"$job = Start-Job -ScriptBlock $__BorealisScript\n"
|
"$job = Start-Job -ScriptBlock $__BorealisScript\n"
|
||||||
|
|||||||
@@ -33,6 +33,35 @@ def _env_string(value: Any) -> str:
|
|||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_env_key(name: Any) -> str:
|
||||||
|
try:
|
||||||
|
return re.sub(r"[^A-Za-z0-9_]", "_", str(name or "").strip()).upper()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_env_aliases(env_map: Dict[str, str], variables: List[Dict[str, Any]]) -> Dict[str, str]:
|
||||||
|
expanded: Dict[str, str] = dict(env_map or {})
|
||||||
|
if not isinstance(variables, list):
|
||||||
|
return expanded
|
||||||
|
for var in variables:
|
||||||
|
if not isinstance(var, dict):
|
||||||
|
continue
|
||||||
|
name = str(var.get("name") or "").strip()
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
canonical = _canonical_env_key(name)
|
||||||
|
if not canonical or canonical not in expanded:
|
||||||
|
continue
|
||||||
|
value = expanded[canonical]
|
||||||
|
alias = re.sub(r"[^A-Za-z0-9_]", "_", name)
|
||||||
|
if alias and alias not in expanded:
|
||||||
|
expanded[alias] = value
|
||||||
|
if alias != name and re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name) and name not in expanded:
|
||||||
|
expanded[name] = value
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
|
||||||
def _parse_ts(val: Any) -> Optional[int]:
|
def _parse_ts(val: Any) -> Optional[int]:
|
||||||
"""Best effort to parse ISO-ish datetime string or numeric seconds to epoch seconds."""
|
"""Best effort to parse ISO-ish datetime string or numeric seconds to epoch seconds."""
|
||||||
if val is None:
|
if val is None:
|
||||||
@@ -395,7 +424,9 @@ class JobScheduler:
|
|||||||
name = str(var.get("name") or "").strip()
|
name = str(var.get("name") or "").strip()
|
||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
env_key = re.sub(r"[^A-Za-z0-9_]", "_", name.upper())
|
env_key = _canonical_env_key(name)
|
||||||
|
if not env_key:
|
||||||
|
continue
|
||||||
default_val = var.get("default")
|
default_val = var.get("default")
|
||||||
if default_val is None and "defaultValue" in var:
|
if default_val is None and "defaultValue" in var:
|
||||||
default_val = var.get("defaultValue")
|
default_val = var.get("defaultValue")
|
||||||
@@ -404,7 +435,9 @@ class JobScheduler:
|
|||||||
env_map[env_key] = _env_string(default_val)
|
env_map[env_key] = _env_string(default_val)
|
||||||
doc_names[name] = True
|
doc_names[name] = True
|
||||||
for name, val in overrides.items():
|
for name, val in overrides.items():
|
||||||
env_key = re.sub(r"[^A-Za-z0-9_]", "_", name.upper())
|
env_key = _canonical_env_key(name)
|
||||||
|
if not env_key:
|
||||||
|
continue
|
||||||
env_map[env_key] = _env_string(val)
|
env_map[env_key] = _env_string(val)
|
||||||
|
|
||||||
variables: List[Dict[str, Any]] = []
|
variables: List[Dict[str, Any]] = []
|
||||||
@@ -423,6 +456,7 @@ class JobScheduler:
|
|||||||
for name, val in overrides.items():
|
for name, val in overrides.items():
|
||||||
if name not in doc_names:
|
if name not in doc_names:
|
||||||
variables.append({"name": name, "value": val})
|
variables.append({"name": name, "value": val})
|
||||||
|
env_map = _expand_env_aliases(env_map, variables)
|
||||||
timeout_seconds = 0
|
timeout_seconds = 0
|
||||||
try:
|
try:
|
||||||
timeout_seconds = max(0, int(doc.get("timeout_seconds") or 0))
|
timeout_seconds = max(0, int(doc.get("timeout_seconds") or 0))
|
||||||
|
|||||||
@@ -2816,6 +2816,35 @@ def _safe_filename(rel_path: str) -> str:
|
|||||||
return rel_path or ""
|
return rel_path or ""
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_env_key(name: Any) -> str:
|
||||||
|
try:
|
||||||
|
return re.sub(r"[^A-Za-z0-9_]", "_", str(name or "").strip()).upper()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_env_aliases(env_map: Dict[str, str], variables: List[Dict[str, Any]]) -> Dict[str, str]:
|
||||||
|
expanded: Dict[str, str] = dict(env_map or {})
|
||||||
|
if not isinstance(variables, list):
|
||||||
|
return expanded
|
||||||
|
for var in variables:
|
||||||
|
if not isinstance(var, dict):
|
||||||
|
continue
|
||||||
|
name = str(var.get("name") or "").strip()
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
canonical = _canonical_env_key(name)
|
||||||
|
if not canonical or canonical not in expanded:
|
||||||
|
continue
|
||||||
|
value = expanded[canonical]
|
||||||
|
alias = re.sub(r"[^A-Za-z0-9_]", "_", name)
|
||||||
|
if alias and alias not in expanded:
|
||||||
|
expanded[alias] = value
|
||||||
|
if alias != name and re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name) and name not in expanded:
|
||||||
|
expanded[name] = value
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/scripts/quick_run", methods=["POST"])
|
@app.route("/api/scripts/quick_run", methods=["POST"])
|
||||||
def scripts_quick_run():
|
def scripts_quick_run():
|
||||||
"""Queue a Quick Job to agents via WebSocket and record Running status.
|
"""Queue a Quick Job to agents via WebSocket and record Running status.
|
||||||
@@ -2860,7 +2889,7 @@ def scripts_quick_run():
|
|||||||
name = str(var.get("name") or "").strip()
|
name = str(var.get("name") or "").strip()
|
||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
env_key = re.sub(r"[^A-Za-z0-9_]", "_", name.upper())
|
env_key = _canonical_env_key(name)
|
||||||
default_val = var.get("default")
|
default_val = var.get("default")
|
||||||
if default_val is None and "defaultValue" in var:
|
if default_val is None and "defaultValue" in var:
|
||||||
default_val = var.get("defaultValue")
|
default_val = var.get("defaultValue")
|
||||||
@@ -2877,7 +2906,7 @@ def scripts_quick_run():
|
|||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
overrides[name] = val
|
overrides[name] = val
|
||||||
env_key = re.sub(r"[^A-Za-z0-9_]", "_", name.upper())
|
env_key = _canonical_env_key(name)
|
||||||
env_map[env_key] = _env_string(val)
|
env_map[env_key] = _env_string(val)
|
||||||
|
|
||||||
variables: List[Dict[str, Any]] = []
|
variables: List[Dict[str, Any]] = []
|
||||||
@@ -2896,6 +2925,7 @@ def scripts_quick_run():
|
|||||||
for name, val in overrides.items():
|
for name, val in overrides.items():
|
||||||
if name not in doc_names:
|
if name not in doc_names:
|
||||||
variables.append({"name": name, "value": val})
|
variables.append({"name": name, "value": val})
|
||||||
|
env_map = _expand_env_aliases(env_map, variables)
|
||||||
timeout_seconds = 0
|
timeout_seconds = 0
|
||||||
try:
|
try:
|
||||||
timeout_seconds = max(0, int(doc.get("timeout_seconds") or 0))
|
timeout_seconds = max(0, int(doc.get("timeout_seconds") or 0))
|
||||||
|
|||||||
Reference in New Issue
Block a user