feat(ci): 通知链接改用非秘密配置 CI_PUBLIC_BASE_URL(run/提交/变更统一),缺失或非法明确回退
build / build (push) Successful in 49s
build / build (push) Successful in 49s
为什么要改:通知里的链接一直用 GITHUB_SERVER_URL 拼(内网地址),群里/换网就点不开。 做法(不覆盖 GITHUB_SERVER_URL、不碰检出与内部调用): - ci_report.py 新增 _link_base():CI_PUBLIC_BASE_URL 优先,缺/非法(非 http(s)、带 userinfo、带空白)⇒ 明确回退 GITHUB_SERVER_URL,并把来源写进 payload 的 link_base_source(可复盘)。 - 三类链接统一用它:run_url、commit_url(新增,提交行用短 SHA 展示、完整 SHA 在链接里)、compare_url;仓库必须是真 owner/repo、SHA 必须是完整 40 位,否则宁可不给链接。 - workflow 上报步骤加非秘密 CI_PUBLIC_BASE_URL: https://gitea.zheromiku.top(写在 YAML 里便于审查)。 - **不做消息文本的全局 URL 替换**:提交标题/摘要里的 URL 仍是文本(bridge 侧负例钉住)。 - 自检补 _link_base/_commit_url 分支(缺失回退、非法回退、尾斜杠、短 SHA 不拼链接)。
This commit is contained in:
@@ -182,6 +182,37 @@ def self_test() -> int:
|
||||
_, got, _ = verify(rep, expect, in_ci)
|
||||
if len(got) != want:
|
||||
problems.append(f"{name}:期望 {want} 个问题,实际 {len(got)} 个 {got}")
|
||||
# 通知链接基址:优先 CI_PUBLIC_BASE_URL,缺/非法都必须**明确回退**(不猜、不静默)
|
||||
env_backup = {k: os.environ.get(k) for k in ("CI_PUBLIC_BASE_URL", "GITHUB_SERVER_URL")}
|
||||
try:
|
||||
os.environ["GITHUB_SERVER_URL"] = "http://internal:3000"
|
||||
os.environ.pop("CI_PUBLIC_BASE_URL", None)
|
||||
b, src = _link_base()
|
||||
if b != "http://internal:3000" or "GITHUB_SERVER_URL" not in src:
|
||||
problems.append(f"没配 CI_PUBLIC_BASE_URL 时没回退到 GITHUB_SERVER_URL:{b!r}/{src!r}")
|
||||
os.environ["CI_PUBLIC_BASE_URL"] = "https://gitea.zheromiku.top/"
|
||||
b2, src2 = _link_base()
|
||||
if b2 != "https://gitea.zheromiku.top" or src2 != "CI_PUBLIC_BASE_URL":
|
||||
problems.append(f"配了 CI_PUBLIC_BASE_URL 没用它(或没去掉尾斜杠):{b2!r}/{src2!r}")
|
||||
for bad in ("gitea.zheromiku.top", "ftp://gitea.zheromiku.top",
|
||||
"https://u:p@gitea.zheromiku.top", "https://a b/"):
|
||||
os.environ["CI_PUBLIC_BASE_URL"] = bad
|
||||
b3, src3 = _link_base()
|
||||
if b3 != "http://internal:3000" or "回退" not in src3:
|
||||
problems.append(f"非法 CI_PUBLIC_BASE_URL({bad!r}) 没明确回退:{b3!r}/{src3!r}")
|
||||
os.environ["CI_PUBLIC_BASE_URL"] = "https://gitea.zheromiku.top"
|
||||
if _commit_url("a/b", "c" * 40, "https://gitea.zheromiku.top") != \
|
||||
f"https://gitea.zheromiku.top/a/b/commit/{'c' * 40}":
|
||||
problems.append("提交链接拼错")
|
||||
for bad_repo, bad_sha in (("onlyone", "c" * 40), ("a/b", "c" * 12), ("a/b", "")):
|
||||
if _commit_url(bad_repo, bad_sha, "https://gitea.zheromiku.top"):
|
||||
problems.append(f"仓库/短 SHA 不合法却仍拼出链接:{bad_repo!r}/{bad_sha[:12]!r}")
|
||||
finally:
|
||||
for k2, v2 in env_backup.items():
|
||||
if v2 is None:
|
||||
os.environ.pop(k2, None)
|
||||
else:
|
||||
os.environ[k2] = v2
|
||||
if problems:
|
||||
print("❌ 上报核对自检失败:", file=sys.stderr)
|
||||
for p in problems:
|
||||
@@ -228,9 +259,47 @@ def _scope_for_notify():
|
||||
return [], "", f"{e.__class__.__name__}"
|
||||
|
||||
|
||||
def _compare_url(repo: str) -> str:
|
||||
def _link_base() -> tuple:
|
||||
"""通知里的链接用哪个基址:**CI_PUBLIC_BASE_URL 优先**,没配/非法就退回 GITHUB_SERVER_URL。
|
||||
|
||||
只接受 `http(s)://host[:port][/path]` 的绝对地址、不接受带凭据(userinfo)的写法。
|
||||
返回 (base, source);source 会写进上报与 CI 日志,方便事后核对"这条链接是谁给的基址"。
|
||||
|
||||
⚠️ 这是**唯一的**链接基址来源:不做"把消息文本里的 URL 全部替换一遍"那种事 ——
|
||||
那样会把提交标题、日志摘要里的 URL 也改掉(那些是事实,不许动)。
|
||||
"""
|
||||
pub = (os.environ.get("CI_PUBLIC_BASE_URL") or "").strip()
|
||||
fallback = (os.environ.get("GITHUB_SERVER_URL") or "").strip().rstrip("/")
|
||||
if pub:
|
||||
bad = None
|
||||
if not re.match(r"^https?://", pub):
|
||||
bad = "不是 http(s) 绝对地址"
|
||||
elif " " in pub or " " in pub:
|
||||
bad = "含空白字符"
|
||||
else:
|
||||
m = re.match(r"^[a-z]+://([^/]+)", pub, re.I)
|
||||
if not m or "@" in m.group(1):
|
||||
bad = "主机部分不合法或带凭据(userinfo)"
|
||||
if bad:
|
||||
return fallback, f"CI_PUBLIC_BASE_URL 非法({bad})⇒ 回退 GITHUB_SERVER_URL"
|
||||
return pub.rstrip("/"), "CI_PUBLIC_BASE_URL"
|
||||
return fallback, "GITHUB_SERVER_URL(未配 CI_PUBLIC_BASE_URL)"
|
||||
|
||||
|
||||
def _repo_ok(repo: str) -> bool:
|
||||
"""链接里的仓库必须是真的 owner/repo(两段、都非空),否则宁可不给链接。"""
|
||||
parts = [x for x in str(repo or "").split("/")]
|
||||
return len(parts) == 2 and all(x.strip() for x in parts)
|
||||
|
||||
|
||||
def _commit_url(repo: str, sha: str, base: str) -> str:
|
||||
if not _repo_ok(repo) or not base or not re.match(r"^[0-9a-f]{40}$", str(sha or "")):
|
||||
return ""
|
||||
return f"{base}/{repo}/commit/{sha}"
|
||||
|
||||
|
||||
def _compare_url(repo: str, base: str) -> str:
|
||||
"""变更列表链接:只有拿到真实的 before/after 才给(不然就是编一个打不开的地址)。"""
|
||||
base = (os.environ.get("GITHUB_SERVER_URL") or "").rstrip("/")
|
||||
ev = (os.environ.get("GITHUB_EVENT_PATH") or "").strip()
|
||||
if not base or not repo or not ev or not os.path.exists(ev):
|
||||
return ""
|
||||
@@ -351,6 +420,15 @@ def main(argv: list[str]) -> int:
|
||||
if problems:
|
||||
print("❌ 汇总不可信/不完整 ⇒ 不能报绿(作业这次不算通过)", file=sys.stderr)
|
||||
|
||||
_lb_base, _lb_source = _link_base()
|
||||
repo_full = (rep.get("repository") or os.environ.get("GITHUB_REPOSITORY") or "").strip()
|
||||
if _lb_source != "CI_PUBLIC_BASE_URL":
|
||||
print(f"提示:通知链接基址 = {_lb_base or '(未取得)'}({_lb_source})", flush=True)
|
||||
else:
|
||||
print(f"通知链接基址 = {_lb_base}(来自 CI_PUBLIC_BASE_URL)", flush=True)
|
||||
if not _repo_ok(repo_full):
|
||||
print(f"⚠️ 仓库名不是 owner/repo({repo_full[:40]!r})⇒ 通知里不给 run/提交/变更 链接",
|
||||
file=sys.stderr)
|
||||
_scope_commits, _scope_source, _scope_err = _scope_for_notify()
|
||||
_scope_payload = {"commits": _scope_commits, "source": _scope_source,
|
||||
"count": len(_scope_commits)}
|
||||
@@ -384,9 +462,8 @@ def main(argv: list[str]) -> int:
|
||||
"event": rep.get("event"),
|
||||
"run_mode": rep.get("run_mode"),
|
||||
"run_id": os.environ.get("GITHUB_RUN_ID", ""),
|
||||
"run_url": (f"{os.environ.get('GITHUB_SERVER_URL', '')}/"
|
||||
f"{rep.get('repository', '')}/actions/runs/{os.environ.get('GITHUB_RUN_ID', '')}"
|
||||
if os.environ.get("GITHUB_RUN_ID") else ""),
|
||||
"run_url": (f"{_lb_base}/{repo_full}/actions/runs/{os.environ.get('GITHUB_RUN_ID', '')}"
|
||||
if (os.environ.get("GITHUB_RUN_ID") and _lb_base and _repo_ok(repo_full)) else ""),
|
||||
"checks": checks,
|
||||
"manifest": rep.get("manifest"),
|
||||
"problems": (rep.get("problems") or []) + problems,
|
||||
@@ -403,7 +480,10 @@ def main(argv: list[str]) -> int:
|
||||
"commits": _scope_payload["commits"], # 准确范围里的提交标题(≤10 条)
|
||||
"range_source": _scope_payload["source"], # 范围是怎么算出来的(可复盘)
|
||||
"commit_count": (_scope_payload["count"] or payload_count), # 条数由 Git 算
|
||||
"compare_url": _compare_url(rep.get("repository", "")),
|
||||
"compare_url": _compare_url(repo_full, _lb_base),
|
||||
"commit_url": _commit_url(repo_full, rep.get("commit_sha") or "", _lb_base),
|
||||
"link_base": _lb_base, # 链接基址(可复盘)
|
||||
"link_base_source": _lb_source, # 基址从哪来:CI_PUBLIC_BASE_URL / GITHUB_SERVER_URL 回退
|
||||
"run_duration_s": "", # 整次运行耗时:本 workflow 没留起点 ⇒ 不编
|
||||
"duration_kind": "gate",
|
||||
"first_failure": _first_failure(checks),
|
||||
@@ -438,4 +518,4 @@ def main(argv: list[str]) -> int:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
||||
@@ -177,6 +177,11 @@ jobs:
|
||||
env:
|
||||
BRIDGE_URL: ${{ vars.CI_BRIDGE_URL }}
|
||||
BRIDGE_TOKEN: ${{ secrets.CI_BRIDGE_TOKEN }}
|
||||
# 通知里的链接基址(**非秘密**,直接写在 workflow 里便于审查与复盘):
|
||||
# CI 运行 / 提交 / 变更 三类链接统一用它拼;提交与变更里的仓库、SHA、run_id 仍是真实值。
|
||||
# **不覆盖 GITHUB_SERVER_URL**(检出与所有内部调用继续用它,检出地址一个字节没变)。
|
||||
# 删掉这一行 ⇒ ci_report.py 明确回退到 GITHUB_SERVER_URL,并在上报里写 link_base_source。
|
||||
CI_PUBLIC_BASE_URL: https://gitea.zheromiku.top
|
||||
run: |
|
||||
set +e; set +o pipefail
|
||||
B="${BRIDGE_URL:-}"
|
||||
|
||||
Reference in New Issue
Block a user