一键导入
aether-dev
开发环境部署与测试工具。支持手动部署分支到 dev、运行临时测试 Job、 绕过 CI 直接部署、查看 dev 环境日志等开发阶段操作。 使用场景:"部署这个分支到 dev"、"跑个测试 job"、"手动部署到开发环境"、"查看 dev 日志"
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
开发环境部署与测试工具。支持手动部署分支到 dev、运行临时测试 Job、 绕过 CI 直接部署、查看 dev 环境日志等开发阶段操作。 使用场景:"部署这个分支到 dev"、"跑个测试 job"、"手动部署到开发环境"、"查看 dev 日志"
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
新项目接入 Aether 集群。先分析项目特征生成部署方案,确认后生成部署文件。 两阶段流程:Phase 1 分析与设计 → Phase 2 生成文件。 使用场景:"接入新项目到 Aether"、"生成部署配置"、"初始化 CI/CD"、"规划部署方案"
Aether 集成规范速查 —— 四域 investigation-first hub。消费方项目里临时手写 dev env / 测试配置 / 连接串时的规范速查点 (#185→#186→#241 同根因三次复发的知识层加固)。 回答: "内网服务连接该用 consul 名还是 IP"、"数据库怎么连"、"DATABASE_URL 怎么写"、 "REDIS_URL 怎么写"、"连接串该用 IP 还是 consul"、"连 postgres/redis/内网服务的地址"、 "host volume 该注册几个节点/规范"、"stateful 服务模板规范"、"有状态服务该怎么配"、 "DB 密码该存哪"、"应用 secret 该存哪"、"连接串密码能不能硬编码进 HCL"。 使用场景: "Aether 集成规范速查"、"consul 名还是 IP"、"内网服务怎么连"、"数据库怎么连"、 "DATABASE_URL 怎么写"、"REDIS_URL 怎么写"、"连接串用 IP 还是 consul"、"连 postgres/redis 地址"、"host volume 该注册几个节点"、"volume 规范"、"stateful 服务模板规范"、"有状态服务 该怎么配"、"DB 密码该存哪"、"应用 secret 该存哪"、"连接串密码能不能硬编码进 HCL"
Aether 环境下 Forgejo 凭据的决策 / 诊断 / 轮换指南。回答"该用哪个 token"、 拆解人机两账号模型 (simonfish 人 / 10cg-ci-bot 机)、辨别 CF Access 与 forgejo PAT 两个凭据平面、诊断误导性 403 ("Only signed in user")、安全轮换 (先枚举全部 store)、 凭据卫生红线。深度内容指向 docs/guides/forgejo-token-map.md + .aether/pat-inventory.yaml。 使用场景: "该用哪个 forgejo token"、"which forgejo token"、"forgejo 401"、 "forgejo 403"、"Only signed in user"、"docker login forgejo"、"CF Access 403"、 "token rotation"、"凭据轮换"、"FORGEJO_TOKEN"、"forgejo credential"、 "人机账号"、"registry pull 401"、"docker push unauthorized"
管理 Nomad 节点的 host volume 配置。支持创建、列出、删除 volume。 使用场景:"配置 volume"、"创建 host volume"、"删除 volume"、"查看 volume"
Aether 集群上 owner-triggered 一次性 build container 原语 (walking skeleton, #27). 把本地 git ref 的 tracked 树打包送上 heavy 节点宿主 docker, build + push 到内网 registry, 返回 immutable image_sha256 契约。无 CLI 子命令、无版本 bump、零 Go 代码。 使用场景: "build 镜像", "build aria-runner", "构建容器镜像", "build container", "把 Dockerfile 推到 registry", "image build", "用 Aether 跑 build", "10CG 项目 build image", "首次镜像构建", "无 CI 触发的镜像 build"
向 Aether 维护团队报告 Bug 或提交功能建议。自动收集环境信息, 自动路由到 Forgejo(内部用户)或 GitHub(外部用户)。 使用场景:"报告 bug"、"report an issue"、"提交功能建议"、 "aether 有个问题想反馈"、"feature request"、"提 issue"、 "反馈问题"、"report bug to aether"
| name | aether-dev |
| description | 开发环境部署与测试工具。支持手动部署分支到 dev、运行临时测试 Job、 绕过 CI 直接部署、查看 dev 环境日志等开发阶段操作。 使用场景:"部署这个分支到 dev"、"跑个测试 job"、"手动部署到开发环境"、"查看 dev 日志" |
| argument-hint | <action> [args] |
| disable-model-invocation | false |
| user-invocable | true |
| allowed-tools | Bash, Read, Write, AskUserQuestion |
| dependencies | {"cli":{"required":true,"min_version":"0.7.0"}} |
版本: 0.3.0 | 优先级: P1
⚠️ 此 Skill 需要 aether CLI
# 使用共享检测脚本
source "${CLAUDE_PLUGIN_ROOT}/scripts/cli-functions.sh"
require_aether_cli || exit 1
/aether:deploy/aether:status/aether:initdeploy — 部署到 dev/aether:dev deploy [service-name]
流程:
deploy/nomad.hcl + Dockerfile)# 实际执行的核心命令
# Docker 模式
IMAGE="forgejo.10cg.pub/${ORG}/${PROJECT}:dev-$(git rev-parse --short HEAD)"
docker build -t ${IMAGE} .
docker push ${IMAGE}
sed "s|__IMAGE__|${IMAGE}|g" deploy/nomad.hcl | nomad job run -
# exec 模式
rsync -avz --exclude '.git' ./ root@192.168.69.90:/opt/apps/${PROJECT}/
nomad job run deploy/nomad.hcl
与 CI 部署的区别:
| 维度 | CI 自动部署 | /aether:dev deploy |
|---|---|---|
| 触发 | push 到 main | 手动,任意分支 |
| 镜像 tag | commit SHA | dev-<short-sha> |
| 审批 | 无 | 无 |
| 用途 | 正式 dev 部署 | 临时测试、分支验证 |
run — 运行临时测试 Job/aether:dev run [--docker|--exec] <command>
快速在集群上运行一个一次性任务,无需手写 nomad.hcl。
示例:
# 在 heavy 节点上运行 Docker 容器
/aether:dev run --docker "nginx:alpine"
# 在 light 节点上运行命令
/aether:dev run --exec "/opt/apps/my-script/test.sh"
# 运行带端口映射的容器
/aether:dev run --docker "my-api:dev-abc123" --port 3000
生成的临时 Job:
job "dev-temp-<timestamp>" {
region = "global"
datacenters = ["dc1"]
type = "batch"
constraint {
attribute = "${node.class}"
value = "<heavy_workload|light_exec>"
}
group "task" {
task "run" {
driver = "<docker|exec>"
config {
image = "<image>" # Docker 模式
command = "<command>" # exec 模式
}
resources {
cpu = 500
memory = 512
}
}
}
}
Job 名称带 dev-temp- 前缀,便于识别和清理。
logs — 查看 dev 服务日志/aether:dev logs <service-name>
流程:
# 获取最新的 allocation ID
ALLOC_ID=$(curl -s "${NOMAD_ADDR}/v1/job/${SERVICE}/allocations" | \
jq -r '[.[] | select(.ClientStatus == "running")] | sort_by(.CreateTime) | last | .ID')
# 获取日志
nomad alloc logs ${ALLOC_ID}
# 如果有 stderr
nomad alloc logs -stderr ${ALLOC_ID}
clean — 清理临时 Job/aether:dev clean
清理所有 dev-temp-* 前缀的临时 Job:
# 列出所有临时 Job
curl -s "${NOMAD_ADDR}/v1/jobs" | jq -r '.[] | select(.Name | startswith("dev-temp-")) | .Name'
# 逐个停止并清除
for JOB in $(curl -s "${NOMAD_ADDR}/v1/jobs" | jq -r '.[] | select(.Name | startswith("dev-temp-")) | .Name'); do
nomad job stop -purge ${JOB}
done
restart — 重启 dev 服务/aether:dev restart <service-name>
重启服务的所有 allocation(不重新部署):
# 获取所有 running allocation
ALLOCS=$(curl -s "${NOMAD_ADDR}/v1/job/${SERVICE}/allocations" | \
jq -r '.[] | select(.ClientStatus == "running") | .ID')
# 逐个重启
for ALLOC in ${ALLOCS}; do
nomad alloc restart ${ALLOC}
done
# 检查 Dockerfile 存在
if [ ! -f "Dockerfile" ]; then
echo "错误: 当前目录未找到 Dockerfile"
echo "修复:"
echo " 1. 运行 /aether:init 生成 Dockerfile"
echo " 2. 或手动创建: touch Dockerfile"
exit 1
fi
# 构建镜像并捕获错误
if ! docker build -t "${IMAGE}" . 2>&1; then
echo ""
echo "错误: Docker 镜像构建失败"
echo "常见原因:"
echo " - Dockerfile 语法错误(检查 FROM/RUN/COPY 指令)"
echo " - 依赖下载失败(检查网络连接或镜像源)"
echo " - COPY 的文件不存在(检查 .dockerignore 是否排除了必要文件)"
echo "修复: docker build -t ${IMAGE} . --no-cache --progress=plain"
exit 1
fi
# 推送镜像并捕获错误
PUSH_OUTPUT=$(docker push "${IMAGE}" 2>&1)
PUSH_EXIT=$?
if [ $PUSH_EXIT -ne 0 ]; then
if echo "$PUSH_OUTPUT" | grep -qi "unauthorized\|authentication\|denied"; then
echo "错误: Registry 认证失败 (${AETHER_REGISTRY})"
echo "修复:"
echo " docker login ${AETHER_REGISTRY}"
echo " # 或检查凭据: cat ~/.docker/config.json"
elif echo "$PUSH_OUTPUT" | grep -qi "timeout\|connection refused\|no such host"; then
echo "错误: 无法连接到 Registry (${AETHER_REGISTRY})"
echo "修复:"
echo " 1. 检查 Registry 是否运行: curl -s https://${AETHER_REGISTRY}/v2/"
echo " 2. 检查 DNS 解析: nslookup ${AETHER_REGISTRY}"
echo " 3. 检查网络连通: ping ${AETHER_REGISTRY}"
else
echo "错误: 镜像推送失败"
echo "详情: ${PUSH_OUTPUT}"
fi
exit 1
fi
# 提交 Job 并捕获错误
JOB_OUTPUT=$(nomad job run deploy/nomad.hcl 2>&1)
JOB_EXIT=$?
if [ $JOB_EXIT -ne 0 ]; then
if echo "$JOB_OUTPUT" | grep -qi "connection refused"; then
echo "错误: 无法连接到 Nomad (${NOMAD_ADDR})"
echo "修复: 检查 Nomad 服务状态或运行 /aether:setup 重新配置"
elif echo "$JOB_OUTPUT" | grep -qi "403\|permission\|token"; then
echo "错误: Nomad ACL 认证失败"
echo "修复: 检查 NOMAD_TOKEN 环境变量是否正确"
elif echo "$JOB_OUTPUT" | grep -qi "constraint"; then
echo "错误: 没有节点满足 Job 约束条件"
echo "修复:"
echo " 1. 检查节点状态: nomad node status"
echo " 2. 检查 nomad.hcl 中的 constraint 配置"
elif echo "$JOB_OUTPUT" | grep -qi "invalid\|parse error\|syntax"; then
echo "错误: nomad.hcl 配置文件语法错误"
echo "修复: nomad job validate deploy/nomad.hcl"
else
echo "错误: Job 提交失败"
echo "详情: ${JOB_OUTPUT}"
fi
exit 1
fi
# 带超时的 API 调用
api_call() {
local url="$1"
local result
result=$(curl -s --connect-timeout 5 --max-time 15 "$url" 2>&1)
if [ $? -ne 0 ]; then
if echo "$result" | grep -qi "timed out"; then
echo "错误: 请求超时 ($url)"
echo "修复:"
echo " 1. 检查网络连通: ping $(echo $url | sed 's|https\?://||;s|/.*||;s|:.*||')"
echo " 2. 检查防火墙规则"
echo " 3. 增加超时重试: curl --connect-timeout 10 $url"
else
echo "错误: 网络请求失败 ($url)"
echo "详情: ${result}"
fi
return 1
fi
echo "$result"
}
# rsync 文件同步
RSYNC_OUTPUT=$(rsync -avz --exclude '.git' ./ root@${TARGET_NODE}:/opt/apps/${PROJECT}/ 2>&1)
RSYNC_EXIT=$?
if [ $RSYNC_EXIT -ne 0 ]; then
if echo "$RSYNC_OUTPUT" | grep -qi "permission denied\|ssh"; then
echo "错误: SSH 连接失败 (${TARGET_NODE})"
echo "修复:"
echo " 1. 测试 SSH: ssh root@${TARGET_NODE} 'echo ok'"
echo " 2. 检查密钥: ls -la ~/.ssh/id_ed25519"
echo " 3. 检查权限: chmod 600 ~/.ssh/id_ed25519"
elif echo "$RSYNC_OUTPUT" | grep -qi "No such file\|not found"; then
echo "错误: 目标路径不存在 (/opt/apps/${PROJECT}/)"
echo "修复: ssh root@${TARGET_NODE} 'mkdir -p /opt/apps/${PROJECT}'"
elif echo "$RSYNC_OUTPUT" | grep -qi "No space left"; then
echo "错误: 目标节点磁盘空间不足"
echo "修复: ssh root@${TARGET_NODE} 'df -h /opt/apps/'"
else
echo "错误: 文件同步失败"
echo "详情: ${RSYNC_OUTPUT}"
fi
exit 1
fi
执行前需要配置 Aether 集群入口,参考 /aether:setup。
# 1. 项目级配置
if [ -f ".aether/config.yaml" ]; then
NOMAD_ADDR=$(yq '.cluster.nomad_addr' .aether/config.yaml)
CONSUL_HTTP_ADDR=$(yq '.cluster.consul_addr' .aether/config.yaml)
AETHER_REGISTRY=$(yq '.cluster.registry' .aether/config.yaml)
fi
# 2. 全局配置 fallback
if [ -z "$NOMAD_ADDR" ] && [ -f "$HOME/.aether/config.yaml" ]; then
NOMAD_ADDR=$(yq '.cluster.nomad_addr' ~/.aether/config.yaml)
CONSUL_HTTP_ADDR=$(yq '.cluster.consul_addr' ~/.aether/config.yaml)
AETHER_REGISTRY=$(yq '.cluster.registry' ~/.aether/config.yaml)
fi
# 3. 未配置则提示
if [ -z "$NOMAD_ADDR" ]; then
echo "请先运行 /aether:setup 配置集群"
exit 1
fi
/aether:setup → 配置集群入口(首次使用)
↓
开发阶段 生产阶段
───────── ─────────
/aether:init → 项目接入
/aether:dev → 开发测试部署
/aether:status → 状态查询 → 状态查询
/aether:deploy → 生产部署
/aether:rollback → 生产回滚