jbang v0.143.0(Published 2026-10-30T22:07:21Z,约北京时间 10 月 31 日 06:07)把别名换版本收成同一种写法:alias:version@catalog。不管 catalog 里的 script-ref 是 Maven GAV、GitHub/GitLab/Bitbucket URL,还是带 ${jbang.app.version} 的占位符,调用侧都用这一套。本机装上 0.143.0 后,在本地 catalog 里给 Spring Boot CLI 建了 bootcli 别名:默认打出 Spring CLI v3.4.5,写成 bootcli:3.5.5@... 就切到 Spring CLI v3.5.5,再用钉住的 CLI 从 start.spring.io 抽出一个 Spring Boot 4.1.2 工程并能起来。
官方 Release 标题就是 Unified version syntax for aliases。文档在 Alias Catalogs。它强调这是选择器,不是 lockfile:浮动版本或 git 分支仍会解析到「今天」指向的东西。下面只写本机跑过的行为。
环境
从 GitHub Release 解压官方 zip,把 bin/jbang 链到 ~/.local/bin:
$ jbang version
0.143.0
JDK 是本机的 21。演示目录里放本地 jbang-catalog.json,以及后面用到的 bootprobe.java。路径写成 /path/to/demo。
本地 catalog:Spring Boot CLI 默认钉在 3.4.5
Spring Boot CLI 的坐标在 Maven Central 上能直接下到 jar,但没有可用的 Main-Class 清单项。本机用 jbang alias add -m org.springframework.boot.cli.SpringCli 写进 catalog,字段名是 main:
{ "aliases": {
"bootcli": {
"script-ref": "org.springframework.boot:spring-boot-cli:3.4.5",
"description": "Spring Boot CLI catalog default 3.4.5",
"main": "org.springframework.boot.cli.SpringCli",
"enable-preview": false
},
"bootcli-prop": {
"script-ref": "org.springframework.boot:spring-boot-cli:${jbang.app.version:3.4.5}",
"description": "Spring Boot CLI with jbang.app.version placeholder",
"main": "org.springframework.boot.cli.SpringCli",
"enable-preview": false
},
"bootprobe": {
"script-ref": "bootprobe.java",
"description": "Prints jbang.app.version and Spring Boot jar version from classpath"
}
}
}
不带版本时,走 catalog 默认 GAV:
$ jbang bootcli@./jbang-catalog.json version
Spring CLI v3.4.5
同一别名加上 :3.5.5,解析目标换成 3.5.5,CLI 自己报的版本也跟着变:
$ jbang bootcli:3.5.5@./jbang-catalog.json version
Spring CLI v3.5.5
--verbose 里能看到自动替换的过程(时间戳因机器而异,这里只留关键行):
Version '3.5.5' requested for alias
Applying automatic version replacement to: org.springframework.boot:spring-boot-cli:3.4.5
Version pinned Maven GAV: org.springframework.boot:spring-boot-cli:3.4.5 → org.springframework.boot:spring-boot-cli:3.5.5
Spring CLI v3.5.5
占位符写法也成立。catalog 默认仍是 3.4.5,调用 bootcli-prop:3.5.5@... 时 ${jbang.app.version:3.4.5} 被换成 3.5.5,输出同样是 Spring CLI v3.5.5。官方说明:一旦 script-ref 里写了 ${jbang.app.version},就优先走属性替换,不再做 GAV 自动改写。
非 GAV 别名:script-ref 不动,属性照样传入
Release 里写得很清楚:请求了显式版本时,一定会带上 -Djbang.app.version=<version>,哪怕 script-ref 对不上任何自动改写规则。本机用一个读属性的脚本对着 Spring Boot 3.4.5 坐标验证:
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.springframework.boot:spring-boot:3.4.5
public class bootprobe {
public static void main(String[] args) {
String pinned = System.getProperty("jbang.app.version", "<unset>");
String boot = org.springframework.boot.SpringBootVersion.getVersion();
System.out.println("jbang.app.version=" + pinned);
System.out.println("SpringBootVersion=" + boot);
}
}
$ jbang bootprobe@./jbang-catalog.json
jbang.app.version=<unset>
SpringBootVersion=3.4.5
$ jbang bootprobe:9.9.9@./jbang-catalog.json
jbang.app.version=9.9.9
SpringBootVersion=3.4.5
第二次把 9.9.9 传进了系统属性,但 //DEPS 仍是写死的 3.4.5,所以 SpringBootVersion 不变。这和 GAV 自动改写是两回事:一个改坐标,一个只负责把请求版本塞给脚本。
钉住的 CLI 真去拉 Spring 工程
换版本如果只停在 version 子命令,说服力不够。本机继续用钉住的 3.5.5 CLI 调 Initializr:
$ jbang bootcli:3.5.5@./jbang-catalog.json init \
--dependencies=web \
--build=maven \
--java-version=21 \
--name=demo \
--package-name=com.example.demo \
--extract \
demo-app
Using service at https://start.spring.io
Project extracted to '/path/to/demo/demo-app'
抽出的 pom.xml 父工程是当时 start.spring.io 给的 Spring Boot 4.1.2,依赖是 spring-boot-starter-webmvc,java.version 为 21。补了一个 /hello 控制器后打包启动:
:: Spring Boot :: (v4.1.2)
Tomcat started on port 18081 (http) with context path '/'
Started DemoApplication in 1.306 seconds
$ curl -s http://127.0.0.1:18081/hello
hello-from-jbang-pinned-bootcli
HTTP 200。也就是说:catalog 里只维护一条 bootcli,复现旧 CLI、核对新 CLI、再顺手拉工程,都可以靠调用时的 :版本 完成,不必为每个 Spring Boot CLI 小版本复制别名条目。
一起交流
分享你的思考,让讨论更进一步。