mcpField
A
appgarden

appgarden

github.com/lukastk云与 DevOps0492 次安装8 周前更新
代码仓库

关于 appgarden

appgarden 是用于通过 SSH 部署和运维 Web 应用的命令行技能,适合管理远程服务器的开发者。它支持多种部署方式,并可用项目配置管理不同环境。

主要功能

  1. 01通过 SSH 部署应用
  2. 02支持静态、命令及 Docker 部署
  3. 03使用 appgarden.toml 配置环境
  4. 04查看应用状态与日志
  5. 05管理应用生命周期

适用场景

  1. 01将静态站点部署到远程服务器
  2. 02按 staging 和 production 环境发布
  3. 03检查应用状态并排查日志
  4. 04重启、停止或重新部署应用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lukastk/appgarden --skill appgarden

安装后在 Claude Code 中输入/appgarden确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.env.sample
.envrc
CLAUDE.md
.gitignore
README.md
NBLITE_INSTRUCTIONS.md
nblite.toml
.github/workflows/
ci.yml
release.yml
dev_scripts/
clean-project
publish-new
run-tests
pts/appgarden/
01_remote.pct.py
03_routing.pct.py
00_config.pct.py
06_apps.pct.py
05_deploy.pct.py
04_server.pct.py
08_environments.pct.py
07_auto_docker.pct.py
02_ports.pct.py
09_tunnel.pct.py
_dir_server.pct.py
10_cli.pct.py
pts/tests/
test_cli.pct.py
test_deploy.pct.py
test_apps.pct.py
test_environments.pct.py
pts/tests/integration/
test_server_init.pct.py
conftest.pct.py

详细介绍

AppGarden CLI 技能

使用 appgarden CLI 部署或运维 Web 应用时,请使用此技能。

AppGarden 是一款无需 Agent 的部署工具:本地 CLI 通过 SSH 连接,准备远程服务,上传或克隆应用源码,写入 Caddy/systemd/Docker 文件,并将状态记录在服务器的 AppGarden 根目录下(默认为 /srv/appgarden)。

运行命令前

  • 开发或测试 AppGarden 本身时,优先从 AppGarden 仓库检出目录运行:

    cd /path/to/appgarden && uv run appgarden ...
    

    如果环境中已安装 AppGarden,也可以使用 appgarden ...。

  • 以下只读/安全的发现命令无需确认即可运行:appgarden --help、appgarden version、appgarden config show、appgarden server list、appgarden server ping、appgarden apps list、appgarden apps status、appgarden apps logs、appgarden tunnel list。

  • 对会创建、更改、部署、重启、停止、删除或公开服务的命令,请先征求确认:server add、server remove、server default、server init、deploy、apps start|stop|restart|redeploy|remove、apps meta set|replace|remove、tunnel open|close|cleanup。

  • 对以下操作要格外谨慎:

    • appgarden server init --include ssh:加固 SSH 配置并重新加载 sshd。
    • appgarden server init --include firewall:启用 UFW 并更改防火墙策略。
    • appgarden apps remove NAME --yes:删除应用资源;只有在确实要保留应用的 data/ 目录时,才添加 --keep-data。
    • appgarden deploy:可能覆盖已有应用的上传源码,并可能更新 Caddy/systemd/Docker 资源。
    • appgarden tunnel open:通过配置的服务器公开本地端口/文件/目录,直到隧道停止。

配置位置

本地配置存储于:

~/.config/appgarden/config.toml

服务器配置格式:

default_server = "myserver"

[defaults]
method = "dockerfile"
container_port = 3000

[servers.myserver]
host = "203.0.113.10"              # or hcloud_name + hcloud_context
ssh_user = "root"
ssh_key = "~/.ssh/id_rsa"
domain = "apps.example.com"
app_root = "/srv/appgarden"        # optional; defaults to /srv/appgarden

[servers.myserver.init]
skip = ["upgrades"]               # optional persistent init skips

远程状态和资源位于 app_root 下,通常为:

/srv/appgarden/garden.json          # app registry
/srv/appgarden/apps/<name>/         # per-app files/source/app.json/.env/data
/srv/appgarden/caddy/apps/*.caddy   # deployed app Caddy snippets
/srv/appgarden/caddy/tunnels/*.caddy
/srv/appgarden/tunnels/active.json

appgarden config show 会显示本地配置。对于指定服务器的命令,使用 --server <name> / -s <name> 或设置 APPGARDEN_SERVER;否则 AppGarden 会使用 default_server。

在一台主机上运行多个 garden: 为每个 garden 指定不同的 app_root(例如 /srv/appgarden 和 /srv/appgarden-proto),即可在同一台设备上运行多个 AppGarden garden。每个 garden 的状态(garden.json)、Caddy 片段以及 /etc/caddy/Caddyfile 中由 AppGarden 管理的区块,都会按 app_root 分别标识,因此一个 garden 的 server init 不会影响另一个。端口则是整台设备共享的资源——所有 garden 共用主机的 TCP 端口空间——因此端口分配记录在单个主机级注册表 /var/lib/appgarden/ports.json 中,由所有 garden 共享并通过同一把锁保护。这可以防止两个 garden 分配到相同的主机端口;你无需配置端口范围。注意事项:systemd 单元和 Docker 容器名称(appgarden-<name>)是整台设备共享的,因此同一主机上的不同 garden 必须使用唯一的应用名称。如果要升级的主机早于共享注册表功能,请对每个现有 garden 运行一次 appgarden server init——init 会将每个 garden 中已部署应用的端口同步到共享注册表(可幂等执行)。

服务器设置

服务器必须完成配置、可通过 SSH 访问,并且 DNS 必须指向该服务器。

appgarden server add myserver \
  --host <server-ip-or-hostname> \
  --ssh-user root \
  --ssh-key ~/.ssh/id_rsa \
  --domain apps.example.com

# Hetzner alternative: resolve IP through hcloud instead of --host
appgarden server add myserver \
  --hcloud-name <hetzner-server-name> \
  --hcloud-context <hcloud-context> \
  --domain apps.example.com

appgarden server list
appgarden server ping myserver
appgarden server default myserver

对于子域名部署和隧道,请创建通配符 DNS 记录,例如将 *.apps.example.com 指向服务器 IP。显式配置的自定义主机名也需要 DNS 指向服务器。

初始化服务

每个服务只需运行一次:

appgarden server init myserver

当前 server init 的行为:

  • 默认启用的可选步骤;使用 --skip 跳过:update、docker、caddy、upgrades。
  • 默认关闭的可选步骤;使用 --include 启用:firewall、ssh、fail2ban、group。
  • 始终运行的必要步骤:配置 AppGarden Caddyfile 导入块、创建目录、安装特权辅助程序/ sudoers 条目、设置部署用户的所有权、初始化状态文件,以及在可用时启动 Docker/Caddy。

示例:

appgarden server init myserver --minimal              # essential steps only
appgarden server init myserver --skip upgrades        # skip unattended-upgrades
appgarden server init myserver --include group        # create/use appgarden group for non-root deploy users
appgarden server init myserver --include firewall     # configure UFW; confirm first
appgarden server init myserver --include ssh          # harden sshd; confirm first

对于非 root SSH 用户,server init 会安装 /usr/local/bin/appgarden-privileged,并添加一条仅允许执行 AppGarden 范围内特权操作的 sudoers 规则。

首选部署流程:appgarden.toml

在项目根目录创建 appgarden.toml,并部署命名环境。

[app]
name = "myapp"
slug = "my-app"                  # optional; used by {app.slug}; falls back to name
server = "myserver"
method = "dockerfile"            # static | command | dockerfile | docker-compose | auto
source = "."                     # local path or git URL
container_port = 3000
subdomain = "{app.slug}"         # or path/url; supports placeholders
exclude = ["node_modules", ".git"]
gitignore = true                  # local uploads honor .gitignore by default
meta = { team = "frontend" }

[environments.production]
branch = "main"
subdomain = "{app.slug}"
env = { NODE_ENV = "production" }

[environments.staging]
branch = "staging"
subdomain = "{app.slug}-staging"
env_file = ".env.staging"
meta = { visibility = "internal" }

部署:

appgarden deploy production
appgarden deploy staging
appgarden deploy --all-envs
appgarden deploy --project /path/to/project production

如果存在 appgarden.toml,且未提供环境/名称,appgarden deploy 会部署所有环境。如果提供了位置参数环境,该环境必须存在。

appgarden.toml 语义
  • [app].name 为必填项。已部署应用的名称由此派生:production 使用基础名称,其他环境使用 <name>-<env>。例如:myapp 和 myapp-staging。
  • 值按以下顺序逐层覆盖:硬编码默认值 < ~/.config/appgarden/config.toml 中的全局 [defaults] < [app] 默认值 < [environments.<name>] < CLI 标志 / APPGARDEN_* 环境变量。
  • 字典字段 env 和 meta 按键合并;环境/CLI 值会覆盖较早的键。
  • 列表字段 exclude 和 volumes 会跨层拼接并去重。
  • 项目/环境配置中的字符串值支持占位符:{app.name}、{app.slug}、{env.name}。
  • 相对本地 source 和 env_file 值相对于项目目录,或相对于显式指定的 --project 文件所在目录解析。

[app] 或 [environments.<name>] 中的常用字段:

字段含义
server已配置的服务名称。
methodstatic、command、dockerfile、docker-compose 或 auto。默认值为 static。
source本地目录/路径或 Git URL。static、dockerfile、docker-compose、auto 必填;command 可选。
url显式指定的公网主机名/路径,例如 example.com 或 example.com/docs。
subdomain与 domain 或服务域名组合的前缀,例如 docs -> docs.apps.example.com。
pathdomain 或服务域名下的路径,例如 docs -> apps.example.com/docs。
domain覆盖用于 subdomain/path 的服务基础域名。
port主机端口;省略时,AppGarden 会根据其端口状态自动分配。
container_portDockerfile/auto 容器内部公开的端口。默认值为 3000。
cmdcommand 和 auto 方法的启动命令。两者均为必填项。
setup_cmdauto 的安装/构建命令;覆盖检测到的运行时默认值。
branchGit 源的分支。
env内联环境变量。
env_file本地 dotenv 文件。
meta与应用一同存储并在状态中显示的任意元数据。
exclude本地上传的 Rsync 排除模式;apps redeploy 也会复用这些模式。
gitignore本地上传是否应用 rsync 的 .gitignore 过滤规则。默认值为 true;CLI 可通过 --no-gitignore 覆盖。
volumesdockerfile 和 auto 的 Docker 卷挂载(host:container[:opts])。
created_at、updated_at、repo可选的元数据覆盖项。日期接受简短日期、ISO 字符串或 TOML 日期;如果可能,repo 会自动从本地 git origin 检测。

URL 解析顺序为:显式指定的 url;否则使用 subdomain + 域名;否则使用 path + 域名。对于子目录路由(domain/path),同一域名上的多个应用会共享生成的 Caddy 配置,AppGarden 也会检查 URL 冲突。

不使用 appgarden.toml 进行临时部署

使用 --name 和标志。大多数标志也有对应的 APPGARDEN_* 环境变量(APPGARDEN_NAME、APPGARDEN_SERVER、APPGARDEN_METHOD、APPGARDEN_SOURCE、APPGARDEN_URL、APPGARDEN_SUBDOMAIN、APPGARDEN_PATH、APPGARDEN_DOMAIN、APPGARDEN_PORT、APPGARDEN_CONTAINER_PORT、APPGARDEN_CMD、APPGARDEN_SETUP_CMD、APPGARDEN_BRANCH、APPGARDEN_ENVVAR_FILE、APPGARDEN_PROJECT、APPGARDEN_ALL_ENVS)。

# Static site / SPA
appgarden deploy --name mysite --method static --source ./dist --subdomain mysite

# Dockerfile app
appgarden deploy --name myapp --method dockerfile --source . --container-port 3000 --subdomain myapp

# Auto-generated Dockerfile; runtime is detected from package.json, requirements.txt,
# pyproject.toml, Gemfile, go.mod, or Cargo.toml. --cmd is required.
appgarden deploy --name myapp --method auto --source . --cmd "npm start" --subdomain myapp

# Bare process managed by systemd. --source is optional; --cmd is required.
appgarden deploy --name myapi --method command --source ./api --cmd "python app.py" --subdomain myapi

# Docker Compose stack; your compose file is used directly.
appgarden deploy --name mystack --method docker-compose --source ./project --subdomain mystack

# Explicit path routing instead of subdomain routing
appgarden deploy --name docs --method static --source ./docs --url apps.example.com/docs

# Env, metadata, excludes, and volumes
appgarden deploy --name myapp --method dockerfile --source . --subdomain myapp \
  --envvar NODE_ENV=production \
  --envvar-file .env.production \
  --meta team=backend --meta visibility=internal \
  --exclude node_modules --exclude .git \
  --volume ./data:/app/data \
  --no-gitignore

部署方式说明:

方式适用场景必需输入运行时行为
staticHTML/CSS/JS/SPAsource上传/克隆源代码,并使用 Caddy 直接提供服务。
command不使用 Docker 的本地进程cmd(source 可选)创建一个设置了已分配/显式端口 PORT 的 systemd 服务。
dockerfile含有 Dockerfile 的项目source在远程构建镜像、写入 AppGarden compose 文件,并通过 systemd 运行。
docker-compose现有的 compose 项目source通过 systemd 运行你的 docker compose up/down;不会注入 volumes 配置。
auto简单的 Node/Python/Ruby/Go/Rust 项目source、cmd检测运行时、写入 Dockerfile、构建镜像并写入 compose 文件。

对于本地上传,AppGarden 使用带 --delete 的 rsync,默认遵循 .gitignore,并应用任何 --exclude/exclude 模式。对于 git 源,它会在远程克隆仓库,并可选检出指定分支。

部署应用 .env 文件的环境变量优先级为:appgarden.toml env < env_file 内容 < CLI --envvar 值。

管理已部署的应用

appgarden apps list [-s server] [--short]
appgarden apps status <name> [-s server]
appgarden apps logs <name> [-s server] [-n 100]
appgarden apps restart <name> [-s server]
appgarden apps redeploy <name> [-s server]
appgarden apps stop <name> [-s server]
appgarden apps start <name> [-s server]
appgarden apps remove <name> [-s server] [--keep-data] [--yes]

运维指南:

  • 不确定确切的应用名称时,先使用 apps list。

  • 使用 apps status 查看 URL、路由、方式、端口、仓库/源、时间戳和元数据。

  • 调试时,重启前使用 apps logs NAME -n 200。

  • apps redeploy 会更新源:git 应用运行 git pull(如果已存储分支,则使用该分支);本地源应用会使用已存储的 exclude/gitignore 重新上传;Dockerfile/auto 应用会重建镜像并保留已存储的卷;static 应用会重新加载 Caddy。

  • 如果在 appgarden.toml 中更改应用名称,请先移除旧应用,以避免 URL/Caddy 冲突:

    appgarden apps remove old-name --yes
    appgarden deploy production
    

元数据

元数据是与应用一同存储的任意 JSON 兼容键值数据。

appgarden apps meta get myapp [-s server]
appgarden apps meta set myapp --meta team=backend --meta tier=premium [-s server]
appgarden apps meta replace myapp --json '{"team":"frontend"}' [-s server]
appgarden apps meta remove myapp tier visibility [-s server]

在 appgarden.toml 中,meta 字典的合并方式与 env 字典相同:

[app]
name = "myapp"
meta = { team = "backend", visibility = "internal" }

[environments.production]
meta = { visibility = "public" }
# result: { team = "backend", visibility = "public" }

本地主机隧道

使用隧道通过远程服务,以 HTTPS 暴露本地开发服务、文件、目录或命令。除非在 --cmd/--serve 中使用 --close-on-cmd-exit,否则隧道会一直运行,直到按下 Ctrl+C。

# Expose an existing local server. URL omitted => random three-word subdomain.
appgarden tunnel open 3000

# Use server domain subdomain shorthand or an explicit URL
appgarden tunnel open 3000 --subdomain preview
appgarden tunnel open 3000 --url preview.apps.example.com

# Run a local command while the tunnel is open
appgarden tunnel open 3000 --cmd "npm run dev"
appgarden tunnel open 3000 --cmd "npm run dev" --close-on-cmd-exit

# Serve a local file or directory. LOCAL_PORT is optional with --serve.
appgarden tunnel open --serve ./dist --subdomain docs-preview
appgarden tunnel open --serve ./dist --include "*.html" --exclude "node_modules"

# Reclaim a URL a dead run still holds, then open (idempotent re-open)
appgarden tunnel open 3000 --url preview.apps.example.com --replace

appgarden tunnel list [-s server]
appgarden tunnel list --json [-s server]      # machine-readable, for scripting
appgarden tunnel close <tunnel-id> [-s server]
appgarden tunnel cleanup [-s server]

--cmd 和 --serve 不能同时使用。只有当 --serve 指向目录时,--include/--exclude 才适用。隧道会分配一个远程端口,在 caddy/tunnels 下写入 Caddy 配置片段,并在 tunnels/active.json 中注册状态;清理时会移除这些资源。

--replace 是实现无人值守重新打开的关键。 隧道的 Caddy 配置片段以其自身的隧道 ID 为键,因此,如果某次运行被强制终止且未清理(例如重启或 SIGKILL),配置片段仍会占用该主机名;随后对相同 --url 执行 tunnel open 时,Caddy 重新加载会因站点定义不明确而失败,并回滚操作。--replace 会先关闭已注册到该 URL 的任何隧道。它要求明确指定 --url/--subdomain——自动生成的子域名无法匹配,因此会拒绝执行,而不会静默地什么也不做。请注意,tunnel cleanup 不能替代此操作:它只会回收远程端口上没有进程监听的隧道。

隧道在 SIGTERM 和 Ctrl+C 下都会正常关闭,因此通过监控程序执行 stop(supervisord、systemd、docker stop)时,会释放注册信息、Caddy 配置片段和远程端口,而不会遗留这些资源。

tunnel list --json 会输出 JSON 数组(没有隧道时为空数组,绝不会输出人类可读的 “No active tunnels.” 行)——请解析该数组,而不是表格。

故障排除工作流

  1. 确认配置和服务器目标:

    appgarden config show
    appgarden server list
    appgarden server ping <server>
    
  2. 检查应用状态和日志:

    appgarden apps list --short
    appgarden apps status <app>
    appgarden apps logs <app> -n 200
    
  3. 阅读错误信息后再重新部署或重启:

    appgarden apps redeploy <app>
    appgarden apps restart <app>
    
  4. 本地来源上传的常见问题:

    • 本地必须安装 rsync。
    • 加密的 SSH 密钥需要 SSH Agent:eval $(ssh-agent) && ssh-add ~/.ssh/id_rsa。
    • 非 root 部署时出现权限错误,通常意味着需要重新运行 appgarden server init --include group 或修正远程所有权。

本仓库中的参考文件

生成的 src/appgarden/*.py 文件标有 “AUTOGENERATED”;通常应在对应的 pts/appgarden/*.pct.py 源文件中进行实现修改。有关使用技能的问题,请按需阅读以下文件:

  • README.md — 面向用户的指南和 CLI 参考。
  • src/appgarden/cli.py / pts/appgarden/10_cli.pct.py — 命令定义和选项。
  • src/appgarden/config.py / pts/appgarden/00_config.pct.py — 本地配置模型和默认配置路径。
  • src/appgarden/environments.py / pts/appgarden/08_environments.pct.py — appgarden.toml 解析、合并语义和占位符。
  • src/appgarden/server.py / pts/appgarden/04_server.pct.py — 服务器初始化步骤。
  • src/appgarden/deploy.py / pts/appgarden/05_deploy.pct.py — 部署行为和远程状态。
  • src/appgarden/apps.py / pts/appgarden/06_apps.pct.py — 应用生命周期和重新部署行为。
  • src/appgarden/routing.py / pts/appgarden/03_routing.pct.py — Caddy 路由和 URL 冲突行为。
  • src/appgarden/tunnel.py / pts/appgarden/09_tunnel.pct.py — 隧道行为。

评论

支持 Markdown