Lua API Cookbook
Common patterns and best practices for writing Zoi package definitions.
This cookbook provides a collection of common patterns and "recipes" for handling various scenarios when creating .pkg.lua files.
Platform Mapping
Upstream projects often use different naming conventions for OS and Architecture than Zoi's standard (linux-amd64, macos-arm64, etc.). You can use a Lua table to map Zoi's system info to upstream values.
local platform_map = {
["linux-amd64"] = "x86_64-unknown-linux-gnu",
["linux-arm64"] = "aarch64-unknown-linux-gnu",
["macos-amd64"] = "x86_64-apple-darwin",
["macos-arm64"] = "aarch64-apple-darwin",
["windows-amd64"] = "x86_64-pc-windows-msvc",
}
local current_platform = SYSTEM.OS .. "-" .. SYSTEM.ARCH
local target_triple = platform_map[current_platform]
function prepare()
local url = "https://example.com/releases/download/v" .. PKG.version .. "/" .. target_triple .. ".tar.gz"
UTILS.EXTRACT(url, "source")
endConditional Build Types
Packages that support both source and pre-compiled builds should use the BUILD_TYPE variable to branch logic in prepare and package.
function prepare()
if BUILD_TYPE == "pre-compiled" then
local ext = (SYSTEM.OS == "windows") and "zip" or "tar.gz"
local url = string.format("https://example.com/bin-%s.%s", SYSTEM.OS, ext)
UTILS.EXTRACT(url, "bin")
elseif BUILD_TYPE == "source" then
cmd("git clone " .. PKG.git .. " source")
end
end
function package()
if BUILD_TYPE == "source" then
cmd("cd source && make")
zcp("source/my-app", "${pkgstore}/bin/my-app")
else
zcp("bin/my-app", "${pkgstore}/bin/my-app")
end
endDynamic Versioning from GitHub
Instead of hardcoding a version, you can fetch the latest release tag directly from GitHub using Zoi's utility functions.
local version = ZOI.VERSION or UTILS.FETCH.GITHUB.LATEST.release({ repo = "Zillowe/Zoi" })
metadata({
name = "zoi",
version = version,
-- ...
})Cloning Specific Git Tags or Revisions
When building from source, you often need to clone a specific version of a repository rather than the default main or master branch. Since Zoi executes standard shell commands via the cmd() function, you can pass standard Git arguments.
Cloning a Specific Tag
To clone a specific tag efficiently (without downloading the entire git history), use the --branch and --depth 1 flags:
function prepare()
-- Clone a specific tag (e.g. v1.2.3) based on the package version
local tag = "v" .. PKG.version
cmd("git clone --depth 1 --branch " .. tag .. " " .. PKG.git .. " source")
endChecking out a Specific Commit Revision
Git's clone --branch flag does not support specific commit hashes. To build from an exact commit revision, you must clone the repository first and then check out the commit:
local commit_hash = "a1b2c3d4e5f6g7h8i9j0"
function prepare()
-- Clone the repository and checkout the specific commit
cmd("git clone " .. PKG.git .. " source")
cmd("cd source && git checkout " .. commit_hash)
endAdvanced Verification
When a project provides a central checksums.txt file, you can use UTILS.PARSE.checksumFile to extract the relevant hash for your specific platform archive.
function verify()
if BUILD_TYPE == "pre-compiled" then
local checksum_url = "https://example.com/releases/download/v" .. PKG.version .. "/checksums.txt"
local content = UTILS.FETCH.url(checksum_url)
-- Zoi platform archives follow a pattern
local filename = string.format("my-app-%s-%s.tar.gz", SYSTEM.OS, SYSTEM.ARCH)
local hash = UTILS.PARSE.checksumFile(content, filename)
if not hash then return false end
return verifyHash(BUILD_DIR .. "/" .. filename, "sha256-" .. hash)
end
return true
endBundling Local Assets
If your package requires a configuration file or icon that is stored alongside the .pkg.lua file, use ${pkgluadir} to reference it.
-- File structure:
-- my-pkg/
-- my-pkg.pkg.lua
-- default-config.toml
function package()
-- Copy local config to the package store
zcp("${pkgluadir}/default-config.toml", "${pkgstore}/share/default-config.toml")
-- Copy a local script to user home (use with caution)
zcp("${pkgluadir}/init-script.sh", "${usrhome}/.config/my-app/init.sh")
endMulti-stage Build Logic
For complex builds, you might want to install build-time dependencies into a temporary directory or use specific output paths.
function package()
-- Create a temporary install prefix inside BUILD_DIR
local prefix = BUILD_DIR .. "/install_root"
-- Run build and install to that prefix
cmd("cd source && ./configure --prefix=" .. prefix)
cmd("cd source && make install")
-- Selectively copy only what you need to the Zoi store
zcp("install_root/bin", "${pkgstore}/bin")
zcp("install_root/lib", "${pkgstore}/lib")
endShared Logic with INCLUDE
Keep your package definitions DRY (Don't Repeat Yourself) by including external Lua scripts. This is especially useful for large lists of hashes or common build functions.
-- shared_build.pkg.lua
function common_make()
cmd("make clean")
cmd("make -j2")
end
-- package.pkg.lua
INCLUDE("shared_build.pkg.lua")
function package()
-- Use function defined in shared_build.pkg.lua
common_make()
zcp("output/bin", "${pkgstore}/bin")
endUse the .pkg.lua extension for included files if you want them to be clearly
associated with Zoi packaging, or .lua for general utility scripts. Note
that INCLUDE uses paths relative to the current script.
Using Hooks for System Integration
Use hooks to perform actions that zcp cannot do, such as updating a system database or reloading a daemon.
hooks({
post_install = {
linux = {
"systemctl daemon-reload",
"systemctl enable --now my-app.service"
},
default = { "echo 'Installation complete!'" }
},
pre_remove = {
linux = { "systemctl disable --now my-app.service" }
}
})Shell Completions
Use zshell() to bundle shell completion scripts with your package. Zoi automatically symlinks them into the user's global completions directory and cleans them up on uninstall.
-- my-cli.pkg.lua
metadata({
name = "my-cli",
repo = "community",
version = "1.0.0",
description = "A CLI tool with shell completions",
bins = { "my-cli" },
types = { "pre-compiled" },
})
function prepare()
UTILS.EXTRACT("https://example.com/my-cli-" .. SYSTEM.OS .. ".tar.zst", "src")
end
function package()
zcp("src/my-cli", "${pkgstore}/bin/my-cli")
-- Stage shell completions
zshell("${pkgluadir}/completions/_my-cli", "zsh")
zshell("${pkgluadir}/completions/my-cli.bash", "bash")
zshell("${pkgluadir}/completions/my-cli.fish", "fish")
endAfter installation, completions are available at:
- zsh:
~/.zoi/pkgs/shell/zsh/my-cli/_my-cli(add~/.zoi/pkgs/shell/zshtofpath) - bash:
~/.zoi/pkgs/shell/bash/my-cli/my-cli.bash - fish:
~/.zoi/pkgs/shell/fish/my-cli/my-cli.fish
2026 © All Rights Reserved.
- All the content is available under CC BY-SA 4.0, expect where otherwise stated.
- Source code is available on GitLab, licensed under Apache 2.0.
Last updated on
