- 11 个超 20KB 的 SKILL.md 拆分,大章节移入 references/details.md - 7 处非标准字段(arguments/user_invocable)移入 metadata - 修 req-dev 4 条相对链接;validate 跳过行内代码避免  误报 - validate-skills.py: 23 warns -> 0 warns,0 errors
183 lines
4.9 KiB
Markdown
183 lines
4.9 KiB
Markdown
# skills 详细内容
|
||
|
||
## 回滚策略
|
||
|
||
### iOS TestFlight 回滚
|
||
|
||
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
|
||
|
||
| 手段 | 说明 | API |
|
||
|------|------|-----|
|
||
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
|
||
| 过期 build | 强制过期有问题的 build | 同上 |
|
||
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
|
||
|
||
```bash
|
||
# 通过 ASC API 停止分发某个 build
|
||
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
|
||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
|
||
```
|
||
|
||
### Docker 回滚
|
||
|
||
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
|
||
|
||
```bash
|
||
# 1. 确定上一个正常的 tag
|
||
PREVIOUS_TAG=<previous-good-tag>
|
||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||
|
||
# 2. 在服务器上回滚
|
||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
|
||
export IMAGE_TAG=$PREVIOUS_TAG
|
||
docker compose pull
|
||
docker compose up -d
|
||
"
|
||
|
||
# 3. 验证回滚成功
|
||
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
|
||
```
|
||
|
||
### 数据库回滚注意事项
|
||
|
||
| 场景 | 策略 |
|
||
|------|------|
|
||
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
|
||
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
|
||
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
|
||
|
||
```bash
|
||
# 数据库 migration 回滚示例(如果使用 golang-migrate)
|
||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
|
||
"
|
||
```
|
||
|
||
---
|
||
|
||
## 部署监控
|
||
|
||
### Post-deploy 健康检查模式
|
||
|
||
```bash
|
||
# 通用部署后验证函数
|
||
post_deploy_verify() {
|
||
local url=$1
|
||
local max_retries=${2:-5}
|
||
local interval=${3:-10}
|
||
|
||
echo "Verifying deployment at $url ..."
|
||
for i in $(seq 1 $max_retries); do
|
||
local status
|
||
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||
if [ "$status" = "200" ]; then
|
||
echo "Health check passed (attempt $i/$max_retries)"
|
||
return 0
|
||
fi
|
||
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
|
||
sleep $interval
|
||
done
|
||
echo "Health check FAILED after $max_retries attempts"
|
||
return 1
|
||
}
|
||
|
||
# 使用示例
|
||
post_deploy_verify "http://39.104.87.246/health" 5 10
|
||
```
|
||
|
||
### 飞书通知模板
|
||
|
||
部署完成后通过飞书 Webhook 发送通知:
|
||
|
||
```bash
|
||
# 部署成功通知
|
||
send_feishu_deploy_notification() {
|
||
local env=$1 # staging / production
|
||
local version=$2 # 版本号或 tag
|
||
local status=$3 # success / failure
|
||
local detail=$4 # 额外说明
|
||
|
||
local WEBHOOK_URL="<飞书群 Webhook 地址>"
|
||
|
||
if [ "$status" = "success" ]; then
|
||
local color="green"
|
||
local emoji="✅"
|
||
else
|
||
local color="red"
|
||
local emoji="❌"
|
||
fi
|
||
|
||
curl -s -X POST "$WEBHOOK_URL" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"msg_type": "interactive",
|
||
"card": {
|
||
"header": {
|
||
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
|
||
"template": "'"$color"'"
|
||
},
|
||
"elements": [
|
||
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
|
||
]
|
||
}
|
||
}'
|
||
}
|
||
|
||
# 使用示例
|
||
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
|
||
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
|
||
```
|
||
|
||
### iOS TestFlight 构建状态监控
|
||
|
||
通过 ASC API 持续监控 build 处理状态:
|
||
|
||
```bash
|
||
# 监控 TestFlight build 处理状态
|
||
monitor_testflight_build() {
|
||
local build_id=$1
|
||
local jwt_token=$2
|
||
local max_wait=600 # 最长等待 10 分钟
|
||
local elapsed=0
|
||
|
||
while [ $elapsed -lt $max_wait ]; do
|
||
local response
|
||
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
|
||
-H "Authorization: Bearer $jwt_token")
|
||
|
||
local state
|
||
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
|
||
|
||
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
|
||
|
||
case "$state" in
|
||
VALID)
|
||
echo "Build 处理完成,可用于测试"
|
||
return 0
|
||
;;
|
||
FAILED|INVALID)
|
||
echo "Build 处理失败: $state"
|
||
return 1
|
||
;;
|
||
PROCESSING)
|
||
sleep 30
|
||
((elapsed+=30))
|
||
;;
|
||
*)
|
||
sleep 15
|
||
((elapsed+=15))
|
||
;;
|
||
esac
|
||
done
|
||
|
||
echo "Build 处理超时(${max_wait}s)"
|
||
return 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|