
Lazycat Dynamic Deploy
- 177 installs
- 54 repo stars
- Updated April 3, 2026
- whoamihappyhacking/lazycat-skills
Helps with devops & ci/cd tasks.
About
lazycat-dynamic-deploy is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted development.
- lazycat-dynamic-deploy
- DevOps & CI/CD
- AI-coding skill
Lazycat Dynamic Deploy by the numbers
- 177 all-time installs (skills.sh)
- +4 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #429 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/whoamihappyhacking/lazycat-skills --skill lazycat-dynamic-deployAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 177 |
|---|---|
| repo stars | ★ 54 |
| Last updated | April 3, 2026 |
| Repository | whoamihappyhacking/lazycat-skills ↗ |
What it does
Helps with devops & ci/cd tasks.
Files
懒猫微服动态部署与注入指南
你是一个专业的懒猫微服应用架构师。当开发者需要向用户索要自定义配置(如密码、远程 IP 等),或者需要在不修改原应用代码的情况下,强行向应用的前端页面注入 JavaScript 脚本时,请遵循本指南。
1. 动态部署参数与模板渲染 (v1.3.8+)
懒猫微服支持在安装应用前,弹出一个 UI 界面让用户填写参数,然后利用这些参数动态渲染 lzc-manifest.yml。
步骤 A: 编写 lzc-deploy-params.yml
在项目根目录创建此文件,定义需要用户填写的字段。
params:
- id: target_ip
type: string
name: "目标服务器 IP"
description: "你要代理的内网服务器 IP"
- id: enable_debug
type: bool
name: "开启 Debug"
default_value: "false"
optional: true类型支持: string, bool, secret, lzc_uid
步骤 B: 在 lzc-manifest.yml 中使用模板渲染
使用 Go 模板语法 ({{ ... }}) 读取参数。
- 用户参数使用
.U.参数ID(例如:{{ .U.target_ip }})。如果 ID 包含.,需使用index(如{{ index .U "my.param" }})。 - 系统参数使用
.S(例如:.S.BoxDomain,.S.IsMultiInstance)。 - 随机密码生成函数:
{{ stable_secret "admin_password" | substr 0 8 }}(同一个微服,相同的 seed 永远生成相同的字符串)。
示例:
services:
myapp:
image: xxx
environment:
- REMOTE_IP={{ .U.target_ip }}
- DB_PASS={{ stable_secret "db_root_pass" }}2. 网页脚本注入 (application.injects) (v1.5.0+)
适用于在不修改第三方 Docker 镜像前端代码的情况下,向特定网页强行注入 JS 脚本(比如用来自动填充难以修改的默认密码)。
核心逻辑: 只有满足 include(白名单)且不命中 exclude(黑名单)的 HTML 页面才会被注入。
示例:实现第三方系统的自动登录
application:
injects:
- id: auto-login
mode: exact # 支持 exact(精确) 或 prefix(前缀)
include:
- "/login" # 当访问 /login 时注入
- "/#signin" # 也能匹配 hash 路由
scripts:
# 使用懒猫内置的表单填充脚本
- src: builtin://simple-inject-password
params:
user: "admin"
password: "{{ stable_secret "app_admin_pass" }}"
autoSubmit: true自定义注入脚本: 如果你想注入自己写的脚本,可以将 JS 文件放在打包目录中,通过 file:///lzcapp/pkg/content/myscript.js 引用。在脚本内部,可以通过 __LZC_INJECT_PARAMS__ 获取传入的 params 参数。
平台兼容性说明
如果需要查看详细的内置模板函数列表、系统参数列表(SysParams)或了解脚本注入的 builtin://simple-inject-password 的详细参数配置(如修改选择器),请主动读取本技能包 references/ 目录下的相关 Markdown 文档。
deploy-params =============
lzc-deploy-params.yml 是开发者定义安装时参数的配置文件。 本文档将详细描述其结构和各字段的含义。
DeployParams
| 字段名 | 类型 | 描述 |
|---|---|---|
params | []DeployParam | 开发者定义的部署参数列表 |
locales | map | 国际化相关 |
-------------------------------
DeployParam
| 字段名 | 类型 | 描述 |
|---|---|---|
id | string | 应用内的唯一ID,供国际化和manifest.yml中引用 |
type | string | 字段类型,目前支持bool、lzc_uid、string、secret |
name | string | 字段渲染时的名称,支持国际化 |
description | string | 字段渲染时详细介绍,支持国际化 |
optional | bool | 此字段是否可选。若可选则不会强制要求用户填写,若所有字段均为可选则会直接跳过部署界面 |
default_value | string | 开发者提供的默认值,支持 $random(len=5) 生成随机串(lzcos 1.5.0+) |
hidden | bool | 字段依旧生效,但不在界面中渲染 |
脚本注入(injects) ==================
概述 ====
injects 用于在指定 URL 命中的 HTML 页面中注入脚本,适合第三方应用最小侵入适配。此功能需要 lzcos 1.5.0+。 字段定义请先看 manifest.md#injects;本文聚焦行为细节和实践建议。
快速示例 ========
application:
injects:
- id: login-autofill
include:
- "/"
- "/?version=1.2&channel=stable"
- "/#login"
scripts:
- src: builtin://simple-inject-password
params:
user: "admin"
password: "admin123"规则模型 ========
include:白名单规则,任一命中即进入候选exclude:黑名单规则,任一命中即排除mode:exact或prefix,默认exact,作用于path/hashprefix_domain:非空时,仅匹配域名前缀为<prefix>-的请求injects条目按声明顺序注入;每个条目内scripts也按顺序注入
规则语法 ========
单条规则格式:
<path>[?<query>][#<hash>]
示例:
"/""/?version=1.2""/#login""/app?version=1.2&channel=stable#signin"
解析说明:
path必填,且必须以/开头querytoken 支持两种形式:key:要求 key 存在key=value:要求 key 至少有一个 value 等于该值- 单条规则内 query token 为 AND(全部满足)
- query 为 contains 语义:请求允许包含额外参数
匹配语义 ========
整体逻辑:
include是 OR:任意一条命中即可进入候选exclude是 OR:任意一条命中即拒绝- 最终结果:
matched = includeMatched && !excludeMatched
单条规则逻辑(AND):
path命中- 且
query命中(若声明) - 且
hash命中(若声明)
mode 语义(作用于 path/hash):
exact:完全匹配prefix:前缀匹配
Hash 行为(hard/soft) ======================
path/query是服务端可见条件,属于 hard 匹配hash是服务端不可见条件,自动降级为客户端 soft 匹配
这意味着:
- 服务端先根据
path/query判断是否注入 wrapper - wrapper 在浏览器端再按完整规则(含
hash)决定是否执行脚本 - 可能出现“注入了 wrapper 但因 hash 不匹配未执行脚本”,这是预期行为
执行时机与运行时参数 ====================
wrapper 的触发时机:
1. 页面加载后执行一次评估(trigger=load) 2. 监听 hashchange,每次 hash 变化后再次评估(trigger=hashchange) 3. 只要命中规则就执行脚本,不做内置去重
脚本可读取以下对象:
__LZC_INJECT_PARAMS__:来自scripts[].params__LZC_INJECT_RUNTIME__:executedBefore:当前页面生命周期内,该脚本此前是否执行过executionCount:当前是第几次执行(从1开始)trigger:load或hashchange
示例(脚本侧):
(() => {
const runtime = __LZC_INJECT_RUNTIME__ || {};
if (runtime.executedBefore) {
return;
}
const params = __LZC_INJECT_PARAMS__ || {};
console.log("inject params:", params);
})();脚本来源 ========
scripts[].src 支持:
builtin://name:使用 lzcinit 内置脚本file:///path:读取应用文件系统内脚本(常见路径/lzcapp/pkg/content/)http(s)://...:远程脚本(建议仅调试使用)
远程脚本加载会使用条件请求缓存(ETag/Last-Modified)。
内置脚本 ========
builtin://hello -----------------
打印调试信息。
参数:
message:输出内容,默认hello world
builtin://simple-inject-password ----------------------------------
自动填充账号/密码,并可选自动提交。建议仅在明确登录路径下注入。
参数说明(params):
| 参数 | 类型 | 说明 |
|---|---|---|
user | string | 账号值,默认空 |
password | string | 密码值,默认空 |
requireUser | bool | 是否必须找到账号输入框;默认逻辑:若 allowPasswordOnly=true 则 false,否则当 user 非空时为 true |
allowPasswordOnly | bool | 允许仅填充密码,默认 false |
autoSubmit | bool | 是否自动提交,默认 true |
submitMode | string | 提交模式:auto/requestSubmit/click/enter,默认 auto |
submitDelayMs | int | 自动提交前延迟(毫秒),默认 50,最小 0 |
retryCount | int | 自动提交重试次数,默认 10 |
retryIntervalMs | int | 自动提交重试间隔(毫秒),默认 300 |
observerTimeoutMs | int | DOM/状态观察超时(毫秒),默认 8000 |
debug | bool | 开启调试日志,默认 false |
userSelector | string | 显式指定账号输入框选择器 |
passwordSelector | string | 显式指定密码输入框选择器 |
formSelector | string | 限定在指定容器内搜索输入框 |
submitSelector | string | 显式指定提交按钮选择器 |
allowHidden | bool | 允许填充不可见输入框,默认 false |
allowReadOnly | bool | 允许填充只读输入框,默认 false |
onlyFillEmpty | bool | 仅当输入框为空时才填充,默认 false |
allowNewPassword | bool | 允许填充 autocomplete=new-password 的密码框,默认 false |
includeShadowDom | bool | 是否搜索开放的 Shadow DOM,默认 false |
shadowDomMaxDepth | int | Shadow DOM 最大递归深度,默认 2 |
preferSameForm | bool | 优先选择与密码框同一表单内的账号框,默认 true |
eventSequence | string 或 []string | 触发事件序列,默认 input,change,keydown,keyup,blur |
keyValue | string | 触发键盘事件时的按键值,默认 a |
userKeywords | string 或 []string | 追加账号字段关键词(逗号分隔或数组) |
userExcludeKeywords | string 或 []string | 追加账号字段排除关键词 |
passwordKeywords | string 或 []string | 追加密码字段关键词 |
passwordExcludeKeywords | string 或 []string | 追加密码字段排除关键词 |
submitKeywords | string 或 []string | 追加提交按钮关键词 |
实践建议 ========
- 需要多个页面时,优先增加多条
include,而不是放宽规则 - 登录跳转场景可直接用 query 条件约束,例如
"/?version=1.2&channel=stable" - hash 路由场景建议在脚本中结合
__LZC_INJECT_RUNTIME__.executedBefore控制是否重跑 - 强烈建议将用户名、密码改为部署参数注入,避免在代码或 manifest 中写死弱密码
- 非 HTML 响应天然不会被注入;
exclude主要用于进一步限制 HTML 页面范围(例如/admin/debug这类页面路径) - 远程脚本仅建议调试使用,正式发布建议改为
builtin://或file://
manifest.yml渲染
lzcos-v1.3.8+ 支持 manifest.yml 文件的动态渲染,以便开发者可以更好的控制部署参数。
manifest.yml 的渲染流程为
1. 开发者在项目根目录下创建一个 lzc-deploy-params.yml 文件,并使用 lzc-cli project build 打包到 lpk 中(需要 lzc-cli 版本 v1.3.7+) 2. 在运行前会跳转到一个参数补充的UI界面,要求用户补充开发者在 lzc-deploy-params.yml 中定义的所有参数 3. 系统获取到用户提供的参数,并将其作为模板参数 U 与 lpk 中的 lzc-manifest.yml 一起渲染为最终结果 4. 将最终的manifest存放在 /lzcapp/run/manifest.yml (相对于原始文件 /lzcapp/pkg/manifest.yml )并以此作为最终文件
--------------
1. 用户可以在应用列表中主动重新进入修改部署参数的页面,点击后进入步骤 2。每次修改部署参数后应用实例会被停止并重新走上述流程 2. 多实例应用下,每个用户的部署参数都是独立的,由每个用户自行填写。 3. 即使应用没有配置 lzc-deploy-params.yml 依旧会进行 manifest 渲染流程
渲染机制
使用 golang 的 text/template 对 lpk 中的 manifest.yml 进行渲染,您需要先熟悉一下Go官方的模板语法外 以下为一些内置的模板函数和模板参数。
内置模板函数
1. spring 支持的函数。( env 相关除外) 2. stable_secrt "seed" 模板函数,用来产生稳定的密码。此函数需要传递一个任意字符串。 1. 同样的 seed,不同应用的结果保证不相同 1. 同样的 seed,同样的应用不同的微服结果保证不相同 2. 同样的 seed,相同的应用相同的微服(未重新恢复出厂设置)保证多次调用结果相同
模板参数
主要为两个大参数(括号内为其简写方式)
.UserParams(.U)lzc-deploy-params.yml 中要求的参数
.SysParams(.S)系统相关参数.BoxName微服的名称.BoxDomain微服的域名.OSVersion微服系统版本号,注意如果是测试版则会强制修改为v99.99.99-xxx.AppDomain应用的域名,注意此域名目前是根据开发者写死,将来会动态分配并支持管理员动态调整。.IsMultiInstance是否为多实例部署方式,目前是开发者写死,将来版本会调整为管理员可以动态调整最终值。.DeployUID实际部署时的用户ID,单实例部署方案下无此字段。.DeployID实例本身的唯一ID
::: tip 调试阶段,您可以在 lzc-manifest.yml 任意位置添加来渲染出所有的可用参数
xx-debug: {{ . }}您可以在 application.route 里增加一条规则来查看最终的 manifest.yml
application:
route:
- /m=file:///lzcapp/run/manifest.yml或直接使用 devshell 后 cat /lzcapp/run/manifest.yml :::
示例
完整 demo 示例可以参考这里
更安全的内部密码
package: cloud.lazycat.app.redmine
name: Redmine
services:
mysql:
binds:
- /lzcapp/var/mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD={{ stable_secret "root_password" }}
- MYSQL_USER=LAZYCAT
- MYSQL_PASSWORD={{ stable_secret "admin_password" | substr 0 6 }}
image: registry.lazycat.cloud/mysql
redmine:
environment:
- DB_PASSWORD={{ stable_secret "root_password" }}多实例/单实例不同配置
如果是单实例应用,则将数据放到每个用户的文稿目录下
如果是多实例应用,则将用户数据放到应用内部
#lzc-manifest.yml
services:
some_service_name:
binds:
{{ if .S.IsMultiInstance }}
- /lzcapp/run/mnt/home:/home/
{{ else }}
- /lzcapp/run/mnt/home/{{ .S.DeployUID }}/the_name:/home/
{{ end }}启动参数由用户配置
#lzc-deploy-params.yml
params:
- id: target
type: string
name: "target"
description: "the target IP you want forward"
- id: listen.port
type: string
name: "listen port"
description: "the forwarder listen port, can't be 80, 81"
default_value: "33"
optional: true#lzc-manifest.yml
package: org.snyh.netmap
version: 0.0.1
name: netmap
application:
subdomain: netmap
upstreams:
- location: /
backend_launch_command: /lzcapp/pkg/content/netmap -target={{ .U.target }} -port={{ index .U "listen.port" }}