Propagate MDM setup failures via exit code across all tools#187
Propagate MDM setup failures via exit code across all tools#187MohamedAklamaash wants to merge 1 commit into
Conversation
The MDM orchestrator (mdm/onboard.py) gates each tool's success on the subprocess returncode. Every tool's setup.py main() returned None on its error paths and __main__ exited 0 unless an exception was raised, so any mid-run failure (missing privileges, MDM key fetch, env var write, hook config) reported success to the orchestrator. This mirrors the exit-code contract landed for copilot in #171: - main() returns True on success / clear, False on every error path - __main__ captures the result and exits 1 on KeyboardInterrupt, on exception, or when main() returns falsy; 0 only on real success Applied to augment, claude-code (gateway + hooks), codex (gateway + hooks), cursor, and gemini-cli. The copilot per-user read-as-root/ write-as-user refactor is not ported: those tools download the hook script as the dropped target user into the user's own home, so they never had copilot's cross-privilege read failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 98cb63a. Configure here.
| if clear_mode: | ||
| clear_setup() | ||
| return | ||
| return True |
There was a problem hiding this comment.
Clear mode false success
High Severity
In --clear mode, main() always returns True right after clear_setup(), even when clear_setup() exits early (for example missing root/admin) without performing cleanup. The new __main__ block then exits 0, so mdm/onboard.py treats a failed uninstall as success.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 98cb63a. Configure here.
| clear_setup() | ||
| return | ||
| return True |
There was a problem hiding this comment.
Clear Failure Exits Successfully
When --clear runs without root or a cleanup step only prints a failure, clear_setup() returns without raising and this branch still returns True. The new sys.exit(0 if ok else 1) path then reports success to the MDM orchestrator even though cleanup did not happen or only partially completed.
🛡️ Automated Security Review (consensus)0 findings — 0 high-confidence, 0 to triage. Reviewers: Cursor, Claude, Semgrep, Gitleaks. ✅ Security consensus: no issues found. (reviewers: Cursor, Claude, Semgrep, Gitleaks) 🤖 consensus review · reviewers: Cursor,Claude,Semgrep,Gitleaks · head |


What
Follow-up to #171 (review request: "keep other tools' MDM scripts in sync"). Ports the exit-code contract from the copilot fix to every other tool's MDM
setup.py.Why
mdm/onboard.pygates each tool's success on the subprocess returncode. But in every other tool'ssetup.py:main()used barereturn(→None) on all error paths, and__main__calledmain()then exited0unless an exception was raised.So any mid-run failure — missing root, MDM key fetch, env-var write, hook/managed-settings config — silently reported success to the orchestrator. This is the same silent-failure class that caused the original copilot symptom (tool "installed" per the orchestrator, but artifacts missing on the machine).
Change (uniform across all 7)
main()returnsTrueon success /--clear,Falseon every error path.__main__captures the result andsys.exit(1)onKeyboardInterrupt, on exception, or whenmain()is falsy;sys.exit(0)only on real success.Files:
augment/hooks,claude-code/{gateway,hooks},codex/{gateway,hooks},cursor,gemini-cli/gateway.Scope / what's intentionally NOT ported
The copilot-specific per-user read-as-root / write-as-user refactor (
_fetch_hook_script,_write_file) is not carried over. Those tools download the hook script as the dropped target user directly into the user's own home, so they never had copilot's cross-privilege read failure. Only the exit-code contract is broadly applicable.Verification
python3 -m py_compilepasses on all 7 files.returnremains in anymain()body.__main__block.🤖 Generated with Claude Code
Note
Low Risk
Behavior-only change to process exit codes; no install logic or security paths modified.
Overview
Aligns seven MDM
setup.pyscripts (Augment, Claude gateway/hooks, Codex gateway/hooks, Cursor, Gemini gateway) with the subprocess contract thatmdm/onboard.pyalready uses: success only whenreturncode == 0.main()now returnsTrueon success and on--clear, andFalseon every validation/setup failure path (missing admin, bad args, MDM key fetch, env writes, hook/managed-settings config, etc.) instead of barereturn(None).The
__main__block captures that result and callssys.exit(0 if ok else 1), withsys.exit(1)onKeyboardInterruptand exceptions (replacingexit(1)where applicable).This stops mid-run failures from exiting 0 and being reported as installed when artifacts were never applied—the same silent-failure class as the earlier Copilot fix.
Reviewed by Cursor Bugbot for commit 98cb63a. Bugbot is set up for automated code reviews on this repo. Configure here.
Greptile Summary
This PR makes MDM setup scripts report failures through process exit codes. The main changes are:
TrueandFalsereturns from setupmain()functions.Confidence Score: 4/5
The setup failure paths mostly behave correctly, but the clear path can still report success after failed cleanup.
sysimports and explicit boolean results.--clearreturns success without checking whether cleanup actually completed.The mirrored
--clearbranches in the changed MDM setup scripts.Important Files Changed
Reviews (1): Last reviewed commit: "Propagate MDM setup failures via exit co..." | Re-trigger Greptile