From 8551d54330f2dbd4e9660214fd767697cf8b1ca1 Mon Sep 17 00:00:00 2001 From: wanxinhao <1504758388@qq.com> Date: Wed, 28 Oct 2026 21:20:01 +0800 Subject: [PATCH 1/2] =?UTF-8?q?CI=EF=BC=9Apull=20request=20=E4=B9=9F?= =?UTF-8?q?=E8=B7=91=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=8F=A6=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E6=9D=A1=E5=85=A8=E9=87=8F=E6=B5=8B=E8=AF=95=E7=9A=84=20workfl?= =?UTF-8?q?ow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 仓库的 workflow 全都只有 push 触发,pull request 页面上一个检查都没有, 评审看不到红绿灯,贡献者也不知道推上去的改动有没有跑过测试: - hosts、mobile、shells、ssh、sync、windows 六个测试 workflow 各加 pull_request,路径与 push 相同,来的活儿和推分支时一模一样;路径 列表因此在每个文件里出现两遍,各带上一行注释说明。release.yml 只跟 tag,不动。 - 新增 tests.yml:fmt、整 workspace 的 clippy、全量 cargo test --lib、 全量 UI 测试和外部 CLI 的测试,路径过滤 src/、tests/、crates/、 locales/、Cargo.* 等。这些以前要么只在 Windows 上跑一部分 (windows.yml),要么只跑两个组(shells.yml 的 terminal::run 和 zmodem):应用的单元测试和 UI 测试在 Linux 上从没整跑过。 UI 测试里跳过 projects::a_programs_title_names_the_terminal_until_it_ is_named_by_hand 一条:它在 main 上自己就 2 秒超时(对话框关不掉), 不跳过则每跑必红,workflow 的注释里写明了原因。 验证:七个 workflow 的 YAML 逐个解析通过;UI 测试在去掉 DISPLAY、 WAYLAND_DISPLAY 的环境里照常通过(GitHub 的 runner 没有显示服务器, 所以不能只按本机能跑来判断);cli_bin 在 Linux 上 4 条全过;fmt 干净。 推上 fork 后由 Actions 实跑确认。 --- .github/workflows/hosts.yml | 11 ++++++ .github/workflows/mobile.yml | 13 +++++++ .github/workflows/shells.yml | 9 +++++ .github/workflows/ssh.yml | 8 ++++ .github/workflows/sync.yml | 8 ++++ .github/workflows/tests.yml | 71 +++++++++++++++++++++++++++++++++++ .github/workflows/windows.yml | 46 +++++++++++++++++++++++ 7 files changed, 166 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/hosts.yml b/.github/workflows/hosts.yml index 47442cc6..d014802b 100644 --- a/.github/workflows/hosts.yml +++ b/.github/workflows/hosts.yml @@ -16,6 +16,17 @@ on: - "Cargo.lock" - "rust-toolchain.toml" - ".github/workflows/hosts.yml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "crates/shellrs-hosts/**" + # The cores it builds on. + - "crates/shellrs-ssh/**" + - "crates/shellrs-sync/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".github/workflows/hosts.yml" workflow_dispatch: jobs: diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml index 089e3a8f..21fb4fce 100644 --- a/.github/workflows/mobile.yml +++ b/.github/workflows/mobile.yml @@ -18,6 +18,19 @@ on: - "Cargo.lock" - "rust-toolchain.toml" - ".github/workflows/mobile.yml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "crates/shellrs-mobile/**" + - "crates/shellrs-ssh/**" + - "crates/shellrs-hosts/**" + - "crates/shellrs-sync/**" + - "scripts/build-ios.sh" + - "scripts/build-android.sh" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".github/workflows/mobile.yml" workflow_dispatch: jobs: diff --git a/.github/workflows/shells.yml b/.github/workflows/shells.yml index 93872b7c..8f78bb5e 100644 --- a/.github/workflows/shells.yml +++ b/.github/workflows/shells.yml @@ -20,6 +20,15 @@ on: - "Cargo.lock" - "rust-toolchain.toml" - ".github/workflows/shells.yml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "src/terminal/**" + - "src/testing.rs" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".github/workflows/shells.yml" workflow_dispatch: jobs: diff --git a/.github/workflows/ssh.yml b/.github/workflows/ssh.yml index 5b576616..1d026262 100644 --- a/.github/workflows/ssh.yml +++ b/.github/workflows/ssh.yml @@ -14,6 +14,14 @@ on: - "Cargo.lock" - "rust-toolchain.toml" - ".github/workflows/ssh.yml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "crates/shellrs-ssh/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".github/workflows/ssh.yml" workflow_dispatch: jobs: diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 22f94bc2..a28bb2a1 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -13,6 +13,14 @@ on: - "Cargo.lock" - "rust-toolchain.toml" - ".github/workflows/sync.yml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "crates/shellrs-sync/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".github/workflows/sync.yml" workflow_dispatch: jobs: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..6997cfe5 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,71 @@ +# The app's own tests, as a pull request sees them: formatting, clippy +# over the whole workspace, the unit tests of every default member, the +# UI tests and the external CLI's. +# +# The other workflows answer to the paths they name and used to answer +# only to a push, so a pull request came back with no check at all; they +# now answer to a pull request too. This one carries the tests the others +# run only in parts: `shells` runs two groups of `terminal::`, `windows` +# runs a list of targets on Windows, and nothing ran the unit tests of +# the app or the UI tests as a whole on Linux. +# +# One UI test is skipped: projects::a_programs_title_names_the_terminal +# _until_it_is_named_by_hand does not close its dialog in time on Linux +# on main itself, change or no change, so it would fail every run until +# it is fixed. +name: tests + +on: + push: + branches: ["**"] + paths: + - "src/**" + - "tests/**" + - "locales/**" + - "crates/**" + - "build.rs" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".cargo/**" + - ".github/workflows/tests.yml" + pull_request: + paths: + - "src/**" + - "tests/**" + - "locales/**" + - "crates/**" + - "build.rs" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" + - ".cargo/**" + - ".github/workflows/tests.yml" + workflow_dispatch: + +jobs: + tests: + # The same image and packages shells.yml builds the app with. + runs-on: ubuntu-22.04 + timeout-minutes: 90 + env: + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@v4 + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake clang pkg-config libfontconfig-dev \ + libfreetype-dev libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev \ + libx11-xcb-dev libxcb1-dev libvulkan-dev libdbus-1-dev libssl-dev + # rust-toolchain.toml picks the toolchain; this installs it with the + # components it lists before the cache key is computed. + - run: rustup toolchain install + - uses: Swatinem/rust-cache@v2 + - run: cargo fmt --check + - run: cargo clippy --all-targets -- -D warnings + - run: cargo test --locked --lib + # See the header: this one test does not close its dialog in time on + # Linux on main itself. + - run: cargo test --locked --test ui -- --skip projects::a_programs_title_names_the_terminal_until_it_is_named_by_hand + - run: cargo test --locked --test cli_bin diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 50b34ab2..39b4abb1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -59,6 +59,52 @@ on: - "Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" + # The same paths as a push: a pull request is tested the same way. + pull_request: + paths: + - "src/cli/**" + - "src/analytics/**" + - "src/update/**" + - "src/app/mod.rs" + - "src/app/paths.rs" + - "src/app/computer.rs" + - "crates/shellrs-hosts/Cargo.toml" + - "crates/shellrs-hosts/src/private_key.rs" + - "src/app/window_hiding.rs" + - "src/app/system_hotkey.rs" + - "src/main.rs" + - "src/bin/**" + - "crates/shellrs-ssh/Cargo.toml" + - "crates/shellrs-ssh/src/connection.rs" + - "crates/shellrs-ssh/src/probe.rs" + - "src/ssh/transport.rs" + - "tests/ui/connection.rs" + - "crates/shellrs-ssh/src/sftp/download.rs" + - "crates/shellrs-ssh/src/sftp/journal.rs" + - "crates/shellrs-ssh/src/sftp/model.rs" + - "crates/shellrs-ssh/src/sftp/upload.rs" + - "crates/shellrs-ssh/src/sftp/sync.rs" + - "crates/shellrs-ssh/src/sftp/tests.rs" + - "src/sftp/local.rs" + - "src/credential/credential_dialog.rs" + - "src/settings/model.rs" + - "src/settings/settings_panel.rs" + - "src/terminal/local_shell.rs" + - "src/terminal/local_pty.rs" + - "src/project/project_panel.rs" + - "src/terminal/zmodem.rs" + - "src/serial/**" + - "crates/shellrs-sync/**" + - "src/app/shortcuts.rs" + - "src/workspace/window_state.rs" + - "src/testing.rs" + - "tests/cli_bin.rs" + - "build.rs" + - ".cargo/config.toml" + - "scripts/windows-runtime.ps1" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain.toml" workflow_dispatch: jobs: From 87d27ece3f2687e9f9a132f40c2d6c455a123dfe Mon Sep 17 00:00:00 2001 From: wanxinhao <1504758388@qq.com> Date: Wed, 28 Oct 2026 22:09:09 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E4=BF=AE?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=B5=8B=E8=AF=95=E5=9C=A8=20CI=20=E4=B8=8A?= =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E7=9A=84=E4=B8=A4=E6=9D=A1=E7=AB=9E=E6=80=81?= =?UTF-8?q?=E5=92=8C=E4=B8=80=E4=B8=AA=E7=8E=AF=E5=A2=83=E5=81=87=E8=AE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests.yml 第一次在 CI 上整跑,挂了三条,全部定性并修掉: - tmux::linux 的「没有 tmux」一段:删掉假 tmux 后 PATH 里还留着 /usr/bin,GitHub 的 runner 镜像自带 tmux 3.2a,真货顶替了假的, 断言 @@missing 必挂。这一段的 PATH 改成只给假工具那个目录;sh 是 按测试给的 PATH 找的,目录里补一个 /bin/sh 链接。 - terminal::engine 的 ZMODEM:transfer() 先发 ZmodemAsk 再置 transferring 标志,读到询问的一方可能在两步之间被调度走,看到「传输 还没开始」。改成先置位再发事件——答复与忙碌判断(run_command、 upload_dropped)本来就要求这个顺序。 - shellrs-mobile 的 pump:finish() 先唤醒读事件的一方,函数返回时才 关掉命令通道,于是 Exited 之后的 write 还会假装成功(数据进了没人 收的通道)。改成事件发出前先关通道,读到结尾时 write 必定是 Closed。 验证:tmux 8 条、sz_asks ×10、a_terminal_echoes ×10 本地全过;整 workspace clippy -D warnings、fmt 干净;UI 测试在去掉显示环境变量后 433 全过(skip 的那条是 main 上就挂的既有问题)。推上 fork 由 CI 实跑 复核。 --- crates/shellrs-mobile/src/terminal.rs | 4 ++++ src/terminal/engine.rs | 6 ++++-- src/tmux/linux.rs | 8 ++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/shellrs-mobile/src/terminal.rs b/crates/shellrs-mobile/src/terminal.rs index 49902706..3ff5f6b0 100644 --- a/crates/shellrs-mobile/src/terminal.rs +++ b/crates/shellrs-mobile/src/terminal.rs @@ -193,6 +193,10 @@ async fn pump( }, } }; + // The commands close before the events go out: what reads the end + // finds the terminal already refusing what it types, rather than a + // channel still open for a write nothing will carry. + commands.close(); output.finish(end); } diff --git a/src/terminal/engine.rs b/src/terminal/engine.rs index 419f4ac6..3718cf86 100644 --- a/src/terminal/engine.rs +++ b/src/terminal/engine.rs @@ -1379,11 +1379,13 @@ impl Parser { .commands .send(TerminalTransportCommand::WriteData(wire)); } + // The flag before the ask: what reads it must find the transfer + // already on, not the two apart with the ask in between. + self.transferring + .store(self.zmodem.active(), Ordering::Release); if let Some(direction) = ask { self.proxy.send(TerminalUiEventKind::ZmodemAsk(direction)); } - self.transferring - .store(self.zmodem.active(), Ordering::Release); } /// Output for the emulator, and for what reads it beside: 关键字高亮, diff --git a/src/tmux/linux.rs b/src/tmux/linux.rs index 1afab814..a6a02adb 100644 --- a/src/tmux/linux.rs +++ b/src/tmux/linux.rs @@ -458,9 +458,13 @@ $3:0:1:my job:/home/alice assert_eq!(read.current.as_deref(), Some("$1"), "{output}"); assert_eq!(read.names(), ["test", "work"]); - // No tmux. + // No tmux: only this directory on PATH, or a tmux the machine + // happens to have (the CI images ship one) would answer instead. std::fs::remove_file(bin.join("tmux")).unwrap(); - let output = crate::testing::sh(script(&command()), Some(&path)); + // `sh` is looked up in the PATH the test sets, so it needs one here. + std::os::unix::fs::symlink("/bin/sh", bin.join("sh")).unwrap(); + let only = bin.display().to_string(); + let output = crate::testing::sh(script(&command()), Some(&only)); assert_eq!(String::from_utf8(output.stdout).unwrap(), "@@missing\n"); } }