任何程序都可以作为 GitHub Actions 的 Shell
文章指出,在 GitHub Actions 中,`shell` 关键字不仅限于常见的 shell,还可以设置为 `$PATH` 上的任何可执行文件。GitHub 会将 `run` 代码块写入临时文件,并将其作为输入传递给指定的 shell。这使得可以使用 C 语言等其他程序作为步骤 runner。文章还提到了动态修改 `$PATH` 的可能性。从安全角度来看,这种灵活性影响不大,但作者对 GitHub 允许 `$PATH` 查找的行为感到惊讶,认为其 shell 值应该被固定。
任何程序都可以作为 GitHub Actions 的 Shell
2025-04-07
在 GitHub Actions 中,你可以使用 shell
关键字来指定运行 run:
代码块的 shell。 这个关键字对于工作流是可选的,但对于 action 定义是强制的。
通常,shell 默认为你的 runner 提供一些合理的默认值,例如 Linux 和 macOS 上的 bash
以及 Windows 上的 pwsh
。 但它也可以被显式指定,并且 GitHub 文档说明,显式指定它也意味着会使用一些 GitHub 选择的标志:
# 显式设置 bash 意味着 bash --noprofile --norc -eo pipefail
- shell: bash
run: |
echo "Hello, world!"
基于此,你可能会认为 shell
值有一个固定的有效数量1,GitHub 跟踪并添加其特殊标志。 但你错了!
事实证明,你可以将 shell
设置为 $PATH
上的任何可执行文件,GitHub 会很乐意使用它来执行 run
代码块。 如果该命令尚未将单个文件作为输入,则需要将特殊的 {0}
参数传递给它,GitHub 会将该参数替换为临时文件,该文件将模板扩展的 run
代码块生成到该临时文件中。
多亏了这一点,我们可以做各种疯狂的事情。
使用 C 作为我们的步骤 runner 效果很好:
- run: sudo apt install -y tcc
- shell: tcc -run {0}
run: |
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
...动态修改 $PATH
通过 $GITHUB_PATH
也是可行的: dynamically modifying:
- run: |
touch ./bash
chmod +x ./bash
echo '#!/bin/sh' > ./bash
echo 'echo hello from fake bash' >> ./bash
echo "${PWD}" >> "${GITHUB_PATH}"
- run: |
echo "this doesn't do what you expect"
shell: bash
从安全角度来看,这重要吗? 很难说 - 在 GitHub Actions 中,文件写入意味着执行有很多其他方式,包括 GITHUB_ENV
本身。
另一方面,令人惊讶的是 (IMO) GitHub 甚至对其“众所周知”的 shell 值执行 $PATH
查找; 我希望这些值被固定为特定值(例如,bash
为 /bin/bash
),特别是由于它们基于这些众所周知的值将标志注入到 run
的命令行中。
- 或者,更一般地说,GitHub 将接受其 toolcache 中的任何预注册工具。 例如,这就是我之前认为
shell: python
的工作方式,直到我注意到这一点。 ↩