railmux 0.4.0.dev26__tar.gz → 0.4.0.dev27__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/MANIFEST.in +1 -0
- {railmux-0.4.0.dev26/src/railmux.egg-info → railmux-0.4.0.dev27}/PKG-INFO +1 -1
- railmux-0.4.0.dev27/src/railmux/__init__.py +1 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_msys2.py +19 -2
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27/src/railmux.egg-info}/PKG-INFO +1 -1
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux.egg-info/SOURCES.txt +2 -1
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_install_log.py +25 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_msys2.py +32 -4
- railmux-0.4.0.dev27/tools/windows_archive_smoke.py +67 -0
- railmux-0.4.0.dev26/src/railmux/__init__.py +0 -1
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/LICENSE +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/README.md +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/pyproject.toml +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/setup.cfg +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/__main__.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/atomic_file.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/background_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/cli.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/codex_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/config_cli.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/diagnostics.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/discovery.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/display_transport.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/entrypoint.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fast_display_client.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fast_display_history.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fast_display_input.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fast_display_protocol.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fast_display_server.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/favorites.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/fuzzy.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/help_workspace.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/launcher.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/legacy_sessions.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/local_clipboard.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/local_open.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/models.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/modes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/mouse_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/orphan_marker.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/pane_surface.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/path_codec.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/provider_paths.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/remote_config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/renames.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/restart_state.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/runtime_config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/scroll_agent.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/scroll_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/selection_isolation.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/self_update.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/session_cache.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/session_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/session_lease.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/setting_contracts.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/settings.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ssh_args.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ssh_compat.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ssh_display_diagnostics.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ssh_doctor.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/system_deps.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/terminal_status.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/tmux_binding_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/tmux_ctl.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/tmux_health.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/tmux_server.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/tool_panes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/transcript.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/__init__.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/_widgets.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/app.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/keymap.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/modals.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/projects_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/running_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/sessions_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/sidebar.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/statusbar.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/ui/workspace.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_attach_relay.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_bootstrap.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_install_log.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_pacman.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_tmux_lifecycle.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux/windows_ui_transition.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux.egg-info/dependency_links.txt +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux.egg-info/entry_points.txt +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux.egg-info/requires.txt +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/src/railmux.egg-info/top_level.txt +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_agent_workspace.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_atomic_file.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_background_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_cli.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_codex_delete.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_codex_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_codex_lineage.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_codex_transcript.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_config_cli.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_denested_transport_benchmark.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_diagnostics.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_discovery.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_display_transport.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_fast_full_window.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_focus_highlight.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_fullscreen_binding.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_fuzzy.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_help_agent.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_help_workspace.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_history_cleanup.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_keymap.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_launcher.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_layout_profile.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_legacy_sessions.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_local_clipboard.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_local_open.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_modals.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_mode_selection.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_modes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_mouse_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_new_project.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_options.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_orphan_recovery.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_pane_surface.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_path_codec.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_projects_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_provider_paths.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_release_notes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_release_workflow.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_remote_config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_renames.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_restart_state.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_running_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_running_sort.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_runtime_config.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_scroll_agent.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_scroll_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_selection_isolation.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_self_update.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_session_cache.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_session_index.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_session_lease.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_sessions_pane.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_setting_contracts.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_settings.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_sidebar.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_soft_quit.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_ssh_args.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_ssh_compat.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_ssh_display_benchmark.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_ssh_display_diagnostics.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_ssh_doctor.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_status.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_statusbar.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_system_deps.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_terminal_size.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_terminal_status.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_termux_local_touch.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_binding_manager.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_ctl.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_health.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_integration.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_server.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tmux_status.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_tool_panes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_transcript.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_utf8_state.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_web_demo_recorder.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_wheel_smoke.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_widgets.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_attach_relay.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_bootstrap.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_pacman.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_tmux_lifecycle.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_ui_transition.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_windows_wrapper_contract.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tests/test_yolo_prompt.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tools/denested_transport_benchmark.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tools/release_notes.py +0 -0
- {railmux-0.4.0.dev26 → railmux-0.4.0.dev27}/tools/wheel_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0.dev27"
|
|
@@ -80,6 +80,7 @@ MSYS2_BASE_LINEAGE_SHA256 = (
|
|
|
80
80
|
"6fe0cc8154132040e034ff4daface2a4163a9d1f6ebaaa1133394bff460bd5cf"
|
|
81
81
|
)
|
|
82
82
|
MSYS2_RUNTIME_ID = f"msys2-{MSYS2_RELEASE}"
|
|
83
|
+
_NATIVE_WINDOWS = os.name == "nt"
|
|
83
84
|
RUNTIME_SCHEMA = 1
|
|
84
85
|
|
|
85
86
|
_RUNTIME_OVERRIDE = "RAILMUX_MSYS2_ROOT"
|
|
@@ -621,6 +622,16 @@ def _ensure_safe_archive_directory(root: Path, parts: Sequence[str]) -> Path:
|
|
|
621
622
|
return current
|
|
622
623
|
|
|
623
624
|
|
|
625
|
+
def _apply_archive_mode(path: Path, mode: int) -> None:
|
|
626
|
+
if _NATIVE_WINDOWS:
|
|
627
|
+
# Windows chmod maps a missing owner-write bit to the NTFS read-only
|
|
628
|
+
# attribute; applying tar's POSIX 0444/0555 modes would prevent pacman
|
|
629
|
+
# from replacing those files during the first base update.
|
|
630
|
+
os.chmod(path, stat.S_IWRITE)
|
|
631
|
+
else:
|
|
632
|
+
os.chmod(path, mode)
|
|
633
|
+
|
|
634
|
+
|
|
624
635
|
def _extract_msys2_archive(
|
|
625
636
|
archive: Path,
|
|
626
637
|
destination: Path,
|
|
@@ -696,7 +707,7 @@ def _extract_msys2_archive(
|
|
|
696
707
|
)
|
|
697
708
|
output.write(chunk)
|
|
698
709
|
remaining -= len(chunk)
|
|
699
|
-
|
|
710
|
+
_apply_archive_mode(target, member.mode & 0o777)
|
|
700
711
|
except RuntimeInstallError:
|
|
701
712
|
raise
|
|
702
713
|
except OSError as exc:
|
|
@@ -715,7 +726,7 @@ def _extract_msys2_archive(
|
|
|
715
726
|
"the verified MSYS2 archive expanded to an unexpected size"
|
|
716
727
|
)
|
|
717
728
|
for directory, mode in reversed(directories):
|
|
718
|
-
|
|
729
|
+
_apply_archive_mode(directory, mode)
|
|
719
730
|
except RuntimeInstallError:
|
|
720
731
|
reporter.note(
|
|
721
732
|
"MSYS2 archive extraction failed; the temporary base was not "
|
|
@@ -1613,6 +1624,12 @@ def _run_checked(
|
|
|
1613
1624
|
if returncode:
|
|
1614
1625
|
if allow_failure:
|
|
1615
1626
|
return returncode
|
|
1627
|
+
if _NATIVE_WINDOWS and returncode & 0xFFFFFFFF == 0xC0000135:
|
|
1628
|
+
reporter.note(
|
|
1629
|
+
"Windows reported STATUS_DLL_NOT_FOUND. Check whether security "
|
|
1630
|
+
"software quarantined a file in the private MSYS2 staging tree.",
|
|
1631
|
+
level="error",
|
|
1632
|
+
)
|
|
1616
1633
|
reporter.command_failed(label, returncode)
|
|
1617
1634
|
raise RuntimeInstallError(f"{label} failed with exit code {returncode}")
|
|
1618
1635
|
reporter.command_succeeded()
|
|
@@ -2,8 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
|
+
import subprocess
|
|
5
6
|
import sys
|
|
6
7
|
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from railmux import windows_msys2
|
|
7
11
|
from railmux.windows_install_log import InstallReporter, install_log_path
|
|
8
12
|
from railmux.windows_msys2 import _run_checked
|
|
9
13
|
|
|
@@ -194,6 +198,27 @@ def test_default_command_runner_uses_no_stdin_and_captures_utf8(tmp_path):
|
|
|
194
198
|
assert "captured 中文" in path.read_text(encoding="utf-8")
|
|
195
199
|
|
|
196
200
|
|
|
201
|
+
def test_windows_missing_dll_status_is_explained(tmp_path, monkeypatch):
|
|
202
|
+
stream = io.StringIO()
|
|
203
|
+
path = tmp_path / "install.log"
|
|
204
|
+
monkeypatch.setattr(windows_msys2, "_NATIVE_WINDOWS", True)
|
|
205
|
+
|
|
206
|
+
with InstallReporter(path, verbose=False, stream=stream) as reporter:
|
|
207
|
+
with pytest.raises(windows_msys2.RuntimeInstallError, match="3221225781"):
|
|
208
|
+
_run_checked(
|
|
209
|
+
["fixture.exe"],
|
|
210
|
+
env={},
|
|
211
|
+
reporter=reporter,
|
|
212
|
+
runner=lambda *_args, **_kwargs: subprocess.CompletedProcess(
|
|
213
|
+
[], 0xC0000135
|
|
214
|
+
),
|
|
215
|
+
label="MSYS2 base update",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert "STATUS_DLL_NOT_FOUND" in stream.getvalue()
|
|
219
|
+
assert "security software" in path.read_text(encoding="utf-8")
|
|
220
|
+
|
|
221
|
+
|
|
197
222
|
def test_compact_reporter_surfaces_extraction_and_package_progress(tmp_path):
|
|
198
223
|
stream = io.StringIO()
|
|
199
224
|
path = tmp_path / "install-0.4.0.dev8-20260803T000000Z-7.log"
|
|
@@ -3,7 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import io
|
|
5
5
|
import json
|
|
6
|
+
import os
|
|
6
7
|
import re
|
|
8
|
+
import stat
|
|
7
9
|
import subprocess
|
|
8
10
|
import tarfile
|
|
9
11
|
from contextlib import contextmanager
|
|
@@ -1291,12 +1293,16 @@ def test_tar_xz_extraction_is_internal_bounded_and_progress_visible(
|
|
|
1291
1293
|
_tar_directory("msys64"),
|
|
1292
1294
|
_tar_directory("msys64/usr"),
|
|
1293
1295
|
_tar_file("msys64/usr/bash.exe", b"verified bash"),
|
|
1296
|
+
_tar_file("msys64/usr/read-only-upstream", b"must stay writable"),
|
|
1294
1297
|
]
|
|
1298
|
+
entries[-1][0].mode = 0o444
|
|
1295
1299
|
archive = tmp_path / MSYS2_ARCHIVE_NAME
|
|
1296
1300
|
_write_test_tar(archive, entries)
|
|
1297
|
-
monkeypatch.setattr(windows_msys2, "MSYS2_ARCHIVE_MEMBER_COUNT",
|
|
1301
|
+
monkeypatch.setattr(windows_msys2, "MSYS2_ARCHIVE_MEMBER_COUNT", 4)
|
|
1298
1302
|
monkeypatch.setattr(
|
|
1299
|
-
windows_msys2,
|
|
1303
|
+
windows_msys2,
|
|
1304
|
+
"MSYS2_ARCHIVE_UNPACKED_SIZE",
|
|
1305
|
+
len(b"verified bash") + len(b"must stay writable"),
|
|
1300
1306
|
)
|
|
1301
1307
|
destination = tmp_path / "stage"
|
|
1302
1308
|
destination.mkdir()
|
|
@@ -1309,12 +1315,34 @@ def test_tar_xz_extraction_is_internal_bounded_and_progress_visible(
|
|
|
1309
1315
|
assert (destination / "msys64" / "usr" / "bash.exe").read_bytes() == (
|
|
1310
1316
|
b"verified bash"
|
|
1311
1317
|
)
|
|
1318
|
+
package_owned = destination / "msys64" / "usr" / "read-only-upstream"
|
|
1319
|
+
if os.name == "nt":
|
|
1320
|
+
readonly = getattr(stat, "FILE_ATTRIBUTE_READONLY", 0x1)
|
|
1321
|
+
assert not getattr(package_owned.stat(), "st_file_attributes", 0) & readonly
|
|
1322
|
+
else:
|
|
1323
|
+
assert stat.S_IMODE(package_owned.stat().st_mode) == 0o444
|
|
1312
1324
|
rendered = output.getvalue()
|
|
1313
|
-
assert "Extracting private runtime: 1/
|
|
1314
|
-
assert "Extracting private runtime:
|
|
1325
|
+
assert "Extracting private runtime: 1/4 files (25%)" in rendered
|
|
1326
|
+
assert "Extracting private runtime: 4/4 files (100%)" in rendered
|
|
1315
1327
|
assert "MSYS2 archive extraction" in log.read_text(encoding="utf-8")
|
|
1316
1328
|
|
|
1317
1329
|
|
|
1330
|
+
def test_native_windows_extraction_clears_posix_read_only_modes(
|
|
1331
|
+
tmp_path, monkeypatch,
|
|
1332
|
+
):
|
|
1333
|
+
path = tmp_path / "package-owned"
|
|
1334
|
+
path.write_bytes(b"fixture")
|
|
1335
|
+
applied = []
|
|
1336
|
+
monkeypatch.setattr(windows_msys2, "_NATIVE_WINDOWS", True)
|
|
1337
|
+
monkeypatch.setattr(
|
|
1338
|
+
windows_msys2.os, "chmod", lambda target, mode: applied.append((target, mode))
|
|
1339
|
+
)
|
|
1340
|
+
|
|
1341
|
+
windows_msys2._apply_archive_mode(path, 0o444)
|
|
1342
|
+
|
|
1343
|
+
assert applied == [(path, stat.S_IWRITE)]
|
|
1344
|
+
|
|
1345
|
+
|
|
1318
1346
|
@pytest.mark.parametrize(
|
|
1319
1347
|
("member", "message"),
|
|
1320
1348
|
[
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Exercise the pinned MSYS2 archive with native Windows process loading."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import os
|
|
6
|
+
import stat
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from railmux.windows_install_log import InstallReporter
|
|
12
|
+
from railmux.windows_msys2 import (
|
|
13
|
+
MSYS2_ARCHIVE_SHA256,
|
|
14
|
+
Msys2Runtime,
|
|
15
|
+
_extract_msys2_archive,
|
|
16
|
+
download_from_sources,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main() -> int:
|
|
21
|
+
if os.name != "nt":
|
|
22
|
+
raise SystemExit("the Windows archive smoke test requires native Windows")
|
|
23
|
+
with tempfile.TemporaryDirectory(prefix="railmux-archive-smoke-") as raw:
|
|
24
|
+
stage = Path(raw)
|
|
25
|
+
archive = stage / "msys2-base.tar.xz"
|
|
26
|
+
download_from_sources(archive, MSYS2_ARCHIVE_SHA256)
|
|
27
|
+
extraction = stage / "extract"
|
|
28
|
+
extraction.mkdir()
|
|
29
|
+
with InstallReporter(
|
|
30
|
+
stage / "install.log", verbose=False, stream=io.StringIO()
|
|
31
|
+
) as reporter:
|
|
32
|
+
_extract_msys2_archive(archive, extraction, reporter=reporter)
|
|
33
|
+
runtime = Msys2Runtime(extraction / "msys64", managed=False)
|
|
34
|
+
bashbug = runtime.root / "usr" / "bin" / "bashbug"
|
|
35
|
+
readonly = getattr(stat, "FILE_ATTRIBUTE_READONLY", 0x1)
|
|
36
|
+
if getattr(bashbug.stat(), "st_file_attributes", 0) & readonly:
|
|
37
|
+
raise SystemExit("POSIX archive mode became an NTFS read-only attribute")
|
|
38
|
+
with bashbug.open("r+b") as stream:
|
|
39
|
+
first = stream.read(1)
|
|
40
|
+
stream.seek(0)
|
|
41
|
+
stream.write(first)
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
[
|
|
44
|
+
str(runtime.bash),
|
|
45
|
+
"--noprofile",
|
|
46
|
+
"--norc",
|
|
47
|
+
"-c",
|
|
48
|
+
"cygpath --version >/dev/null && pacman --version >/dev/null",
|
|
49
|
+
],
|
|
50
|
+
env=runtime.environment(os.environ),
|
|
51
|
+
stdin=subprocess.DEVNULL,
|
|
52
|
+
stdout=subprocess.PIPE,
|
|
53
|
+
stderr=subprocess.STDOUT,
|
|
54
|
+
timeout=30,
|
|
55
|
+
check=False,
|
|
56
|
+
)
|
|
57
|
+
if result.returncode:
|
|
58
|
+
output = result.stdout.decode("utf-8", errors="replace")[-2000:]
|
|
59
|
+
raise SystemExit(
|
|
60
|
+
f"extracted MSYS2 executables failed ({result.returncode}):\n{output}"
|
|
61
|
+
)
|
|
62
|
+
print("Native Windows archive extraction and executable loading passed.")
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
raise SystemExit(main())
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.0.dev26"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|