fix(hlcc): use native shared repository permissions
This commit is contained in:
parent
93a75fb33e
commit
556f4898f0
6 changed files with 179 additions and 44 deletions
|
|
@ -26,6 +26,13 @@ STATE_ROOT = pathlib.Path("/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1")
|
|||
LEGACY_DB = STATE_ROOT / "data" / "owner-identity-source.db"
|
||||
OWNER_NAME = "bingshuo"
|
||||
CHANNEL_REPOSITORY = "guanghu-ice-heart"
|
||||
CHANNEL_REPOSITORY_PATH = (
|
||||
STATE_ROOT
|
||||
/ "data"
|
||||
/ "repositories"
|
||||
/ OWNER_NAME
|
||||
/ f"{CHANNEL_REPOSITORY}.git"
|
||||
)
|
||||
LEGACY_REPOSITORY_URL = "https://guanghulab.com/fifth-domain/bingshuo/fifth-domain.git"
|
||||
SEED_COMMIT_NUMBER = "HLCC-ICE-000001"
|
||||
SEED_CONTRIBUTION_NUMBER = "ZY-CONTRIB-20260723-001"
|
||||
|
|
@ -473,6 +480,59 @@ def seed_fifth_domain_channel(binary: pathlib.Path) -> str:
|
|||
delete_bootstrap_token(channel_db, token_name)
|
||||
|
||||
|
||||
def configure_shared_channel_repository(
|
||||
repository: pathlib.Path = CHANNEL_REPOSITORY_PATH,
|
||||
) -> str:
|
||||
"""Use Git's native shared-repository mode instead of a post-receive chmod."""
|
||||
if not repository.is_dir():
|
||||
raise RuntimeError("channel repository path unavailable")
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"--git-dir",
|
||||
str(repository),
|
||||
"config",
|
||||
"core.sharedRepository",
|
||||
"group",
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
configured = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"--git-dir",
|
||||
str(repository),
|
||||
"config",
|
||||
"--get",
|
||||
"core.sharedRepository",
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
if configured not in {"1", "group"}:
|
||||
raise RuntimeError("shared repository configuration verification failed")
|
||||
|
||||
obsolete_hook = (
|
||||
repository
|
||||
/ "hooks"
|
||||
/ "post-receive.d"
|
||||
/ "guanghu-ice-heart-share"
|
||||
)
|
||||
if obsolete_hook.exists():
|
||||
if obsolete_hook.is_symlink() or not obsolete_hook.is_file():
|
||||
raise RuntimeError("obsolete sharing hook path is unsafe")
|
||||
hook_text = obsolete_hook.read_text(encoding="utf-8")
|
||||
if "post_receive_permissions_reconciled" not in hook_text:
|
||||
raise RuntimeError("obsolete sharing hook identity mismatch")
|
||||
obsolete_hook.unlink()
|
||||
|
||||
return "configured"
|
||||
|
||||
|
||||
def bootstrap() -> None:
|
||||
process: subprocess.Popen[bytes] | None = None
|
||||
try:
|
||||
|
|
@ -514,6 +574,8 @@ def bootstrap() -> None:
|
|||
migrate_owner_identity()
|
||||
set_status(stage="seeding-fifth-domain-channel")
|
||||
seed_fifth_domain_channel(binary)
|
||||
set_status(stage="configuring-shared-repository")
|
||||
configure_shared_channel_repository()
|
||||
set_status(mode="isolated-candidate", ready=True, stage="ready")
|
||||
return_code = process.wait()
|
||||
raise RuntimeError(f"candidate stopped with code {return_code}")
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ assert.match(bootstrap, /password_hash_preserved/);
|
|||
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, /obsolete sharing hook identity mismatch/);
|
||||
assert.match(bootstrap, /configure_shared_channel_repository\(\)/);
|
||||
assert.doesNotMatch(
|
||||
bootstrap,
|
||||
/print\s*\([^)]*token|stderr\.write\s*\([^)]*token|stdout\.write\s*\([^)]*token/,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import importlib.util
|
|||
import json
|
||||
import pathlib
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
|
@ -138,5 +139,50 @@ class OwnerMigrationTests(unittest.TestCase):
|
|||
self.assertEqual(MODULE.migrate_owner_identity(self.old, self.new, self.receipt), "already-present")
|
||||
|
||||
|
||||
class SharedRepositoryTests(unittest.TestCase):
|
||||
def test_configures_native_group_sharing_and_removes_only_known_hook(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
repository = pathlib.Path(temporary) / "channel.git"
|
||||
subprocess.run(["git", "init", "--bare", str(repository)], check=True, capture_output=True)
|
||||
hook = repository / "hooks" / "post-receive.d" / "guanghu-ice-heart-share"
|
||||
hook.parent.mkdir(parents=True)
|
||||
hook.write_text(
|
||||
"#!/bin/sh\nprintf '%s\\n' post_receive_permissions_reconciled\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
MODULE.configure_shared_channel_repository(repository),
|
||||
"configured",
|
||||
)
|
||||
configured = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"--git-dir",
|
||||
str(repository),
|
||||
"config",
|
||||
"--get",
|
||||
"core.sharedRepository",
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
self.assertIn(configured, {"1", "group"})
|
||||
self.assertFalse(hook.exists())
|
||||
|
||||
def test_refuses_unknown_hook_content(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
repository = pathlib.Path(temporary) / "channel.git"
|
||||
subprocess.run(["git", "init", "--bare", str(repository)], check=True, capture_output=True)
|
||||
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")
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "identity mismatch"):
|
||||
MODULE.configure_shared_channel_repository(repository)
|
||||
self.assertTrue(hook.exists())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in a new issue