Files
Unlucky-Launcher/packaging/tests/gpu_shot_ci_expr.py
T
AskaEth a5d1dec495
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / update-e2e (windows x64) (push) Waiting to run
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Waiting to run
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Waiting to run
Signing status(播报,不是门禁) / 判据自测(三行/退出码/变异重放/接线) (push) Waiting to run
Signing status(播报,不是门禁) / 签名就绪度(播报) (push) Waiting to run
Release / 选槽位(默认四个:Windows 两档 + Linux 两档) (push) Waiting to run
release: v0.2.4 里程碑快照(孤儿提交,无父;树 = dev 68362e5)
本批内容见 CHANGELOG.md 的 0.2.4 段。按 D41 用孤儿快照推 main,保留「公开史只有里程碑」的模型。
2026-11-11 21:41:38 +08:00

65 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""GitHub `if:` 表达式的小求值器(M253 / r2 复审返工,判据 3 用)。
为什么需要它:字符串存在性判据被两轮复审证伪过 —— `if: needs.probe.outputs.available == 'true' || always()`
用 `in` 判照样绿,`if: true` 也绿。所以这里**真求值**:给 `needs.probe.outputs.available` 分别取
`'true'` / `'false'` / `''`,门必须分别判真 / 假 / 假 —— 门到底还挂不挂,由语义决定,不由子串决定。
**只认一小撮语法,其余一律抛 `ConditionError`(调用方按红处理,宁可假红)**:
`==` / `!=` / `&&` / `||` / 括号 / 单引号字符串 / 数字 / `needs.<job>.outputs.<名>` / `true|false|null` /
`always()|success()|failure()|cancelled()`(后四个按「假定上游成功」取常量)。**不支持 `!`**:GitHub 与
Python 的 `!` 优先级不同,认不出比误判安全。不是 GitHub 语义的替身,是它的一个保守子集。
"""
from __future__ import annotations
import re
AVAILABLE_IDENT = "needs.probe.outputs.available"
CALL_FUNCS = {"always": True, "success": True, "failure": False, "cancelled": False}
_TOKEN_RE = re.compile(r"(?P<string>'[^']*')|(?P<number>\d+)|(?P<ident>[A-Za-z_][\w.\-]*)|(?P<op>==|!=|&&|\|\||[()!])")
class ConditionError(Exception):
"""`if:` 表达式认不出 —— 认不出即判红,绝不默认放过。"""
def evaluate_condition(raw, available: str) -> bool:
"""把 GitHub 的 `if:` 表达式求值。只认一小撮语法,其余抛 `ConditionError`(= 判红)。"""
text = str(raw).strip()
if text.startswith("${{") and text.endswith("}}"):
text = text[3:-2].strip()
for name, value in CALL_FUNCS.items():
text = re.sub(rf"\b{name}\s*\(\s*\)", "true" if value else "false", text)
if re.search(r"\b(" + "|".join(CALL_FUNCS) + r")\s*\(", text):
raise ConditionError("函数只支持不带参数的形式")
parts: list[str] = []
pos = 0
while pos < len(text):
if text[pos].isspace():
pos += 1
continue
match = _TOKEN_RE.match(text, pos)
if not match:
raise ConditionError(f"认不出的字符 {text[pos]!r}")
pos = match.end()
token = match.group()
if match.lastgroup in ("string", "number"):
parts.append(token)
elif match.lastgroup == "ident":
if token == AVAILABLE_IDENT:
parts.append("__available")
elif token in ("true", "false", "null"):
parts.append({"true": "True", "false": "False", "null": "None"}[token])
else:
raise ConditionError(f"认不出的标识符 `{token}`")
else:
if token == "!":
raise ConditionError("不支持 `!`(GitHub 与 Python 的优先级不同,宁可认不出)")
parts.append({"&&": "and", "||": "or"}.get(token, token))
source = " ".join(parts)
try:
return bool(eval(source, {"__builtins__": {}}, {"__available": available})) # noqa: S307 - 词表是白名单,见上
except Exception as error: # noqa: BLE001 - 任何求值失败都按「认不出」处理
raise ConditionError(f"求值失败({error}):`{source}`") from None