- 构建工具
- CLI
【免费下载链接】leiningen
Moved to Codeberg; this is a temporary convenience mirror
resources/leiningen/new/plugin/CHANGELOG.md是 Leiningen 内建plugin项目模板随工程一起生成的一份变更日志模板。它遵循 Keep a Changelog 的约定组织版本与变更类别,并通过 Mustache 占位符与渲染数据联动,在lein new plugin创建插件工程时自动生成。阅读本文后,你将理解这份模板的结构、占位符的填充机制,以及如何在开发自己的 Leiningen 插件时正确维护变更日志。
这份 CHANGELOG 从哪来:lein new plugin的模板生成链路
在 Leiningen 中,lein new plugin <project-name>会调用src/leiningen/new/plugin.clj中的plugin函数生成一个插件工程的完整骨架:
(defn plugin "A leiningen plugin project template." [^String name] (let [render (renderer "plugin") unprefixed (if (.startsWith name "lein-") (subs name 5) name) data {:name name :unprefixed-name unprefixed :sanitized (sanitize unprefixed) :year (year) :date (date)}] (main/info (str "Generating a fresh Leiningen plugin called " name ".")) (->files data ["project.clj" (render "project.clj" data)] ["README.md" (render "README.md" data)] [".gitignore" (render "gitignore" data)] [".hgignore" (render "hgignore" data)] ["src/leiningen/{{sanitized}}.clj" (render "name.clj" data)] ["LICENSE" (render "LICENSE" data)] ["CHANGELOG.md" (render "CHANGELOG.md" data)])))从源码可见,plugin模板一共生成 7 个文件:project.clj、README.md、.gitignore、.hgignore、主源码文件src/leiningen/{{sanitized}}.clj、LICENSE,以及本文的主角CHANGELOG.md。所有模板资源都存放在resources/leiningen/new/plugin/目录下,与源码一一对应。
模板渲染使用src/leiningen/new/templates.clj提供的renderer与->files机制:renderer是一个高阶函数,按leiningen/new/<template>/<file>的约定从 classpath 上定位 Mustache 模板文件,并用 Stencil 渲染;->files则是生成文件的迷你 DSL,会自动创建父目录,并把路径本身也当作 Mustache 模板处理(因此{{sanitized}}能出现在文件路径中)。
模板源码逐段解读:Keep a Changelog 约定的落地
resources/leiningen/new/plugin/CHANGELOG.md全文如下(模板原样,含占位符):
# Change Log All notable changes to this project will be documented in this file. This change log follows the conventions of keepachangelog.com. ## [Unreleased] ### Changed - Add a new arity to `make-widget-async` to provide a different widget shape. ## [0.1.1] - {{date}} ### Changed - Documentation on how to make the widgets. ### Removed - `make-widget-sync` - we're all async, all the time. ### Fixed - Fixed widget maker to keep working when daylight savings switches over. ## 0.1.0 - {{date}} ### Added - Files from the new template. - Widget maker public API - `make-widget-sync`. [Unreleased]: https://sourcehost.site/your-name/{{name}}/compare/0.1.1...HEAD [0.1.1]: https://sourcehost.site/your-name/{{name}}/compare/0.1.0...0.1.1这份模板把 Keep a Changelog 的核心约定直接固化成了可编辑的骨架,值得注意的要点有:
- 文件头:
# Change Log标题,以及"All notable changes to this project will be documented in this file"这一声明,提示作者只记录值得记录的、对使用者可见的变更。 ## [Unreleased]区块:作为当前开发版本的累积区,所有新变更先记录在这里,发布时才下沉到正式版本小节。- 版本小节:
[0.1.1] - {{date}}、0.1.0 - {{date}}采用"版本号 + 发布日期"的格式;{{date}}是 Mustache 占位符,由生成时机的当前日期填充(见下一节)。第一个版本小节不加链接锚点,后续版本才加方括号以配合底部链接定义。 - 变更类别:模板展示了 Keep a Changelog 中"按类型分组"的组织方式——
Added(新增)、Changed(变更)、Removed(移除)、Fixed(修复)。其中make-widget-sync被移除、make-widget-async新增 arity 的示例,恰好演示了"全异步化"这一典型重构如何记录。 - 底部链接定义:
[Unreleased]与[0.1.1]指向compare/...形式的版本对比地址,占位符{{name}}会被替换为真实工程名。[Unreleased]对比0.1.1...HEAD,[0.1.1]对比0.1.0...0.1.1,这符合语义化版本发布时的常见 diff 路径。
Mustache 占位符如何被填充:渲染数据与时间函数
模板中的{{date}}、{{name}}由plugin函数传入的data映射填充。data中的:name就是用户在命令行输入的工程名;:unprefixed-name则按 Leiningen 插件的命名惯例,把lein-前缀剥掉——例如输入lein new plugin lein-foo,unprefixed就是foo,它决定了后续命令名与命名空间的形态。
:date与:year由src/leiningen/new/templates.clj中的工具函数实时生成:
(defn year "Get the current year. Useful for setting copyright years and such." [] (.get (Calendar/getInstance) Calendar/YEAR)) (defn date "Get the current date as a string in ISO8601 format." [] (let [df (java.text.SimpleDateFormat. "yyyy-MM-dd")] (.format df (java.util.Date.))))即{{date}}会被替换为运行lein new plugin当天的yyyy-MM-dd格式日期(如2026-09-28),{{year}}则用于README.md与LICENSE中的版权年份。这也解释了模板中的版本小节为什么写成## [0.1.1] - {{date}}——初次生成时自动带上发布日期,之后维护者按需修改。
此外,模板文件的换行符在渲染时会统一处理:templates.clj的fix-line-separators会把模板内的\n替换为系统行分隔符,除非设置了环境变量LEIN_NEW_UNIX_NEWLINES强制使用 Unix 换行;对应行为在test/leiningen/test/new/templates.clj的line-separators测试中有覆盖。
配套模板文件:一个完整插件工程如何协同
CHANGELOG 不是孤立文件,它与插件模板的其他产物共同构成可运行的工程骨架。以下四个文件与本文主题直接相关:
resources/leiningen/new/plugin/project.clj:生成的工程配置,关键点是:eval-in-leiningen true——插件代码在 Leiningen 进程内执行,这是插件与普通应用工程的本质区别;许可为EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0。resources/leiningen/new/plugin/name.clj:生成的主源码文件,命名空间为leiningen.{{unprefixed-name}},任务函数签名[project & args]——第一个参数project是插件运行时的工程 map,这也是 Leiningen 任务的标准形态。resources/leiningen/new/plugin/README.md:提供插件安装与使用的两种典型场景——用户级插件放进:userprofile 的:plugins向量,工程级插件放进project.clj的:plugins向量。resources/leiningen/new/plugin/gitignore:忽略target、classes、pom.xml、*.jar、.nrepl-port等构建产物,与.hgignore配套覆盖 Git 与 Mercurial 两种 VCS。
完整调用关系是:lein new plugin经由src/leiningen/new.clj的new任务解析参数并解析模板命名空间,最终落到leiningen.new.plugin/plugin这一入口函数。new任务还支持--to-dir、--force、--snapshot、--template-version等选项,以及用lein new :show plugin查看模板文档。
实战:如何维护插件工程的 CHANGELOG
生成插件工程后,把CHANGELOG.md当作工程的一部分持续维护,推荐做法如下:
- 开发期间写入
[Unreleased]:每次提交值得注意的变更(新增 API、破坏性修改、bug 修复),立即在[Unreleased]下按类别补充条目,避免发布前突击回忆。 - 发布时下沉版本:例如发布
0.1.1时,把[Unreleased]的内容移到## [0.1.1] - <发布日期>下,把{{date}}替换为真实日期,并新建空的[Unreleased]区块供下一轮开发使用。 - 保持类别一致:沿用模板已有的
Added、Changed、Removed、Fixed分组;Keep a Changelog 还允许Deprecated(弃用)与Security(安全)等类别,可按需增补。 - 更新底部对比链接:发布新版本后,补上
[0.1.1]等引用链接定义(compare/0.1.0...0.1.1形式),并让[Unreleased]指向最新版本与HEAD的对比;把占位符{{name}}替换为真实工程名(如my-plugin),sourcehost.site/your-name/换成实际的代码托管地址。 - 遵守版本号语义:模板从
0.1.0起步,后续按语义化版本规则递增;示例中make-widget-sync被移除属于破坏性变更,按惯例应在主版本号或次版本号上体现,而"全异步化"正是这类变更的典型触发场景。
模板机制的源码验证
test/leiningen/test/new/templates.clj中的测试从侧面验证了这套模板机制:
renderers测试断言:当模板资源缺失时,渲染器会以Template resource 'leiningen/new/my_template/boom' not found.的形式中止——这保证了plugin模板引用的CHANGELOG.md等资源必须真实存在于 classpath,缺失即失败,而非静默生成空文件。files测试断言:->files支持:executable true选项把生成文件标记为可执行,可用于生成脚本类模板。line-separators测试断言:模板输出会按平台规范统一换行,保证生成的CHANGELOG.md在 Windows 与 Unix 环境下行为一致。
相关阅读
- 模板生成入口:
src/leiningen/new/plugin.clj、src/leiningen/new.clj - 模板渲染基础设施:
src/leiningen/new/templates.clj - 同目录的其他模板资源:
resources/leiningen/new/plugin/(含project.clj、README.md、name.clj、gitignore、hgignore、LICENSE) - 模板机制测试:
test/leiningen/test/new/templates.clj - 编写自定义模板的完整指南:
doc/TEMPLATES.md
- 构建工具
- CLI
【免费下载链接】leiningen
Moved to Codeberg; this is a temporary convenience mirror
相关推荐
Leiningen 项目变更日志实战:解读 `lein new app` 生成的 CHANGELOG.md 模板与 keepachangelog 规范
Leiningen 项目变更日志实战:解读 lein new app 生成的 CHANGELOG.md 模板与 keepachangelog 规范 本文以 Le
构建工具CLILeiningen `lein new template` 生成的 CHANGELOG.md 模板:Keep a Changelog 规范与 Mustache 变量注入机制
Leiningen lein new template 生成的 CHANGELOG.md 模板:Keep a Changelog 规范与 Mustache 变量
构建工具CLILeiningen 插件开发实战:从 `lein new plugin` 模板骨架到可安装发布的完整指南
Leiningen 插件开发实战:从 lein new plugin 模板骨架到可安装发布的完整指南 lein new plugin 是 Leiningen 内
构建工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考