跳转至

Windows setup-runner shell

在原生 Windows 上,Orca 会将 orca.yaml setup 脚本(以及 issue 命令)写入一个生成的 runner 文件,并在终端中输入一条启动命令。runner 默认是一个 .cmd 批处理文件, 自 setup 钩子发布以来一直如此。

脚本通过以 #! 解释器行开头来选择 bash:

scripts:
  setup: |
    #!/usr/bin/env bash
    [ -f .env ] || cp .env.example .env
    pnpm install

如果没有该行,脚本仍会在 cmd.exe 下运行:

scripts:
  setup: |
    copy .env.example .env
    xcopy /E assets dist

为什么由脚本声明,而不是终端偏好

terminalWindowsShell 指定_交互式终端_打开时使用哪个 shell。它并不说明项目的 setup 脚本是用 什么语言编写的。从它推导 runner 曾导致两个后果:

  • 拥有批处理语法 setup 脚本的 Windows 用户在升级后会被静默切换到 bash,因此 copy、 xcopy、set VAR=value 和 if errorlevel 1 不再可用。
  • 同一仓库中的两个人会为同一个 orca.yaml 得到不同的解释器,因此没有任何项目能编写一个 对所有 Windows 贡献者都有效的 setup 脚本。

#! 行是按项目设置的、显式的,并且对所有检出该仓库的人都相同。

同一规则也适用于 设置 → 仓库钩子 中的按用户 setup 命令 (repo.hookSettings.scripts.setup):它会被合并到到达 runner 的同一个脚本中,因此存储在那里的 POSIX 单行命令需要自己的 #! 行才能在 Windows 上以 bash 运行。

#! 行选择什么以及不选择什么

生成的 runner 在所有平台上始终由 bash 执行(bash <runner>;在原生 Windows 上为 Git Bash)。 因此,#! 行做了两件事:

  • 它声明脚本是为 POSIX shell 编写的,这正是选择 bash runner 的依据。
  • 它的选项标志会通过 set 重放,因此 #!/usr/bin/env -S bash -euo pipefail 确实会获得 pipefail。如果没有这种重放,这些标志会被静默丢弃,因为 bash <runner> 从不解析解释器行。 只有 set 本身接受的标志([--abefhkmnptuvxBCHP] [-o option])会被重放;仅用于启动的标志 (例如 -l)会被丢弃,因为 set -l 会以退出码 2 退出,并会在 runner 的第一行之前中止 runner。

解释器名称本身除了“这是否是 POSIX shell”之外不会被遵循:#!/bin/sh 和 #!/bin/zsh 脚本会在 bash 下运行,正如它们在 macOS 和 Linux 上已经那样做。

bash runner 的要求

#! 行只有在 Orca 确实能够从已配置的终端启动 bash 时才生效——终端 shell 必须解析为 Git Bash (resolveWindowsGitBashShellPath)。生成的 runner 使用 MSYS /c/... 路径,Cygwin 和 WSL shim 不接受这些路径,并且启动命令会被输入到终端打开的任意 shell 中。

当 bash 不可用时(PowerShell/cmd 终端,或 SSH 到 Windows 的主机,后者始终使用远程的 .cmd runner),#! 脚本不会在 cmd 下执行。生成的 .cmd runner 会打印原因并以退出码 1 退出, 因为运行 bash 脚本中与解释器无关的前缀(pnpm install、git submodule update),却只在第一条 仅 bash 可用的行上失败,会留下一个看似已完成但只设置了一半的 worktree。

从 Git Bash 终端启动 .cmd runner

runner 格式与输入启动命令的 shell 相互独立:拥有批处理语法 setup 脚本的 Git Bash 终端会得到一个 从 bash 窗格启动的 .cmd runner。cmd.exe /c "C:\..." 不能在那里使用 — MSYS 会将裸 /c 开关重写为驱动器路径,因此 cmd 会交互式打开, runner 永远不会执行(issue #6896)。这些启动会复用 PowerShell 的 ProcessStartInfo 启动器 (buildWindowsCmdRunnerDelayedLaunchCommand),它将开关和 runner 路径放在命令行之外。 因此,WorktreeSetupLaunch.shell 描述的是启动窗格;runner 文件的 .cmd/.sh 扩展名描述的是格式。

wait-for-setup 门控遵循同样的拆分。窗格输入门控,并且已经为自身引用了 agent 启动命令,因此从 Git Bash 窗格启动的 .cmd runner 仍然获得 bash 门控 — PowerShell 的 Invoke-Expression 无法解析 POSIX '\'' 转义。该门控包装了同一个 ProcessStartInfo 启动器,因此批处理 runner 永远不会交给 bash。

WSL worktree 和非 Windows 平台不受影响:它们始终使用 bash runner。SSH 主机根据远程路径格式选择 runner,而不是根据本地 Windows 偏好。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca