gabriel / muse public
fix patch dev
AI Agent gabriel · 128 days ago · May 9, 2026 · Diff

fix: bridge git-export preserves executable bits via shebang detection (issue #38)

GitExporter.fix_file_modes unconditionally chmod'd every file to 0o644, stripping the executable bit from shell scripts, Node CLI scripts, and any other executable. The help text always advertised 755-for-executables, but the implementation had no concept of executability.

Fix: replace the unconditional 0o644 with shebang-driven heuristic. Files whose first two bytes are '#!' get 0o755; everything else gets 0o644. Add GitExporter._has_shebang(path) static helper — reads 2 bytes, returns False on any OSError (fail-safe), never sets setuid/setgid.

Also change --fix-modes default from False to True (with BooleanOptionalAction so --no-fix-modes opts out), matching the documented behaviour new users expect.

Verified: 613-file bridge of aaronrene/knowtation produces 41 x 100755 and 572 x 100644 — zero false positives, zero false negatives.

Fixes: https://staging.musehub.ai/gabriel/musehub/issues/38

sha256:d69b772f61297a31fe72352c58eb95315f6ec4f60f8a82435f55660f1f7b6e76 sha
+21 ~3 symbols
sha256:691181a9858e75b80f0125c88464e5ecc2e200b0080834b4d6e532b50e17fa26 snapshot
+21
symbols added
~3
symbols modified
0
dead code introduced
Semantic Changes 24 symbols
~ muse/cli/commands/bridge.py .py 1 symbol added, 3 symbols modified
+ _has_shebang method method _has_shebang L2107–2116
~ tests/test_bridge_git_export.py .py 20 symbols added
+ TestBridgeExportShebangEndToEnd class class TestBridgeExportShebangEndToEnd L997–1081
+ test_executable_script_exported_as_100755 method method test_executable_script_exported_as_100755 L1000–1034
+ test_fix_modes_default_is_true method method test_fix_modes_default_is_true L1036–1057
+ test_no_fix_modes_flag_leaves_modes_unchanged method method test_no_fix_modes_flag_leaves_modes_unchanged L1059–1081
+ TestFixFileModesShebang class class TestFixFileModesShebang L923–994
+ _build_exporter method method _build_exporter L926–932
+ test_dotgit_still_never_touched method method test_dotgit_still_never_touched L984–994
+ test_node_script_gets_755 method method test_node_script_gets_755 L949–958
+ test_regular_file_gets_644 method method test_regular_file_gets_644 L960–969
+ test_setuid_bit_never_set method method test_setuid_bit_never_set L971–982
+ test_shebang_script_gets_755 method method test_shebang_script_gets_755 L934–947
+ TestHasShebang class class TestHasShebang L873–920
+ test_bash_shebang method method test_bash_shebang L876–880
+ test_empty_file_no_shebang method method test_empty_file_no_shebang L900–904
+ test_node_shebang method method test_node_shebang L882–886
+ test_nonexistent_file_returns_false method method test_nonexistent_file_returns_false L918–920
+ test_one_byte_file_no_shebang method method test_one_byte_file_no_shebang L906–910
+ test_plain_text_no_shebang method method test_plain_text_no_shebang L894–898
+ test_python_shebang_no_env method method test_python_shebang_no_env L888–892
+ test_shebang_after_leading_whitespace_is_not_shebang method method test_shebang_after_leading_whitespace_is_not_shebang L912–916
← Older Oldest on dev
All commits
Newer → Latest on dev

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:d69b772f61297a31fe72352c58eb95315f6ec4f60f8a82435f55660f1f7b6e76 --body "your comment"