在开发 GitHub Actions 时,有时候会遇到这样的问题:如果这个 Action 接受来自用户的 GitHub token,那该如何以这个 token 背后的身份完成所需的 git 操作?
我在上一篇文章里介绍了我做的 config-git-with-token-action
,专门用来解决这个问题的,但这个 Action 在配置 git 的时候又是怎么获取对应的用户名和邮箱的呢?这是通过另外一个叫做 token-who-am-i-action
来解决的。
这个 Action 可以接受用户生成的 PAT (Personal Access Token),也可以接受 GitHub App 的 token。(默认的 GitHub Actions token 当然是接受的。)它会使用 Action 调用 GitHub API 查询关于 token 自己身份相关的信息,因为类似于 Linux 的 whoami
命令所以我把它叫做 token-who-am-i-action
。
这个 Action 能返回的信息当中,首先要看 type
是 User
还是 Bot
。
- 如果是
User
的话,它所返回的其他信息包括name
和email
。这两项都可能是undefined
,因为用户可以选择隐藏自己的名称和邮箱。 - 如果是
Bot
的话,它必须返回name
、email
和appSlug
,全部都是字符串,不可能是undefined
。每一个 GitHub App 必须有一个用于显示的名字,然后由此生成对外公开的地址(格式为https://github.com/apps/${appSlug}
)。GitHub App 其实并不存在邮箱,但使用特定格式(${id}+${login}@users.noreply.github.com
)生成的邮箱会被正确识别并显示正确的头像,例如说 GitHub Actions 默认 token 的身份使用的邮箱是41898282+github-actions[bot]@users.noreply.github.com
。
除此之外,无论是哪种类型的身份,这个 Action 还能返回 login
、id
和 globalId
。login
对于 User
来说,就是他的个人页面地址(https://github.com/${login}
)中的路径,有些文档也把这个叫做 username
;对于 Bot
来说,这可以由 appSlug
通过特定格式(${appSlug}[bot]
)生成,例如 GitHub Actions 的就是 github-actions[bot]
。
至于 id
和 globalId
分别用于 REST API 和 GraphQL。这是 GitHub 数据存储很有意思的一个地方。对于每一种 REST API 的类型(如 user
),它背后都是一张独立的关系型数据表,都有一个这种类型内部唯一的 id
,但这个 id
可以跟其他类型冲突。在 GraphQL 里面,因为 id
可以用来查询任何类型,所以引入了 globalId
的概念,保证即使跨类型依然唯一。GraphQL 的某些 query/mutation 的 id
默认就是 globalId
;但某些必须加上 X-GitHub-Next-Global-ID: 1
的 header 进行请求才是 globalId
,否则就是跨类型不唯一的 id
。
那我们如何在编写自己的 Action 时调用 token-who-am-i-action
呢?如果你在编写的是 composite action,可以这样写:
runs:
using: 'composite'
steps:
- uses: CatChen/token-who-am-i-action@v1
id: token-who-am-i
with:
github-token: ${{ inputs.github-token }}
- shell: bash
env:
LOGIN: ${{ steps.token-who-am-i.outputs.login }}
GLOBAL_ID: ${{ steps.token-who-am-i.outputs.global-id }}
ID: ${{ steps.token-who-am-i.outputs.id }}
NAME: ${{ steps.token-who-am-i.outputs.name }}
EMAIL: ${{ steps.token-who-am-i.outputs.email }}
TYPE: ${{ steps.token-who-am-i.outputs.type }}
APP_SLUG: ${{ steps.token-who-am-i.outputs.app-slug }}
run: |
echo "Login is $LOGIN"
echo "Global id is $GLOBAL_ID"
echo "Id is $ID"
echo "Name is $NAME"
echo "Email is $EMAIL"
echo "Type is $TYPE"
echo "App slug is $APP_SLUG"
如果你编写的是 JavaScript action 可以先从 NPM 安装同名的 token-who-am-i-action
包,然后再进行调用:
import { tokenWhoAmI } from 'token-who-am-i-action';
const me = await tokenWhoAmI(githubToken);
const {
login,
globalId,
type,
} = me;
if (me.type === 'User') {
const {
id,
name,
email,
} = me;
} else if (me.type === 'Bot') {
const {
appSlug,
id,
name,
email,
} = me;
}
希望这个 Action 对各位 GitHub Actions 开发者有用。非 Actions 开发者也可以直接在 Workflow 里面使用这个 Action,如果你的 Workflow 使用非默认的 GitHub Actions(机器人)身份进行 git 操作的话。大家在使用过程中遇到什么问题,或者是希望增加什么新功能,欢迎到项目的 GitHub 开 issue。