fix(hlcc): persist sandbox-compatible shared mode

This commit is contained in:
冰朔 2026-08-06 16:28:56 +08:00
commit 612e0b810d
5 changed files with 107 additions and 10 deletions

View file

@ -0,0 +1,62 @@
{
"schema": "guanghu.architecture-provision-request/v1",
"request_id": "HLCC-NUMERIC-SHARED-MODE-PERSIST-20260806",
"target_node": "JD-FD-PRIMARY",
"architecture_id": "GLS-HLCC-009",
"module": {
"code": "HLCC-JD-CANDIDATE-01",
"name": "光湖代码频道数值共享模式持久化",
"bind": "loopback:3340,3341",
"owner": "systemd",
"unit": "hlcc-jd-candidate.service",
"run_user": "guanghu",
"writable_paths": [
"/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1"
],
"read_only_paths": []
},
"source_ref": "REPO-012:refs/heads/main",
"deployed_commit_policy": "use the exact human-merged commit and record it in the server-owned deployment receipt",
"source_paths": [
"server-tools/hololake-code-channel/jd-candidate/hlcc-bootstrap.py",
"server-tools/hololake-code-channel/jd-candidate/app.ini",
"server-tools/hololake-code-channel/jd-candidate/hlcc-jd-candidate.service"
],
"initial_provision": {
"kind": "new-architecture-unit",
"not_an_existing_action_bridge_extension": true,
"requires": [
"persist core.sharedRepository=0660 for compatibility with RestrictSUIDSGID",
"preserve UMask=0077 for all non-repository code-channel state",
"keep the obsolete recursive chmod repository hook absent",
"quote the generated Forgejo custom-hook basename test to prevent argument expansion warnings",
"restart only hlcc-jd-candidate.service",
"do not alter repository content, branches, owner identity or authentication"
]
},
"verification": [
"the automatic deployment receipt records DEPLOYED_AND_VERIFIED",
"the candidate subsequently reaches ready=true and mode=isolated-candidate",
"the exact repository reports core.sharedRepository=0660",
"the obsolete repository chmod hook remains absent",
"flat branch create and delete complete without chmod or shell argument warnings",
"both code-channel and direct-receiver repository checks pass"
],
"runtime_check": {
"url": "http://127.0.0.1:3341/health",
"expected": {
"ok": true,
"mode": "bootstrap",
"version": "16.0.1",
"code": "HLCC-JD-CANDIDATE-01",
"package_profile": "full-offline-v16.0.1"
}
},
"rollback": [
"restore the backed-up hlcc-jd-candidate.service",
"restart the previous immutable release",
"retain repository content and authentication state",
"retain the server-owned hot-repair receipts for manual recovery"
],
"status": "ARCHITECTURE_PACKAGE_READY · INITIAL_PROVISION_PENDING"
}

View file

@ -482,8 +482,9 @@ def seed_fifth_domain_channel(binary: pathlib.Path) -> str:
def configure_shared_channel_repository(
repository: pathlib.Path = CHANNEL_REPOSITORY_PATH,
generated_hook: pathlib.Path | None = None,
) -> str:
"""Use Git's native shared-repository mode instead of a post-receive chmod."""
"""Use sandbox-compatible native sharing instead of post-receive chmod."""
if not repository.is_dir():
raise RuntimeError("channel repository path unavailable")
@ -494,7 +495,7 @@ def configure_shared_channel_repository(
str(repository),
"config",
"core.sharedRepository",
"group",
"0660",
],
check=True,
capture_output=True,
@ -513,7 +514,7 @@ def configure_shared_channel_repository(
capture_output=True,
text=True,
).stdout.strip()
if configured not in {"1", "group"}:
if configured != "0660":
raise RuntimeError("shared repository configuration verification failed")
obsolete_hook = (
@ -530,6 +531,23 @@ def configure_shared_channel_repository(
raise RuntimeError("obsolete sharing hook identity mismatch")
obsolete_hook.unlink()
generated_hook = generated_hook or (
STATE_ROOT / "data" / "data" / "home" / "hooks" / "post-receive"
)
if not generated_hook.is_file() or generated_hook.is_symlink():
raise RuntimeError("generated post-receive hook path unavailable")
generated_text = generated_hook.read_text(encoding="utf-8")
unsafe_test = 'if [ $(basename "${hook}") != "gitea" ]; then'
safe_test = 'if [ "$(basename "${hook}")" != "gitea" ]; then'
if unsafe_test in generated_text:
generated_hook.write_text(
generated_text.replace(unsafe_test, safe_test, 1),
encoding="utf-8",
)
generated_hook.chmod(0o700)
elif safe_test not in generated_text:
raise RuntimeError("generated post-receive hook identity mismatch")
return "configured"

View file

@ -48,8 +48,10 @@ assert.match(bootstrap, /access_tokens_migrated": False/);
assert.match(bootstrap, /repositories_migrated": False/);
assert.match(bootstrap, /delete from access_token/);
assert.match(bootstrap, /core\.sharedRepository/);
assert.match(bootstrap, /"group"/);
assert.match(bootstrap, /"0660"/);
assert.match(bootstrap, /obsolete sharing hook identity mismatch/);
assert.match(bootstrap, /generated post-receive hook identity mismatch/);
assert.match(bootstrap, /basename "\$\{hook\}"/);
assert.match(bootstrap, /configure_shared_channel_repository\(\)/);
assert.doesNotMatch(
bootstrap,

View file

@ -150,9 +150,14 @@ class SharedRepositoryTests(unittest.TestCase):
"#!/bin/sh\nprintf '%s\\n' post_receive_permissions_reconciled\n",
encoding="utf-8",
)
generated_hook = pathlib.Path(temporary) / "generated-post-receive"
generated_hook.write_text(
'#!/usr/bin/env bash\nif [ $(basename "${hook}") != "gitea" ]; then\n :\nfi\n',
encoding="utf-8",
)
self.assertEqual(
MODULE.configure_shared_channel_repository(repository),
MODULE.configure_shared_channel_repository(repository, generated_hook),
"configured",
)
configured = subprocess.run(
@ -168,8 +173,12 @@ class SharedRepositoryTests(unittest.TestCase):
capture_output=True,
text=True,
).stdout.strip()
self.assertIn(configured, {"1", "group"})
self.assertEqual(configured, "0660")
self.assertFalse(hook.exists())
self.assertIn(
'if [ "$(basename "${hook}")" != "gitea" ]; then',
generated_hook.read_text(encoding="utf-8"),
)
def test_refuses_unknown_hook_content(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
@ -178,9 +187,14 @@ class SharedRepositoryTests(unittest.TestCase):
hook = repository / "hooks" / "post-receive.d" / "guanghu-ice-heart-share"
hook.parent.mkdir(parents=True)
hook.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
generated_hook = pathlib.Path(temporary) / "generated-post-receive"
generated_hook.write_text(
'#!/usr/bin/env bash\nif [ $(basename "${hook}") != "gitea" ]; then\n :\nfi\n',
encoding="utf-8",
)
with self.assertRaisesRegex(RuntimeError, "identity mismatch"):
MODULE.configure_shared_channel_repository(repository)
MODULE.configure_shared_channel_repository(repository, generated_hook)
self.assertTrue(hook.exists())

View file

@ -125,9 +125,10 @@ node server-tools/lake-lamp-authz/authorize-repo-push.js \
`safe.directory`,不得使用通配符。
光湖代码频道本身保留 `UMask=0077`,以免放宽数据库和其他状态目录。对需要由直达
接收器共同读写的精确裸仓库设置 Git 原生 `core.sharedRepository=group`,让新建的
`objects``refs` 从写入时就继承专用共享组权限。不要安装推送后递归 `chmod`
钩子,也不要为了共享一个裸仓库而修改整个代码频道服务的 UMask。
接收器共同读写的精确裸仓库设置 Git 原生 `core.sharedRepository=0660`。数值模式让
新建文件对专用共享组可读写,同时不会要求安全沙箱禁止的 setgid 操作。部署分支使用
不含斜杠的扁平名称,避免由代码频道服务临时创建新的私有引用目录。不要安装推送后递归
`chmod` 的钩子,也不要为了共享一个裸仓库而修改整个代码频道服务的 UMask。
部署后用当前主分支生成无变化验收 bundle`receiveBundle` 完整执行一次;验收前后
主分支 SHA 必须一致,并且代码频道账户与授权服务账户执行 `git fsck` 均通过。