LuaRocks 与包生态
LuaRocks 是 Lua 事实上的包管理器,负责构建、安装、管理 Lua 模块及其依赖。与 Lua 模块与包管理:require、package 与 LuaRocks 关注的「如何使用 LuaRocks 安装模块」不同,本文聚焦「如何发布一个高质量的 Lua 包」。
一个 Lua 包从源码到被全球开发者 luarocks install,需要经历:合理的目录结构、规范的 rockspec、版本与依赖管理、自动化 CI,以及公开分发。这套流程正是 Lua 生态工程化的核心。
# 发布前的本地安装验证
luarocks make --local # 从本地 rockspec 构建并安装到用户树
luarocks install # 从服务器安装(用户视角)
包目录结构
一个规范的 LuaRocks 包遵循 src 布局约定:
my-awesome-lua/
├── src/ # 源码(rockspec 会映射到 lua/ 路径)
│ └── awesome/
│ ├── init.lua
│ ├── core.lua
│ └── utils.lua
├── spec/ # 测试(busted 等)
│ └── core_spec.lua
├── doc/
│ └── awesome.md
├── rockspec/ # rockspec 文件(或放根目录)
│ └── awesome-0.1.0-1.rockspec
├── LICENSE
├── README.md
└── .github/workflows/ci.yml
LuaRocks 通过 rockspec 的 build 字段把源文件安装到运行时的 lua/ 目录。目录结构直接影响可维护性与发布体验:
src/与spec/分离,测试不污染发布包。- 模块命名与目录对应,
awesome/core.lua→require("awesome.core")。 - 根目录放 rockspec、LICENSE、README,与 GitHub 仓库结构保持一致。
rockspec 编写规范
rockspec 是 LuaRocks 的构建清单,本身是 Lua 文件,以 rockspec_format 开头。
基本字段
-- awesome-0.1.0-1.rockspec
rockspec_format = "3.0"
package = "awesome"
version = "0.1.0-1"
source = {
url = "git+https://github.com/user/awesome-lua.git",
tag = "v0.1.0",
}
description = {
summary = "一个极简的 Lua 工具库",
detailed = [[
awesome 提供字符串处理、缓存与协程工具,
全部代码零依赖,兼容 Lua 5.1 / 5.3 / 5.4 与 LuaJIT。
]],
homepage = "https://github.com/user/awesome-lua",
license = "MIT",
issues_url = "https://github.com/user/awesome-lua/issues",
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
["awesome"] = "src/awesome/init.lua",
["awesome.core"] = "src/awesome/core.lua",
["awesome.utils"] = "src/awesome/utils.lua",
},
copy_directories = {
"doc",
},
}
构建方式
LuaRocks 支持三种构建类型,按复杂度递增:
| 类型 | 说明 | 适用场景 |
|---|---|---|
builtin | 纯 Lua 模块,只做文件复制 | 绝大多数纯 Lua 包 |
make | 调用 Makefile | 需要编译或复杂安装步骤 |
cmake | 调用 CMake | 带 C 扩展的跨平台包 |
builtin 的 modules 字段是核心:映射「运行时模块名 → 源文件」。C 扩展则用 make 配合自定义构建脚本:
build = {
type = "make",
build_variables = {
LIBNAME = "cawesome",
},
build_target = "cawesome.so",
install_target = "cawesome.so",
modules = {
["cawesome"] = {
sources = { "src/cawesome.c" },
libraries = { "m" },
},
},
}
依赖声明
依赖分三类:运行时 dependencies、构建时 build_dependencies、测试时 test_dependencies。正确分类让安装器不会装多余的包:
dependencies = {
"lua >= 5.1, < 5.5",
"luafilesystem >= 1.7",
}
build_dependencies = {
"luarocks-build-make",
}
test_dependencies = {
"busted >= 2.0",
}
版本管理与依赖策略
语义化版本
LuaRocks 版本由「包版本 + rockspec 修订号」组成:0.1.0-1 表示包版本 0.1.0、rockspec 修订第 1 次。发布遵循语义化版本(SemVer):
- 补丁(0.1.1):修复,不破坏 API。
- 次版本(0.2.0):向后兼容的新功能。
- 主版本(1.0.0):破坏性变更。
每个发布都需要对应一个 git tag,如 v0.1.0。LuaRocks 服务器要求 rockspec 文件名与 package、version 一致。
版本约束
rockspec 的依赖约束控制安装时如何选择版本:
dependencies = {
"luafilesystem >= 1.6",
"lua-cjson ~> 2.1", -- >= 2.1, < 2.2(次版本内)
"copas ^2.0", -- >= 2.0, < 3.0(主版本内)
}
| 约束符 | 含义 |
|---|---|
>= x | 大于等于 x |
~> x.y | 从 x.y 到下一个次版本前(含补丁) |
^x.y | 从 x.y 到下一个主版本前 |
x.y(裸) | 精确匹配 |
约束过宽会增加兼容风险,过窄会减少可安装场景。对纯 Lua 包建议放宽到「最低要求 + 主版本锁定」。
本地验证与发布
发布前在本地完整走一遍安装流程:
# 1. 构建并安装到本地树
luarocks make --local
# 2. 运行测试(busted)
busted spec/
# 3. 生成 rockspec 校验
luarocks lint awesome-0.1.0-1.rockspec
# 4. 打包
luarocks pack awesome 0.1.0-1
# 5. 本地安装打包结果,验证依赖解析
luarocks install awesome-0.1.0-1.src.rock
luarocks lint 会检查 rockspec 语法与必填字段,是发布前的快速门禁。测试环节见 Lua 测试工程化指南:busted 框架与 BDD 实践。
发布到官方仓库需要先在 LuaRocks.org 创建账号并上传:
luarocks --api-key=$LUAROCKS_API_KEY upload awesome-0.1.0-1.rockspec
CI 自动化发布:GitHub Actions
把「测试 + 发布」放进 CI,可以让每次 tag 打版都自动完成质量门禁与分发。
测试矩阵
# .github/workflows/ci.yml
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- lua-version: "5.1"
with-luajit: true
- lua-version: "5.3"
- lua-version: "5.4"
steps:
- uses: actions/checkout@v4
- uses: leafo/gh-actions-lua@v10
with:
luaVersion: ${{ matrix.lua-version }}
- uses: leafo/gh-actions-luarocks@v4
- run: luarocks make --local
- run: luarocks install --local busted
- run: busted spec/
矩阵覆盖多 Lua 版本的价值:5.1 与 5.4 在整数语义、table.unpack、标准库差异上的坑(见 Lua 版本对比),只有在矩阵测试中才能稳定暴露。fail-fast: false 保证一个版本失败不中断其他版本。
自动发布
发布 Job 只在打 tag 时触发,测试通过后自动上传 LuaRocks:
publish:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: leafo/gh-actions-lua@v10
with:
luaVersion: "5.1"
- uses: leafo/gh-actions-luarocks@v4
- name: 生成 rockspec 并上传
env:
LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
run: |
# 根据 tag 版本生成 rockspec
version="${GITHUB_REF_NAME#v}"
sed "s/@VERSION@/$version/" rockspec/awesome.template.rockspec > awesome-$version-1.rockspec
luarocks --api-key="$LUAROCKS_API_KEY" upload awesome-$version-1.rockspec --force
实践中常用「template rockspec」:awesome.template.rockspec 中把版本号写成 @VERSION@ 占位符,CI 依据 git tag 生成具体 rockspec。这样发版只做一件事:打 tag。
生态分发:命名空间与公开
发布到 LuaRocks 官方仓库后,包进入全球分发网络。工程化的分发还涉及:
- 命名规范:模块名与包名一致,避免与既有包冲突;可在 LuaRocks 搜索确认名称占用。
- 兼容性声明:在 README 与 rockspec 中声明支持的 Lua 版本与平台。
- 变更日志:CHANGELOG 记录每次版本的行为变化,配合语义化版本让用户判断升级风险。
- 维护策略:及时响应 issue,破坏性变更预留迁移窗口。
<!-- README 中的版本与依赖表 -->
| 版本 | Lua 5.1 | Lua 5.3 | Lua 5.4 | LuaJIT |
|------|---------|---------|---------|--------|
| 0.1.0 | ✓ | ✓ | ✓ | ✓ |
| 0.2.0 | ✓ | ✓ | ✓ | ✓ |
生态分发还有一个维度:私有分发。企业内部包可以自建 LuaRocks 服务器或用私有仓库地址,rockspec 的 source.url 指向私有 git。发布流程与公开包一致,只是访问控制不同。
常见问题(FAQ)
rockspec 与直接 git 安装有什么区别?
git 安装(git clone + 手动 package.path)适合快速开发;rockspec 提供依赖解析、版本管理、可重复安装与生态可见性。发布到 LuaRocks 后,用户 luarocks install awesome 一行完成,且版本可锁定。
为什么 upload 失败或找不到包?
常见原因:rockspec 文件名与 package + version 不一致;未登录或 API key 无效;未打对应 git tag(source.tag 不存在);LuaRocks 服务器同步延迟(上传后需要几分钟到十几分钟生效)。
builtin 构建的模块找不到?
检查 build.modules 的映射:模块名 awesome.core 应指向 src/awesome/core.lua。若源码里有 require("awesome.init") 而模块只注册了 awesome,会运行时找不到。建议模块名与文件一一对应,避免 init.lua 与目录名混用造成的歧义。
如何保证包在 LuaJIT 下也能用?
在 CI 矩阵中加入 LuaJIT(with-luajit: true)。注意 LuaJIT 基于 5.1,goto、位运算库与 64 位整数行为与 PUC Lua 不同,纯 Lua 包通常没问题,但涉及 C 扩展或整数边界时要专门回归。
依赖版本约束放太宽会怎样?
约束过宽(如 lua >= 5.1 不带上限)可能在未来 Lua 6.0 上出问题;约束过窄(如 lua == 5.4.4)会显著缩小用户群。最佳实践是声明「最低版本 + 主版本范围」,并在 CI 中覆盖主要版本做验证。
相关阅读
- Lua 模块与包管理:require、package 与 LuaRocks
- Lua 测试工程化指南:busted 框架与 BDD 实践
- Lua 版本对比:5.1、5.3、5.4 与 LuaJIT 的差异与选型指南
- 现代 Lua 工具链:LuaLS 类型注解、Stylua 与工程化实践
- Neovim 插件工程化:从架构到发布的完整指南
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。