
Boilerplates 模板机制深度指南template.json 清单、files/ 渲染管线与自定义分隔符【免费下载链接】boilerplatesCreate reusable templates and turn them into configurable workloads for homelabs and self-hosted infrastructure. Free and Open-Source.项目地址: https://gitcode.com/GitHub_Trending/bo/boilerplates导读本文是 Boilerplates 模板系统的核心技术指南。模板Template是 Boilerplates 的最小组成单元一篇可用的模板由template.json清单和files/可渲染目录构成。读完本文你将掌握模板的必需目录布局、清单字段语义slug / kind / metadata / variables、自定义分隔符 、% %、# #的渲染规则、模板发现与验证机制以及完整的生成工作流能够独立编写、校验并产出可复用的基础设施配置文件。什么是 Template模板的核心单元在 Boilerplates 中模板Template是核心单元。一个受支持的模板是一个目录目录中包含template.json模板清单manifest声明元数据与变量files/目录存放所有可渲染的输出文件。Boilerplates CLI 发现模板库后通过读取template.json了解模板的元信息与变量结构再把files/下每一个文件用自定义分隔符渲染为最终可用的基础设施配置。官方文档对当前运行时的能力界定是“只支持template.json清单 files/可渲染文件 自定义分隔符 可选的metadata.version对象”这一约束在源码中也被严格固化见下文“清单解析的源码实现”。必需的目录布局一个合法模板的最小结构如下my-template/ ├── template.json └── files/ ├── compose.yaml ├── .env └── config/ └── app.yaml布局规则template.json是唯一受支持的清单格式所有渲染内容必须位于files/之下旧版布局template.yaml、template.yml、顶层.j2文件与当前运行时不兼容。在源码中这些规则被硬编码为常量见 cli/core/template/template.pyTEMPLATE_MANIFEST_FILENAME template.json LEGACY_TEMPLATE_FILENAMES (template.yaml, template.yml) TEMPLATE_FILES_DIRNAME files当目录下找不到template.json时Template._find_manifest_file()会先检查是否存在旧版清单若存在则直接抛出兼容性错误提示“Legacy template manifests are incompatible with boilerplates 0.2.0”并指导用户将文件迁移到template.jsonfiles/结构template.py。这意味着旧版模板不会静默失效而是会在加载阶段被明确拒绝并给出迁移指引。值得说明的是当前仓库的 library/ 目录中仍保留着大量template.yaml格式的旧版模板例如 library/compose/nginx/template.yaml、library/compose/portainer/template.yaml。它们非常适合用来参考真实世界中的变量分组设计如general/ports/traefik分组但作为旧格式示例与本文所述的template.json新运行时并不直接兼容编写新模板时请以本文为准。顶层清单Manifest结构template.json顶层包含四个核心字段slug模板的唯一 ID详见下文kind模板类型如compose、terraform、ansiblemetadata展示与溯源元数据variables变量声明数组分组结构。一个完整的最小示例{ slug: my-template, kind: compose, metadata: { name: My Template, description: Short human description, author: Your Name, date: 2026-04-22, tags: [infra, dev], icon: { provider: mdi, id: docker, color: blue }, draft: false, version: { name: v1.1, source_dep_name: ghcr.io/example/my-image, source_dep_version: 1.1.0, source_dep_digest: sha256:abc123def456, upstream_ref: release-2026-04-22, notes: Tracks the tested upstream dependency snapshot } }, variables: [ { name: general, title: General, items: [ { name: service_name, type: str, title: Service name, default: my-service } ] } ] }清单解析的源码实现从源码看Template类在初始化时会依次执行以下步骤template.py定位并解析template.json必须是合法 JSON 对象否则报“must contain a JSON object”构造TemplateMetadata强制要求存在metadata对象校验kind字段必须存在Template._validate_kind依据slug/ 目录名解析模板 ID校验files/目录必须存在缺失时报missing required files/ directory校验variables必须是数组。其中kind缺失、metadata缺失、variables非数组都会抛出TemplateValidationError并最终包装为TemplateLoadError呈现在 CLI 中。也就是说清单的格式错误不会在渲染时才暴露而是在加载阶段就被拦截。slug 与模板 ID 的解析规则slug是 CLI 对外暴露的规范化模板 ID。其解析行为优先级如下若slug存在以slug为准覆盖目录名若slug以-kind结尾则该冗余后缀会被归一化去除若slug缺失退化为使用目录名。示例目录portainer/kindcomposeslugportainer-composeCLI 中使用 IDportainer这一逻辑由normalize_template_slug()实现template.pydef normalize_template_slug(slug: str, kind: str | None None) - str: normalized_slug str(slug).strip() normalized_kind str(kind or ).strip() if not normalized_slug: return normalized_slug suffix f-{normalized_kind} if normalized_kind else if suffix and normalized_slug.endswith(suffix): return normalized_slug[: -len(suffix)] return normalized_slug可以看到归一化是纯字符串操作先去掉首尾空白再检查是否以-kind结尾并截断。此外Template.set_qualified_id()template.py支持在多个库存在同名模板时生成original_id.library_name形式的限定 ID如nginx.local这是多库场景下消除歧义的机制。Metadata展示元数据与版本溯源metadata常见的字段包括name模板展示名description人类可读的简短描述author作者date创建/更新日期tags标签数组icon图标声明provider/id/colordraft草稿标记置为true时模板从正常发现中隐藏version可选的版本元数据对象。版本元数据Version Metadatametadata.version是可选的一旦出现必须是对象。支持字段name面向用户的版本标签会展示在 list/show 输出中source_dep_name上游依赖名称source_dep_version上游依赖版本source_dep_digest上游依赖镜像摘要如sha256:...upstream_ref上游引用如release-2026-04-22notes备注。关键行为metadata.version.name是用户可见的版本标签其余字段服务于上游依赖追踪snapshot 溯源整个version对象可以省略对象内部的单个字段也可以省略。源码中的对应实现是TemplateVersionMetadatatemplate.pyfrom_metadata()会先检查version是否为字典非字典直接抛出TemplateValidationError(metadata.version must be an object)字段缺失时全部落空字符串。同时__bool__只在name非空时为真因此只有定义了name的版本对象才会被视为“存在版本信息”并展示。变量声明是强制的任何在files/下文件中使用到的变量都必须先在template.json中声明。如果某个文件引用了未声明的变量模板会校验失败加载和渲染操作都会以模板错误template error形式呈现。这一规则在源码中有完整的强制执行链路template.py_validate_variable_definitions()会把files/中所有文件解析成 Jinja AST用meta.find_undeclared_variables()提取用到的变量集合再与清单中声明的变量集合做差集存在未声明变量时错误信息会列出每个缺失变量出现的具体文件路径并附带“请把它声明到 variables[].items 下”的示例片段。有意思的是未声明的变量名还会触发近似匹配提示TemplateErrorHandler.get_common_suggestions()template.py会尝试在已声明变量中寻找相似候选给出 “Did you mean: xxx” 的建议帮助作者快速定位拼写错误。这也解释了为什么模板作者必须保持“清单即事实来源manifest as source of truth”的习惯——当前运行时已经不存在独立的 schema 引用层template.json就是变量定义的唯一权威。变量组group与条目item的完整结构、类型、依赖与 toggle 规则请参考 Variables 文档此处不再展开。files/ 目录与渲染管线files/下的每一个文件都属于输出树的一部分渲染行为如下Boilerplates 会遍历files/下所有文件所有文件都使用自定义分隔符集进行渲染不含模板表达式的文件也会经过渲染管线原样透传并归一化输出路径目前与files/内部的相对路径一一对应即files/config/app.yaml渲染为输出目录/config/app.yaml渲染输出会被清洗sanitize规范化空行与行尾空白。源码层面Template._collect_template_files()template.py用os.walk递归收集files/下所有文件relative_path与output_path相同Template.render()template.py对每个文件调用jinja_env.get_template(...).render(**variable_values)再执行_sanitize_content()。清洗规则template.py包括每行去除行尾空白压缩连续空行为单个空行去除文件开头/结尾的多余空行并保证文件以单个换行结尾。此外渲染结果中内容为空或仅剩---分隔符的文件会被自动丢弃stripped ---时跳过避免生成无意义的空 YAML 文档。渲染错误也不会是裸奔的 Jinja 异常TemplateErrorHandlertemplate.py会把未定义变量、语法错误、文件找不到分别整理为友好信息并附上出错文件的行号上下文标记出错行与可操作建议。例如未定义变量会提示“Variable xxx is not defined in template.json”并建议声明或改用 var | default(value) 形式。自定义分隔符Boilerplates 使用自定义分隔符而非 Jinja 默认语法用途分隔符变量 value 块控制流% if condition %注释# comment #示例services: service_name : image: nginx:1.27.0 % if ports_enabled % ports: - http_port :80 % endif %旧版 Jinja 默认分隔符{{ }}、{% %}、{# #}会被拒绝语法解析失败。源码中这些分隔符被定义在 template.py并由_create_jinja_env()template.py注入 Jinja 环境return SandboxedEnvironment( loaderFileSystemLoader(search_path), autoescapeFalse, variable_start_stringVARIABLE_START, # variable_end_stringVARIABLE_END, # block_start_stringBLOCK_START, # % block_end_stringBLOCK_END, # % comment_start_stringCOMMENT_START, # # comment_end_stringCOMMENT_END, # # keep_trailing_newlineTrue, trim_blocksFalse, lstrip_blocksFalse, )两个细节值得注意使用SandboxedEnvironmentJinja 沙箱环境渲染在受限环境中执行变量、块、注释分隔符分别配置这意味着 只用于变量插值控制流必须使用% %模板作者需要保持这套分隔符使用的一致性。包含与导入Includes / Importsinclude与import均以模板的files/目录为基准解析% include partials/header.yaml %因为 Jinja 环境的FileSystemLoader(search_path)的搜索路径正是files_dir所以被包含文件应放在files/内的相对位置如files/partials/header.yaml。若引用的文件不存在渲染时会抛出TemplateNotFound并被TemplateErrorHandler转换为“检查相对 files/ 目录的 include/import 路径”的建议。模板发现规则模板从配置好的模板库library中发现。一个目录只有在同时满足以下两个条件时才被视为合法模板存在template.json存在files/。实践中Boilerplates 按模块目录路径发现模板例如compose/template/或terraform/template/。常用命令boilerplates compose list boilerplates compose search nginx boilerplates compose show nginx草稿模板将metadata.draft置为true即可把模板从正常发现list / lookup中隐藏。多库场景下的优先级与限定 ID 规则详见 Libraries 文档。生成工作流Generation Workflow典型流程boilerplates compose show nginx boilerplates compose generate nginx --output ./my-nginx先show查看模板运行时状态含变量默认值、依赖、toggle 状态与文件结构再generate实际产出文件。常用旗标flags--output本地输出目录--remote与--remote-pathSSH 远程上传目标--var-fileYAML 格式的变量覆盖文件--var直接在命令行覆盖变量--no-interactive非交互式生成配合--var使用可完全脚本化--dry-run预览生成结果而不写文件--show-files在 dry-run 时打印渲染出的文件内容。变量值的最终生效顺序从低到高template.json的default/value→ 配置文件保存的默认值 →--var-file→--var→ 交互式提示的回答。你保存的用户默认值只会在清单值之上叠加、仍可被命令行覆盖相关管理命令defaults list / set / get / rm / clear见 Defaults 文档。验证Validation校验单个模板boilerplates compose validate nginx校验模块下全部模板boilerplates compose validate验证覆盖范围清单结构manifest structure变量声明覆盖variable declaration coverage即files/用到的变量都已声明分隔符兼容性delimiter compatibility可渲染性renderability可选的模块级语义校验器optional semantic validators for module-specific output。从源码看验证由ValidationRunner执行validation_runner.py。它基于依赖矩阵生成多个校验用例每个用例是一组变量值组合对每个用例执行渲染失败记为stagetpl的失败开启语义校验时用get_validator_registry()注册的校验器逐一检查渲染产物失败记为stagesem并带上具体文件与校验器类名存在 kind 级校验器时对输出做模块专属校验失败记为stagekind校验器不可用或明确跳过时相关用例会被放入kind_skipped_cases。也就是说validate不只是“能不能渲染”而是会针对多组变量取值组合验证模板在依赖关系needs约束下的整体正确性并进一步检查输出是否符合对应模块如 Compose、Terraform的语义规则。这种“矩阵式验证”在 cli/core/validation/dependency_matrix.py 中被驱动。最佳实践清单统一使用template.json所有生成文件统一放在files/下渲染内容中出现过的每个变量都必须在清单中声明全模板保持一致地使用自定义分隔符除非确有必要引入变量否则在渲染文件中硬编码经过测试的上游应用版本用metadata.version.name作为面向用户的版本标签其余metadata.version字段用于记录上游快照snapshot上下文按需填充即可。这七条实践与源码的校验逻辑一一呼应前三条保证了“清单即事实来源”的强校验能够通过硬编码版本则避免为每个镜像 tag 引入不必要的变量声明版本字段的拆分使用让“用户可见标签”与“上游溯源信息”各司其职也正对应TemplateVersionMetadata中name与其余字段在展示时的不同角色。延伸阅读变量系统Variables变量组、条目字段、needs依赖、toggle 与config对象模板库LibrariesGit/static 库、发现优先级与限定 ID默认值Defaults保存的用户默认值与覆盖顺序快速上手Getting Started安装 CLI、同步库、检查与生成模板模板核心实现清单解析、分隔符配置、渲染与清洗的源码级细节校验运行器矩阵式校验的三阶段tpl / sem / kind实现仓库内旧版模板示例供参考变量分组设计compose/nginx、compose/portainer。【免费下载链接】boilerplatesCreate reusable templates and turn them into configurable workloads for homelabs and self-hosted infrastructure. Free and Open-Source.项目地址: https://gitcode.com/GitHub_Trending/bo/boilerplates创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考