Fetch and build the three pinned RDM SHAs in CI
Why: test-pg only cloned the default branch, so prev_sha and legacy_sha were never built or attested. Evidence: SELF python3 -m unittest tests.test_ci_credentials tests.test_lifecycle_pin (11 tests, exit 0). Impact: the lifecycle suite prints the combination inventory and keeps login and rdm.ping as NOT_RUN. Refs: .task/feature-svc-d3-real-scenarios.md Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -387,7 +387,7 @@ jobs:
|
||||
# 凭据纪律与 Checkout 块同源:经 GIT_CONFIG_* 环境注入 URL 级 token 头,
|
||||
# 不落文件/命令行;allowlist 扩项 = Agent-System-RDM 及其 pins(同一
|
||||
# 组件集合),由本 PR 评审背书。SSH pins URL 以 insteadOf 改写到受信
|
||||
# https 基址后再取。
|
||||
# https 基址后再取。只追加 pin 的三个 40 位 hex,不 source pin 文件。
|
||||
SERVER="$(printf '%s' "$GITHUB_SERVER_URL" | sed 's:/*$::')"
|
||||
scheme="${GITHUB_SERVER_URL%%://*}"
|
||||
export GIT_CONFIG_COUNT=4
|
||||
@@ -400,15 +400,43 @@ jobs:
|
||||
export GIT_CONFIG_KEY_3="core.hooksPath"
|
||||
export GIT_CONFIG_VALUE_3="/dev/null"
|
||||
git clone --depth=1 "$SERVER/weichen/Agent-System-RDM.git" rdm
|
||||
# 凭据环境须保持到 pins 子模块取完(insteadOf + token 头覆盖嵌套 SSH URL),
|
||||
# 之后再 unset。
|
||||
git -C rdm submodule update --init --recursive
|
||||
mapfile -t pins < <(python3 - <<'PY'
|
||||
import pathlib
|
||||
got = {}
|
||||
for line in pathlib.Path("tests/integration/lifecycle/rdm.pin").read_text().splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
if key not in ("sha", "prev_sha", "legacy_sha") or len(value) != 40:
|
||||
raise SystemExit("rdm.pin rejected")
|
||||
if any(c not in "0123456789abcdef" for c in value):
|
||||
raise SystemExit("rdm.pin rejected")
|
||||
got[key] = value
|
||||
if set(got) != {"sha", "prev_sha", "legacy_sha"}:
|
||||
raise SystemExit("rdm.pin rejected")
|
||||
print(got["prev_sha"])
|
||||
print(got["legacy_sha"])
|
||||
print(got["sha"])
|
||||
PY
|
||||
)
|
||||
[ "${#pins[@]}" -eq 3 ]
|
||||
git -C rdm fetch --depth=1 origin "${pins[@]}"
|
||||
for rev in "${pins[@]}"; do
|
||||
git -C rdm checkout --detach "$rev"
|
||||
git -C rdm submodule update --init --recursive
|
||||
done
|
||||
echo "RDM commit: $(git -C rdm rev-parse HEAD)"
|
||||
unset GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0 \
|
||||
GIT_CONFIG_KEY_1 GIT_CONFIG_VALUE_1 GIT_CONFIG_KEY_2 \
|
||||
GIT_CONFIG_VALUE_2 GIT_CONFIG_KEY_3 GIT_CONFIG_VALUE_3
|
||||
unset SUBMODULE_TOKEN
|
||||
make -C rdm build
|
||||
: > rdm/pin-build-digests.txt
|
||||
for rev in "${pins[@]}"; do
|
||||
git -C rdm checkout --detach "$rev"
|
||||
make -C rdm build
|
||||
sha256sum rdm/bin/agent-system-rdm | awk -v rev="$rev" '{print rev, $1}' >> rdm/pin-build-digests.txt
|
||||
done
|
||||
- name: Install Node 22
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -1,18 +1,77 @@
|
||||
"""真实 Dev/RDM 组合门禁。由 make test-lifecycle 调用。
|
||||
|
||||
缺 PostgreSQL、RDM 源码或制品时直接失败,不静默跳过。
|
||||
登录依赖账户 CLI 公共化(账户方案 U7/U5),单独标 NOT_RUN,
|
||||
登录与 rdm.ping 依赖账户 CLI 公共化,单独标 NOT_RUN,
|
||||
不因此把已经跑过的真实组合场景记成失败。
|
||||
"""
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
PIN = Path(__file__).resolve().parent / "rdm.pin"
|
||||
|
||||
|
||||
def sha256_file(path):
|
||||
digest = hashlib.sha256()
|
||||
with open(path, "rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def print_inventory(src, dev, rdm):
|
||||
print("组合清单")
|
||||
head = subprocess.run(["git", "-C", str(ROOT), "rev-parse", "HEAD"],
|
||||
capture_output=True, text=True)
|
||||
print("dev_sha", head.stdout.strip() if head.returncode == 0 else "NOT_RUN")
|
||||
modules = subprocess.run(
|
||||
["git", "-C", str(ROOT), "submodule", "status", "--recursive"],
|
||||
capture_output=True, text=True)
|
||||
if modules.returncode == 0:
|
||||
for line in modules.stdout.splitlines():
|
||||
print("submodule", " ".join(line.split()[:2]))
|
||||
else:
|
||||
print("submodule NOT_RUN")
|
||||
print("pin")
|
||||
for line in PIN.read_text().splitlines():
|
||||
if line.strip() and not line.startswith("#"):
|
||||
print(" ", line.strip())
|
||||
print("dev_bin", dev, sha256_file(dev))
|
||||
print("rdm_bin", rdm, sha256_file(rdm))
|
||||
digests = Path(src) / "pin-build-digests.txt"
|
||||
if digests.is_file():
|
||||
for line in digests.read_text().splitlines():
|
||||
print("pin_build", line)
|
||||
else:
|
||||
print("pin_build NOT_RUN:本进程没有预构建三个 pin")
|
||||
print("pg_version", pg_version())
|
||||
print("登录 NOT_RUN:RDM 二进制没有 user/apikey,工作台登录依赖账户方案 U7/U5")
|
||||
print("rdm.ping NOT_RUN:需要已登录会话,本轮不公共化账户 CLI")
|
||||
|
||||
|
||||
def pg_version():
|
||||
parsed = urlparse(os.environ.get("AGENT_PG_TEST_DSN", ""))
|
||||
if not parsed.hostname:
|
||||
return "NOT_RUN"
|
||||
env = os.environ.copy()
|
||||
env.update({
|
||||
"PGHOST": parsed.hostname,
|
||||
"PGPORT": str(parsed.port or 5432),
|
||||
"PGUSER": parsed.username or "",
|
||||
"PGPASSWORD": parsed.password or "",
|
||||
"PGDATABASE": parsed.path.lstrip("/"),
|
||||
})
|
||||
proc = subprocess.run(["psql", "-tAc", "SHOW server_version"],
|
||||
env=env, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
return "NOT_RUN"
|
||||
return proc.stdout.strip() or "NOT_RUN"
|
||||
|
||||
|
||||
def main():
|
||||
missing = []
|
||||
if not os.environ.get("AGENT_PG_TEST_DSN"):
|
||||
@@ -30,18 +89,12 @@ def main():
|
||||
print("test-lifecycle 失败。缺:%s" % ", ".join(missing))
|
||||
return 1
|
||||
|
||||
print("组合清单")
|
||||
print("pin", PIN.read_text().strip())
|
||||
print("rdm_src", src)
|
||||
print("dev_bin", dev)
|
||||
print("rdm_bin", rdm)
|
||||
print("将运行 tests.test_svc_realcombo:SQLite 身份、RDM 二进制生命周期、PostgreSQL 身份环境交付")
|
||||
print("将运行 tests.integration.lifecycle.test_lifecycle_real:pin 上的真实候选与切换")
|
||||
if os.environ.get("SVC_RDM_ORIGIN"):
|
||||
print("将运行全轨部署:SVC_RDM_ORIGIN 已设置")
|
||||
else:
|
||||
print("全轨部署 NOT_RUN:未设置 SVC_RDM_ORIGIN(由 svc-realcombo 作业覆盖)")
|
||||
print("登录 NOT_RUN:RDM 二进制没有 user/apikey,工作台登录依赖账户方案 U7/U5")
|
||||
env = os.environ.copy()
|
||||
env["SVC_TEST_REAL"] = "1"
|
||||
env["SVC_TEST_PG"] = "1"
|
||||
@@ -52,10 +105,11 @@ def main():
|
||||
"tests.test_svc_realcombo",
|
||||
"tests.integration.lifecycle.test_lifecycle_real", "-q"],
|
||||
cwd=str(ROOT), env=env)
|
||||
print_inventory(src, dev, rdm)
|
||||
if proc.returncode != 0:
|
||||
print("test-lifecycle 失败:真实组合用例未通过")
|
||||
return proc.returncode
|
||||
print("test-lifecycle 可跑场景通过;登录与未设 origin 的全轨部署保持 NOT_RUN")
|
||||
print("test-lifecycle 可跑场景通过;登录与 rdm.ping 保持 NOT_RUN")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,14 @@ def checkout_script():
|
||||
return textwrap.dedent("\n".join(lines[start:end]))
|
||||
|
||||
|
||||
def rdm_clone_script():
|
||||
lines = WORKFLOW.read_text().splitlines()
|
||||
start = next(i for i, line in enumerate(lines) if "Clone Agent-System-RDM" in line)
|
||||
start = next(i for i in range(start, len(lines)) if lines[i].strip() == "run: |") + 1
|
||||
end = next(i for i in range(start, len(lines)) if lines[i].startswith(" - name:"))
|
||||
return textwrap.dedent("\n".join(lines[start:end]))
|
||||
|
||||
|
||||
class CheckoutTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
@@ -236,6 +244,49 @@ class CheckoutTests(unittest.TestCase):
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertFalse(any("untrusted" in path for path, auth in self.requests))
|
||||
|
||||
def test_rdm_pin_fetch_is_three_shas_on_the_allowlist(self):
|
||||
source = self.base / "Agent-System-RDM"
|
||||
source.mkdir()
|
||||
self.git(source, "init", "-q", "-b", "main")
|
||||
shas = []
|
||||
for index in range(3):
|
||||
(source / "Makefile").write_text(
|
||||
"build:\n\tmkdir -p bin\n\tprintf 'rev%d\\n' > bin/agent-system-rdm\n" % index)
|
||||
self.git(source, "add", ".")
|
||||
self.git(source, "-c", "user.name=Fixture",
|
||||
"-c", "user.email=fixture@example.invalid",
|
||||
"commit", "-q", "-m", "rev %d" % index)
|
||||
shas.append(self.git(source, "rev-parse", "HEAD"))
|
||||
target = self.published / "weichen" / "Agent-System-RDM.git"
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.git(self.base, "clone", "-q", "--bare", str(source), str(target))
|
||||
self.git(target, "config", "uploadpack.allowReachableSHA1InWant", "true")
|
||||
self.git(target, "config", "uploadpack.allowAnySHA1InWant", "true")
|
||||
work = self.base / "rdm-job"
|
||||
pin = work / "tests" / "integration" / "lifecycle"
|
||||
pin.mkdir(parents=True)
|
||||
(pin / "rdm.pin").write_text(
|
||||
"sha=%s\nprev_sha=%s\nlegacy_sha=%s\n" % (shas[2], shas[1], shas[0]))
|
||||
script = rdm_clone_script()
|
||||
self.assertNotRegex(script, r"(?m)^\s*source\s")
|
||||
self.requests.clear()
|
||||
result = subprocess.run(
|
||||
["bash", "-c", script], cwd=work,
|
||||
env=self.env | {"SUBMODULE_TOKEN": SUB_TOKEN, "GITHUB_SERVER_URL": self.url},
|
||||
capture_output=True, text=True, timeout=60)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
self.assertNotIn(SUB_TOKEN, result.stdout + result.stderr)
|
||||
self.assertTrue(self.requests)
|
||||
for path, auth in self.requests:
|
||||
self.assertRegex(path, r"^/weichen/Agent-System-RDM\.git/")
|
||||
self.assertEqual(auth, "token " + SUB_TOKEN)
|
||||
for sha in shas:
|
||||
self.assertEqual(self.git(work / "rdm", "cat-file", "-t", sha), "commit")
|
||||
self.assertEqual(self.git(work / "rdm", "rev-parse", "HEAD"), shas[2])
|
||||
digests = (work / "rdm" / "pin-build-digests.txt").read_text()
|
||||
for sha in shas:
|
||||
self.assertIn(sha, digests)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user