之前的文章中介绍过,iOS 常用的 Gitlab-CI 执行器是 shell,因为打包时只能使用这个。

今天将包管理工具迁到 mise 时遇到了一些和 shell 有关的问题,涉及到一些之前的知识盲区,遂记录一下。

本文的内容和 mise 无关,只是工具迁移时引出了本文涉及到的问题和知识盲区。对工具感兴趣的朋友可以点击上面的链接查看相关内容。

Shell 会话模式

先介绍一下 Shell 的几种会话模式。

我们都知道 macOS 的 默认终端环境是 zsh,zsh 有多个配套的文件,包含 ~/.zshrc.zprofile 等。另一个比较常见的是 bash,可使用的配置文件有 ~/.bash_profile~/.bashrc 等。

之前我只模糊的知道有这些文件,可以在其中写入一些内容,然后启动终端后就会自动加载这些文件,却从来没有关注过 什么时候会加载哪个文件。这就涉及到 “Shell 会话模式” 这个概念。

“Shell 会话模式”是指用户启动会话时 shell(例如 bash、zsh 或其他命令行解释器)可以运行的不同方式。每种模式都规定了 shell 的行为方式、加载的初始化文件以及处理命令的方式。

此会话模式主要围绕两个概念:“是否是交互式” 以及 “是否登录”。这两种概念两两组合,诞生四种会话模式。

  • 是否是交互式:指定用户是否可以通过键入命令直接与 shell 交互。shell 会等待用户输入并根据输入的命令提供输出。
  • 是否登录:指开启会话时用户是否登录。

举几个例子:

  • 交互式:打开 macOS 中的终端,此时我们可以在里面输入命令,与 shell 交互。
  • 非交互式:Gitlab-CI 执行 script 时,就是非交互式。我们知道执行过程中我们没有办法做任何事,包括输入设备密码,say yes 等。
  • 登录:通过 ssh 连接服务器,此时需要我们输入用户名密码。又或者执行命令时携带了 | /bin/bash --login 参数。
  • 非登录:打开 macOS 的终端,默认是非登录的。又或者从一个已经登录了的终端中,使用 bash 或其他命令开启了新的终端窗口,这些也是非登录式的。

那么在不同的登录模式下,shell 会加载的配置文件其实是不同的:

会话类型 .bash_profile .bashrc .zprofile .zshrc
交互式登录
交互式非登录
非交互式登录
非交互式非登录

如果使用 bash,那么还有一个可能会使用的配置文件:~/.profile。它的作用和 ~/.bash_profile 类似,仅当 .bash_profile 不存在时,会尝试加载 .profile。另外,有的时候 .bash_profile 中会有命令,同时加载 .profile
而 zsh 默认是不会尝试加载 .profile 的。

Gitlab-CI 的 script

Gitlab-CI 的文档有列举出它所支持的 shell 种类,这里我们看默认的 bash

贴一下文档中关于如何执行 script 的示例

# This command is used if the build should be executed in context of another user (the shell executor)
cat generated-bash-script | su --shell /bin/bash --login user

# This command is used if the build should be executed using the current user, but in a login environment
cat generated-bash-script | /bin/bash --login

# This command is used if the build should be executed in a Docker environment
cat generated-bash-script | /bin/bash

从文档中可以看到,不论哪种方式,Gitlab-CI 都是使用 /bin/bash 环境执行的 script 中定义的脚本,并且在非 Docker 环境下,会携带 --login 参数,也就是说此时使用的是 “非交互式登录 Shell”,回看上面的表格,.bash_profile 文件会被加载。

此时就需要注意了,因为 macOS 默认的终端是 zsh,所以我们在编写脚本的时候,很可能会习惯性地将配置写入到 .zprofile 或者 .zshrc 文件中,但是回到 Gitlab-CI 中就会发现不生效,原因就是脚本的执行环境不同。