-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuninstall.py
More file actions
117 lines (101 loc) · 4.08 KB
/
Copy pathuninstall.py
File metadata and controls
117 lines (101 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python3
"""Remove only the global-hook fallback installed by install.py."""
from __future__ import annotations
from pathlib import Path
import sys
from install import (
INSTALL_MANIFEST_NAME,
INSTALLED_SCRIPT_NAME,
LEGACY_V1_0_1_SHA256,
MATCHER,
backup_file,
codex_home_from_environment,
find_legacy_handler,
handlers_targeting_script,
load_install_manifest,
load_hooks,
remove_owned_handler,
sha256_file,
transactional_apply,
json_bytes,
)
def uninstall(codex_home: Path) -> None:
hooks_path = codex_home / "hooks.json"
installed_script = codex_home / "hooks" / INSTALLED_SCRIPT_NAME
manifest_path = codex_home / "hooks" / INSTALL_MANIFEST_NAME
payload = load_hooks(hooks_path)
pre_tool = payload.get("hooks", {}).get("PreToolUse", [])
if not isinstance(pre_tool, list):
raise RuntimeError("Refusing to modify {} because PreToolUse is not a list".format(hooks_path))
manifest = load_install_manifest(manifest_path)
if manifest is not None:
if manifest.get("script_path") != str(installed_script):
raise RuntimeError("Ownership manifest points to a different script; refusing uninstall")
if installed_script.exists() and sha256_file(installed_script) != manifest["script_sha256"]:
raise RuntimeError(
"Installed guard was modified after installation; refusing to remove it. "
"It was left in place. Review {} and {} and clean up the exact script and "
"matching hook manually only if that is intentional.".format(
installed_script,
manifest_path,
)
)
matcher = manifest["matcher"]
handler = manifest["handler"]
elif installed_script.exists():
if sha256_file(installed_script) != LEGACY_V1_0_1_SHA256:
raise RuntimeError(
"Existing script is not an owned Codex Destructive Guard installation: {}".format(
installed_script
)
)
handler = find_legacy_handler(pre_tool, installed_script)
if handler is None:
raise RuntimeError("Legacy guard script has no exact matching hook handler; refusing uninstall")
matcher = MATCHER
else:
matcher = ""
handler = {}
if handler:
targeting_handlers = handlers_targeting_script(pre_tool, installed_script)
owned_matches = [
candidate
for candidate_matcher, candidate in targeting_handlers
if candidate_matcher == matcher and candidate == handler
]
if len(owned_matches) != 1 or len(targeting_handlers) != 1:
raise RuntimeError(
"Hook ownership is ambiguous; refusing to remove the script or any handler. "
"Review {} and {} manually.".format(hooks_path, installed_script)
)
updated_pre_tool, removed = remove_owned_handler(pre_tool, matcher, handler) if handler else (pre_tool, 0)
writes = {}
if removed:
payload["hooks"]["PreToolUse"] = updated_pre_tool
writes[hooks_path] = (json_bytes(payload), 0o600)
deletes = []
if installed_script.exists():
deletes.append(installed_script)
if manifest_path.exists():
deletes.append(manifest_path)
changed_paths = tuple(writes) + tuple(deletes)
backups = [backup_file(path, codex_home) for path in changed_paths]
if writes or deletes:
transactional_apply(writes, deletes)
for backup in backups:
if backup:
print("Backup: {}".format(backup))
if changed_paths:
print("Removed the global Codex Destructive Guard fallback.")
else:
print("No owned global Codex Destructive Guard fallback was installed.")
print("Restart Codex to refresh its hook list.")
def main() -> int:
try:
uninstall(codex_home_from_environment())
except Exception as exc:
print("Uninstall failed: {}".format(exc), file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())