Kill the Standup
读取昨日的 Linear 问题和 GitHub 提交。格式化站会更新。发布到 Slack。
步骤 1: 环境检查
确认所需的环境变量已设置:
echo "LINEAR_API_KEY: ${LINEAR_API_KEY:+set}"
echo "SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL:+set}"
echo "GITHUB_REPO: ${GITHUB_REPO:-未设置}"
echo "GITHUB_USERNAME: ${GITHUB_USERNAME:-未设置}"
如果缺少 LINEAR_API_KEY: 停止。告知用户:“需要 LINEAR_API_KEY。请从 Linear → 设置 → API → 个人 API 密钥 获取,并将其添加到你的 .env 文件中。”
如果缺少 SLACK_WEBHOOK_URL: 停止。告知用户:“需要 SLACK_WEBHOOK_URL。请在 api.slack.com/apps → 你的应用 → 传入 Webhook 中创建一个传入 Webhook,并将其添加到你的 .env 文件中。”
如果缺少 GITHUB_REPO: 继续执行。将跳过 GitHub 提交部分,并向用户说明这一点。
步骤 2: 获取 Linear 活动
计算昨天的日期,格式为 ISO 8601(例如 2026-04-09T00:00:00.000Z)。
获取当前用户 ID:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: Bearer $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "query { viewer { id name email } }"}'
从响应中提取 id 字段。存储为 LINEAR_USER_ID。
获取分配给我且自昨天起有更新的问题:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: Bearer $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "query($since: DateTime!, $userId: ID!) { issues(first: 50, filter: { assignee: { id: { eq: $userId } } updatedAt: { gte: $since } }) { edges { node { identifier title state { name } url completedAt updatedAt createdAt } } } }",
"variables": {"since": "YESTERDAY_ISO", "userId": "LINEAR_USER_ID"}
}'
对问题进行分类:
completedAt不为空 → 已完成 (Done)state.name为“进行中 (In Progress)”、“已开始 (Started)”或“评审中 (In Review)” → 进行中 (Doing)state.name为“已取消 (Cancelled)” → 跳过state.name为“待办 (Todo)”或“积压 (Backlog)” → 跳过(未被处理)
如果问题为零:注明“昨日未找到 Linear 活动。”
步骤 3: 获取 GitHub 提交
如果未设置 GITHUB_REPO,则静默跳过此步骤。
如果已设置 GITHUB_REPO:
gh api "repos/$GITHUB_REPO/commits?author=${GITHUB_USERNAME:-$(gh api user --jq .login)}&since=YESTERDAY_ISO&per_page=30" \
--jq '[.[] | {message: .commit.message, url: .html_url}]'
处理提交:
- 提取每条提交消息的第一行(第一个换行符之前的内容)
- 跳过合并提交:删除任何以“Merge”开头的消息
- 根据消息文本去重
如果设置了 GITHUB_REPO 但返回零条提交:注明“昨日未找到提交。”
步骤 4: 格式化站会内容
在编写之前,完整阅读 references/standup-format.md。
按照以下确切结构生成站会内容:
**已完成 (Done)**
- [ENG-123] 已完成的工单标题
- fix: 提交消息
**进行中 (Doing)**
- [ENG-124] 进行中的工单标题
**阻塞项 (Blockers)**
无阻塞项。
来自 standup-format.md 的规则:
- 不使用第一人称代词(“我”、“我们”)
- 已完成项使用过去时,进行中项使用现在进行时
- 每个 Linear 项使用
[标识符] 标题格式 - 每个 GitHub 提交使用提交消息的第一行
- 如果没有已完成项:在“已完成”下写“无完成项。”
- 如果没有进行中项:在“进行中”下写“无进行中项。”
- 如果没有阻塞项:写“无阻塞项。”
- 总字数不超过 200 字
步骤 5: 发布到 Slack 或仅输出
将格式化后的站会内容呈现给用户。
询问用户:“发布到 Slack,还是仅输出?”
确认发布时:
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*站会 — 日期_HERE*\n\n*已完成*\n项_目\n\n*进行中*\n项_目\n\n*阻塞项*\n项_目"
}
}
]
}'
为避免提交消息或工单标题中的特殊字符导致 Shell 引号问题,请通过临时文件或 heredoc 传递请求体:
cat > /tmp/standup-payload.json << 'ENDJSON'
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "STANDUP_CONTENT_HERE"
}
}
]
}
ENDJSON
curl -s -X POST "$SLACK_WEBHOOK_URL" -H "Content-Type: application/json" -d @/tmp/standup-payload.json
发布后:“站会已发布。”
选择“仅输出”时: 将站会内容以代码块形式打印出来,方便复制粘贴。