feat: rename skills/skiff to pouch and move ACK state under .pouch

Use ~/.pouch, the pouch CLI, and .pouch.yaml as the SSOT container.
Keep the inner skills/ packages, and store ACK project state in
.pouch/ack instead of docs/ack.
This commit is contained in:
2026-08-25 15:20:02 +08:00
parent ee31278947
commit f3cd56b78e
83 changed files with 1265 additions and 1192 deletions
+8 -8
View File
@@ -5,9 +5,9 @@
支持三种用法:
- **独立配置中心**:一个专门的 Git 仓库管所有机器的所有 Compose 服务(如 app00
- **项目内 Compose 环境**:在普通项目里放 `.skiff/deployer/{prod,test,dev}/`
- **项目内 Compose 环境**:在普通项目里放 `.pouch/deployer/{prod,test,dev}/`
把这个项目的生产/测试/开发环境用 rsync + docker compose 部署
- **Argo CD / GitOps**:项目里放 `.skiff/deployer/argocd.yaml`Agent 改 GitOps 并开 MR
- **Argo CD / GitOps**:项目里放 `.pouch/deployer/argocd.yaml`Agent 改 GitOps 并开 MR
你合并后由 Argo CD 同步。仓库既可以只写 Git 地址(部署时浅 clone),也可以指定本机已有目录。
## 什么时候使用
@@ -42,7 +42,7 @@ vhom1 上那个 naiveproxy 为什么 sync 失败?
web1 能出网,直接让它从 https://... 把包拉下来装
# 项目内环境
给这个项目建好 .skiff/deployerprod 和 test 分别放到两台机器上
给这个项目建好 .pouch/deployerprod 和 test 分别放到两台机器上
把 test 环境重新部署一下
prod 的 compose 加个 redis,改完发上去
@@ -55,13 +55,13 @@ GitOps 我已经 clone 在 ../infra-gitops,用那个目录开 MR
## Agent 会做什么
1. 读服务/环境目录(及共享的父目录)的 `_config.yaml`,确定目标机器和远程路径;
项目内布局从 `.skiff/deployer/` 自动发现,无需额外配置
项目内布局从 `.pouch/deployer/` 自动发现,无需额外配置
2. 用 skill 自带脚本把本地目录同步到远程(rsync,自动排除 `data/``_data/`
3. 在远程执行对应的 `docker compose` 操作(启动 / 重建 / 升级 / 重启)
4. deb 包安装走独立脚本:scp 上传到暂存目录后远程 apt 安装,失败自动修依赖
5. 同步后查看容器状态和日志确认生效
6. 只针对你指定的那一个服务操作,不会批量动整台机器
7. Argo CD:读 `.skiff/deployer/argocd.yaml`,浅 clone 或使用 `repo_dir`,改清单,推分支开 MR,停下来等你合并;
7. Argo CD:读 `.pouch/deployer/argocd.yaml`,浅 clone 或使用 `repo_dir`,改清单,推分支开 MR,停下来等你合并;
不直接 kubectl 发布,不把 Harbor/TLS 密钥提交进 Git
项目内布局下,远程目录名自动带上项目前缀(如 `my-project-prod`),
@@ -80,14 +80,14 @@ GitOps 我已经 clone 在 ../infra-gitops,用那个目录开 MR
## Argo CD 的两种接法
在业务项目里放 `.skiff/deployer/argocd.yaml`。Agent 只改 GitOps 并开 MR**你合并之后** Argo CD 才部署。镜像、namespace、域名、Secret 名以 GitOps 清单为准,不必在这个文件里再抄一遍。
在业务项目里放 `.pouch/deployer/argocd.yaml`。Agent 只改 GitOps 并开 MR**你合并之后** Argo CD 才部署。镜像、namespace、域名、Secret 名以 GitOps 清单为准,不必在这个文件里再抄一遍。
### 1. 只写仓库地址(默认)
本机不用长期放 GitOps 仓库。部署时 Agent 浅 clone 到临时目录,改完开 MR,用完删掉。
```yaml
# .skiff/deployer/argocd.yaml
# .pouch/deployer/argocd.yaml
repo: git@git.example.com:org/infra-gitops.git
```
@@ -105,7 +105,7 @@ repo: git@git.example.com:org/infra-gitops.git
GitOps 仓库已经 checkout 在旁边时,写 `repo_dir`,Agent 直接进这个目录改、推分支、开 MR。
```yaml
# .skiff/deployer/argocd.yaml
# .pouch/deployer/argocd.yaml
repo: git@git.example.com:org/infra-gitops.git
repo_dir: ../infra-gitops
```
+9 -9
View File
@@ -42,7 +42,7 @@ description: >-
| 信号 | 轨道 |
|------|------|
| ArgoCD / GitOps / 集群 / 开 MR 部署 / 项目有 `.skiff/deployer/argocd.yaml` | Argo CD,见 [argocd.md](references/argocd.md) |
| ArgoCD / GitOps / 集群 / 开 MR 部署 / 项目有 `.pouch/deployer/argocd.yaml` | Argo CD,见 [argocd.md](references/argocd.md) |
| sync、`TGT=`、某台机器、`compose.yaml` | Compose(下文布局与步骤) |
| 两者都有且意图不清 | 先问 |
@@ -57,7 +57,7 @@ description: >-
### Argo CD
源项目 `.skiff/deployer/argocd.yaml``repo` 写 Git 地址(部署时浅 clone),或加 `repo_dir` 用已有 checkout。
源项目 `.pouch/deployer/argocd.yaml``repo` 写 Git 地址(部署时浅 clone),或加 `repo_dir` 用已有 checkout。
改 GitOps 清单,不要改 Compose 脚本。密钥不入库。两种接法见 skill README,步骤见 [argocd.md](references/argocd.md)。
### 两种 Compose 布局
@@ -75,14 +75,14 @@ repo/
远程目录名 = 目录末级名:`vyyo1/naiveproxy``/opt/app/naiveproxy`
**B. 项目内环境布局**:项目根放 `.skiff/deployer/{prod,test,dev}/`
**B. 项目内环境布局**:项目根放 `.pouch/deployer/{prod,test,dev}/`
每个环境一个目录。从项目内任意位置运行脚本即自动发现(也可用 `DEPLOYER_ROOT`
显式指定),无需环境变量:
```
my-project/
├── src/ ... # 项目本体
└── .skiff/deployer/
└── .pouch/deployer/
├── _config.yaml # 三个环境共享默认(node/base_path 等)
├── argocd.yaml # 可选,Argo CD 指针(不是 compose 环境)
├── prod/
@@ -106,7 +106,7 @@ skill 目录下的 `scripts/deploy/` 是通用部署工具链(lib/sync/remote/
不依赖具体项目路径。部署根按以下顺序解析:
1. 环境变量 `DEPLOYER_ROOT` 显式指定(独立配置中心仓库用这个)
2. 从当前目录向上找 `.skiff/deployer/`(项目内环境布局自动发现)
2. 从当前目录向上找 `.pouch/deployer/`(项目内环境布局自动发现)
3. skill 安装位置兜底(仅用于查看,没有可部署服务)
```bash
@@ -162,9 +162,9 @@ python3 <skill-dir>/scripts/deploy/sync.py <service-path>
项目环境布局:
1. 项目根建 `.skiff/deployer/{env}/`env 通常为 prod/test/dev
1. 项目根建 `.pouch/deployer/{env}/`env 通常为 prod/test/dev
2. 每个环境写 `compose.yaml`;三个环境共享的 node/base_path 放
`.skiff/deployer/_config.yaml`
`.pouch/deployer/_config.yaml`
3. 环境有差异(不同主机、不同排除项)→ 在该环境的 `_config.yaml` 覆盖
4. 同名冲突或需要固定远程目录名 → `_config.yaml``name:`
5. 首次部署前确认目标主机的远程目录不存在旧内容(rsync `--delete` 会清掉)
@@ -173,7 +173,7 @@ python3 <skill-dir>/scripts/deploy/sync.py <service-path>
独立仓库布局:配置移入 `unused/`(自动脱离发现体系),远程按需手动清理:
`ssh <node> "cd <base_path>/<name> && docker compose down"`,数据卷按需保留或删除。
项目环境布局:删除对应 `.skiff/deployer/{env}/` 目录即可脱离发现体系,远程清理同上。
项目环境布局:删除对应 `.pouch/deployer/{env}/` 目录即可脱离发现体系,远程清理同上。
#### 7. 向节点安装 deb 包
@@ -204,7 +204,7 @@ python3 <skill-dir>/scripts/deploy/deb.py <target> apt https://example.com/foo_1
完整步骤与 `argocd.yaml` 字段见 [argocd.md](references/argocd.md)。
1. 读项目 `.skiff/deployer/argocd.yaml`(无则只问 Git 地址,写成 `repo:`)。
1. 读项目 `.pouch/deployer/argocd.yaml`(无则只问 Git 地址,写成 `repo:`)。
`repo_dir` 则用该目录;否则把 `repo` 浅 clone 到临时目录,用完删除。
2. 在工作副本里按**已有应用惯例**新增 Application,或只改镜像 tag / 清单。
3. 从最新默认分支拉出分支,commit、push,用 `glab`/`gh`/`tea` 开 MRCLI 对项目 404 则把
@@ -2,7 +2,7 @@
`_config.yaml`**Compose 轨道**脚本解析,决定同步目标与排除规则。可放在**服务目录、部署根或其任意祖先目录**;子目录中的字段覆盖父目录(继承合并)。
Argo CD 轨道用独立文件 `.skiff/deployer/argocd.yaml`,字段见 [argocd.md](argocd.md)。
Argo CD 轨道用独立文件 `.pouch/deployer/argocd.yaml`,字段见 [argocd.md](argocd.md)。
不要把 `argocd:` 嵌进本文件(脚本解析器不支持嵌套映射)。
## 放置位置(两种布局)
@@ -10,16 +10,16 @@ Argo CD 轨道用独立文件 `.skiff/deployer/argocd.yaml`,字段见 [argocd.
| 布局 | 部署根 | `_config.yaml` 典型位置 |
|------|--------|------------------------|
| 独立配置中心仓库(`DEPLOYER_ROOT` 指向) | 仓库根 | 主机目录 `vyyo1/_config.yaml`、服务目录 |
| 项目内环境 `.skiff/deployer/{env}/` | `.skiff/deployer/` | 根级共享默认、各环境目录覆盖 |
| 项目内环境 `.pouch/deployer/{env}/` | `.pouch/deployer/` | 根级共享默认、各环境目录覆盖 |
项目布局示例:
```yaml
# .skiff/deployer/_config.yaml — 三个环境共享
# .pouch/deployer/_config.yaml — 三个环境共享
node: my-vps
base_path: /srv/apps
# .skiff/deployer/prod/_config.yaml — 仅生产环境差异
# .pouch/deployer/prod/_config.yaml — 仅生产环境差异
node: prod-vps # 覆盖父级
name: my-project-api # 可选,覆盖默认的 {项目名}-{env}
```
@@ -111,7 +111,7 @@ sync_exclude:
| 同一 SSH 主机多个服务 | 主机目录写一份 `node`/`base_path`,子服务免重复 |
| 各服务目标不同 | 服务目录单独写 `_config.yaml` |
| 个别覆盖 | 子目录只写差异字段 |
| 项目三环境同主机 | `.skiff/deployer/_config.yaml` 写共享 node,各环境只放差异 |
| 项目三环境同主机 | `.pouch/deployer/_config.yaml` 写共享 node,各环境只放差异 |
| 项目环境分属不同主机 | 各环境 `_config.yaml` 分别写 `node` |
## 示例
@@ -137,4 +137,4 @@ node: deploy@web2-backup
- 目录内有 `compose.yaml`
- 能通过继承或兜底解析出 `node`
- 独立仓库布局:路径中不含 `unused/`
- 项目环境布局:位于部署根 `.skiff/deployer/` 内(其外的项目文件不扫描)
- 项目环境布局:位于部署根 `.pouch/deployer/` 内(其外的项目文件不扫描)
+12 -10
View File
@@ -3,7 +3,7 @@
支持两种布局:
1. 独立配置中心仓库(DEPLOYER_ROOT 指向,或 skill 安装位置)
2. 项目内环境目录 .skiff/deployer/{prod,test,dev}/(从 CWD 自动发现)
2. 项目内环境目录 .pouch/deployer/{prod,test,dev}/(从 CWD 自动发现;兼容 .skiff/deployer
"""
from __future__ import annotations
@@ -18,10 +18,11 @@ DEFAULT_BASE_PATH = "/opt/app"
DEFAULT_SYNC_EXCLUDES = ("data", "_data")
_SKILL_DIR = Path(__file__).resolve().parent.parent # scripts/
PROJECT_ROOT: Path | None = None
_PROJECT_LAYOUT_DIRS = (".pouch", ".skiff")
def _find_project_root() -> Path:
"""部署根:DEPLOYER_ROOT > 从 CWD 向上找 .skiff/deployer > skill 安装位置。"""
"""部署根:DEPLOYER_ROOT > 从 CWD 向上找 .pouch/deployer > skill 安装位置。"""
env = os.environ.get("DEPLOYER_ROOT", "").strip()
if env:
p = Path(env).expanduser().resolve()
@@ -31,9 +32,10 @@ def _find_project_root() -> Path:
return p
cur = Path.cwd()
while True:
cand = cur / ".skiff" / "deployer"
if cand.is_dir():
return cand
for dirname in _PROJECT_LAYOUT_DIRS:
cand = cur / dirname / "deployer"
if cand.is_dir():
return cand
if cur == cur.parent:
break
cur = cur.parent
@@ -48,13 +50,13 @@ def project_root() -> Path:
def in_project_layout(root: Path | None = None) -> bool:
"""部署根是否为某项目内的 .skiff/deployer/。"""
"""部署根是否为某项目内的 .pouch/deployer/。"""
root = root or project_root()
return root.name == "deployer" and root.parent.name == ".skiff"
return root.name == "deployer" and root.parent.name in _PROJECT_LAYOUT_DIRS
def project_display_name(root: Path | None = None) -> str:
"""项目名:git 仓库名优先,否则 .skiff 的父目录名。"""
"""项目名:git 仓库名优先,否则 .pouch 的父目录名。"""
root = root or project_root()
anchor = root.parent.parent if in_project_layout(root) else root
try:
@@ -111,7 +113,7 @@ def load_config(config_path: os.PathLike | str, *, required: bool = True) -> dic
def config_paths_for_service(service_dir: str) -> list[Path]:
"""收集部署根自身及服务目录各层 _config.yaml(祖先在前,服务目录在后)。
部署根的 _config.yaml(如 .skiff/deployer/_config.yaml)作为全局默认,
部署根的 _config.yaml(如 .pouch/deployer/_config.yaml)作为全局默认,
对所有环境/服务生效。
"""
root = project_root()
@@ -333,7 +335,7 @@ def service_info(service_dir: str, *, strict: bool = True) -> dict | None:
def is_deployable_dir(path: Path, root: Path) -> bool:
if "unused" in path.parts or "__pycache__" in path.parts:
return False
if ".skiff" in path.parts and root.name != "deployer":
if any(part in _PROJECT_LAYOUT_DIRS for part in path.parts) and root.name != "deployer":
return False
if not (path / "compose.yaml").is_file():
return False