rocks

package module
v0.0.0-...-d0d2323 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: BSD-2-Clause Imports: 3 Imported by: 0

README

go-luarocks

A Go library for managing rocks inside a Tarantool installation. It ships two backends: vendored upstream LuaRocks 3.9.2 driven through gopher-lua, and a pure-Go reimplementation of the subset Tarantool needs. Targets Tarantool only — no PUC-Lua or LuaJIT-as-distinct, no Windows. Linux + macOS, amd64 + arm64.

Quick start

import (
    rocks "github.com/tarantool/go-luarocks"
    "github.com/tarantool/go-luarocks/client"
)

r, err := client.New(rocks.Config{
    Tree:       "/path/to/tarantool-tree",
    WorkingDir: ".",
    Tarantool: rocks.TarantoolConfig{
        Prefix:     "/opt/tarantool",
        IncludeDir: "/opt/tarantool/include/tarantool",
    },
    Servers: []string{"http://rocks.tarantool.org/"},
})
// r.Install / r.Build / r.Make / r.List / r.Show / r.Which / r.Pack / r.Unpack

Backends

Most callers want the fast path, so client.New(cfg) defaults to the native backend. Two backends exist:

  • native (default) — pure-Go, no Lua VM. Supports the five write ops (Install/Build/Make/Pack/Unpack) plus List/Show/Which, and returns rocks.ErrNotImplemented for the other upstream commands.
  • luaclient.New(cfg, client.WithBackend(client.BackendLua)). Runs vendored upstream LuaRocks 3.9.2 through gopher-lua (the LState boots lazily on first use), giving full upstream command coverage. This is the REFERENCE for correct behavior.

Reads (List/Show/Which) always go through the native tree reader regardless of backend.

Backend parity

The native backend is built to be a behavioral drop-in for the lua reference — zero divergence in deployed artifacts — so a consumer like tt can prefer tt → lua → go and treat the backend choice as a pure performance decision.

A differential harness verifies this: it installs the same fixture through BOTH backends into two separate trees and diffs the deployed trees against each other (upstream LuaRocks is its own oracle). The contract is that every DEPLOYED ARTIFACT — any file NOT under share/tarantool/rocks/ — is byte-equal between backends. Run it (needs a real tarantool on PATH plus its lua.h):

go test ./client/ -tags integration_luaengine -run TestBackendParity -v

Documented, non-failing differences: the manifest format (share/tarantool/rocks/manifest), the per-rock bookkeeping subtree under share/tarantool/rocks/ (native does not copy the .rockspec into the install dir), and compiled .so modules (not byte-reproducible across two compilations — asserted by presence, not bytes).

Why

Tarantool's tt CLI currently runs LuaRocks by embedding ~70 Lua files in gopher-lua. This works but couples build/pack/run subsystems to a Lua runtime, makes errors hard to type, and leaks process-wide state. This project replaces that with a narrow, pure-Go library that other Go tooling can compose without spinning up a Lua VM — except for sandboxed .rockspec evaluation, which still needs gopher-lua to handle conditional rockspecs.

Packages

  • deps — version parsing, dependency constraints, transitive resolver
  • rockspec — sandboxed gopher-lua rockspec evaluator
  • manif — upstream-compatible manifest reader/writer
  • fetch — source fetch backends (http(s), git/git+https/git+ssh, file://)
  • tree — on-disk tree layout, deploy, conflicts, which
  • build — build backends (builtin, cmake, make, command)
  • remote — HTTP remote index
  • client — the Rocks facade + native and lua engines

License

BSD-2-Clause. © 2026 Eugene Blikh.

Documentation

Overview

Package rocks is a pure-Go implementation of the LuaRocks subset needed to manage rocks inside a Tarantool installation.

The library produces on-disk artifacts (tree layout, manifest files, .rock archives) that are byte-equal to what upstream LuaRocks 3.9.2 would produce for the same rock at the same version against a Tarantool target. A rock installed by lib/luarocks is queryable by upstream LuaRocks's `luarocks show` and vice versa.

The facade — `client.New(rocks.Config{...}).Install(ctx, "metrics", opts)` — lives in the sub-package `github.com/tarantool/go-luarocks/client` rather than at this root: it calls `deps.Resolve` and composes the `remote` index, both of which transitively import this root package for shared data types, so a facade here would form an import cycle.

Index

Constants

View Source
const LuaRocksVersion = "3.9.2"

LuaRocksVersion is the LuaRocks release the embedded Tarantool fork is based on for the lua backend (see internal/luarocks). The native backend produces on-disk artifacts byte-equal to this LuaRocks version.

Variables

View Source
var (
	// ErrMissingTarantoolHeaders signals that a build backend needs the
	// Tarantool development headers but Config.Tarantool.IncludeDir is
	// unset or does not contain `tarantool/module.h`.
	ErrMissingTarantoolHeaders = errors.New("rocks: tarantool headers not found")

	// ErrMissingTarantoolBinary signals that an operation requires the
	// `tarantool` binary on disk (e.g. shelling out for capability probes)
	// and Config.Tarantool.Executable is unset or does not point at an
	// executable.
	ErrMissingTarantoolBinary = errors.New("rocks: tarantool binary not found")

	// ErrUnsupportedCommand is a reserved sentinel for callers that want to
	// reject a luarocks subcommand they consider out of scope. It is not
	// returned by this package today: Exec passes argv straight to the
	// embedded dispatcher, and the typed methods use ErrNotImplemented.
	ErrUnsupportedCommand = errors.New("rocks: unsupported command")

	// ErrUnsupportedRockspecFeature signals that a rockspec used a feature
	// the evaluator does not implement (e.g. build.type outside the
	// {"builtin","cmake","make","command","none"} set, or a platforms
	// block we don't recognize). Fail loud rather than silently skip.
	ErrUnsupportedRockspecFeature = errors.New("rocks: unsupported rockspec feature")

	// ErrNotImplemented signals that the active backend (Engine) has no
	// implementation for the invoked method. It is the SOLE error a method
	// returns in that case — no silent no-op, no zero-value success.
	// Callers branch on it with errors.Is(err, ErrNotImplemented) to
	// detect an unsupported operation for the selected backend.
	ErrNotImplemented = errors.New("rocks: method not implemented by this backend")
)

Sentinel errors callers can branch on with errors.Is.

These replace the "deep cc/cmake failure or generic os.exit from the Lua side" pattern that tt currently string-matches on. Every facade method documents which of these it can return.

Functions

This section is empty.

Types

type Build

type Build struct {
	Type    string
	Modules map[string]Module
	Install BuildInstall

	CopyDirectories []string

	// Variables — cmake -D pass-through (cmake backend only).
	Variables map[string]string

	// make backend.
	Makefile         string
	BuildTarget      string
	BuildVariables   map[string]string
	InstallTarget    string
	InstallVariables map[string]string
	// BuildPass/InstallPass gate the make (always) and cmake (format >= 3.0)
	// build/install phases. nil means the default (run the phase); an explicit
	// false skips it. Tri-state models Lua's nil→default-true.
	BuildPass   *bool
	InstallPass *bool

	// command backend.
	BuildCommand   string
	InstallCommand string

	Platforms map[string]Build
}

Build mirrors the rockspec `build = {...}` table.

Type ∈ {"builtin","cmake","make","command","none"}. Anything else is ErrUnsupportedRockspecFeature.

Platforms contains per-platform Build overrides that the platforms-merge step (rockspec/platforms.go) folds into the top-level fields least- specific-to-most-specific. After Eval, Platforms is empty.

type BuildInstall

type BuildInstall struct {
	Lua  map[string]string
	Lib  map[string]string
	Bin  map[string]string
	Conf map[string]string
}

BuildInstall mirrors the rockspec `build.install = {...}` table. Each map is destination-name → source-path-relative-to-rockspec-dir.

type Builder

type Builder interface {
	Build(ctx context.Context, spec *Rockspec, srcDir, destDir string) error
}

Builder runs the build backend declared in spec.Build.Type against srcDir (the unpacked source) and writes the build output under destDir (`<tree>/share/tarantool/rocks/<name>/<ver>/build`).

The default implementation lives in package build and dispatches on spec.Build.Type ∈ {"builtin","cmake","make","command","none"}.

type Config

type Config struct {
	// Tree is the root of the Tarantool rocks tree to read from and install into.
	Tree string
	// WorkingDir is the base directory for relative paths and build staging;
	// the facade never reads the process cwd.
	WorkingDir string
	// Tarantool describes the Tarantool installation rocks are built against.
	Tarantool TarantoolConfig
	// Servers are the rocks-repository base URLs queried for remote operations.
	Servers []string
	// Rockspec governs the sandboxed rockspec evaluator.
	Rockspec RockspecConfig
	// Logger receives structured operation logs; nil disables logging.
	Logger *slog.Logger

	// InsecureServers lists URL hosts (no scheme) for which TLS certificate
	// verification is skipped by the http fetcher. Escape hatch: upstream
	// luarocks disables certs for `rocks.tarantool.org`; we ALLOW that opt-in
	// via this field but default to verifying.
	InsecureServers []string
}

Config carries every input a Rocks operation needs.

WorkingDir is explicit — the facade never reads process cwd; callers pass WorkingDir directly.

Tarantool fields come from one source of truth on the caller side; the library does not parse `tarantool --version` itself.

type Dep

type Dep struct {
	Name string
	// Namespace is the "user" half of a namespaced dependency written
	// "user/rock" (upstream util.split_namespace). Empty for the common
	// unqualified form.
	Namespace   string
	Constraints []VersionConstraint
}

Dep is one entry in the rockspec `dependencies` (or build_dependencies) list. Name is the dependency rock name; Constraints is the AND'd set of version constraints from "foo >= 1, < 2".

type Deploy

type Deploy struct {
	// WrapBinScripts is tri-state: nil = field absent (default: wrap),
	// non-nil = the rockspec's explicit deploy.wrap_bin_scripts value.
	WrapBinScripts *bool
}

Deploy mirrors the rockspec top-level `deploy = {...}` table. Upstream repos.should_wrap_bin_scripts consults deploy.wrap_bin_scripts to decide whether command scripts get a launcher wrapper.

type Description

type Description struct {
	Summary    string
	Detailed   string
	License    string
	Homepage   string
	IssuesURL  string
	Maintainer string
	Labels     []string
}

Description mirrors the rockspec `description = {...}` table.

type ExternalDep

type ExternalDep struct {
	Header  string
	Library string
}

ExternalDep is one entry in the rockspec `external_dependencies` table: Header is e.g. "tarantool/module.h"; Library is e.g. "tarantool".

type Fetcher

type Fetcher interface {
	Fetch(ctx context.Context, url, destDir string) (string, error)
}

Fetcher pulls a source at url into destDir and returns the on-disk path to the unpacked working tree.

The default implementation lives in package fetch and switches on URL scheme: http(s) via net/http, git* via the go-git library (no git binary), file:// directly.

type InstallStep

type InstallStep struct {
	Name     string
	Version  Version
	URL      string
	Rockspec *Rockspec
}

InstallStep is one entry in the topo-ordered install list produced by deps.Resolve. Steps appear deepest-dep-first: a step's prerequisites all precede it. The facade's Install method walks this list in order.

type InstalledRock

type InstalledRock struct {
	Name    string
	Version string
}

InstalledRock is one row of the result returned by Rocks.List. Version is the raw display string matching what `luarocks list` would print.

type Manifest

type Manifest struct {
	// Repository indexes installed rocks: name → version → per-arch entry.
	Repository map[string]map[string]RepoEntry
	// Modules maps a module name to the "name/version" rocks that provide it.
	Modules map[string][]string
	// Commands maps a command-binary name to the "name/version" rocks that
	// provide it.
	Commands map[string][]string
	// Dependencies records each rock's resolved deps: name → version → deps.
	Dependencies map[string]map[string][]Dep
}

Manifest is the top-level tree manifest at `<tree>/manifest`. The upstream format is a Lua-source serialization of a table.

type ManifestStore

type ManifestStore interface {
	ReadTree(path string) (*Manifest, error)
	WriteTree(path string, m *Manifest) error
	ReadRock(path string) (*RockManifest, error)
	WriteRock(path string, rm *RockManifest) error
}

ManifestStore reads and writes the on-disk manifest files in a tree.

  • ReadTree / WriteTree handle `<tree>/manifest` (the top-level index).
  • ReadRock / WriteRock handle `<tree>/share/tarantool/rocks/<name>/<ver>/rock_manifest` (the per-rock file index).

The default implementation is manif.FileStore.

type Module

type Module struct {
	Path      string
	Sources   []string
	Incdirs   []string
	Libdirs   []string
	Libraries []string
	Defines   []string
}

Module is a single entry in the rockspec `build.modules` table.

String-form module `["foo.bar"] = "src/foo/bar.lua"`: Path != "". Table-form module `["foo.bar"] = {sources = {...}, ...}`: Sources non-empty.

type RemoteIndex

type RemoteIndex interface {
	// Query returns candidate versions of `name`. A non-empty namespace selects
	// the server's per-namespace manifest (/manifests/<namespace>/) and filters
	// results to that namespace, mirroring upstream manifest_search.
	Query(ctx context.Context, name, namespace string) ([]VersionedRock, error)
}

RemoteIndex queries a rock server (or aggregate of servers) for every known version of `name`. It is the seam the deps resolver uses to discover candidate versions to install.

The default implementation is remote.HTTPRemoteIndex, constructed by client.New(cfg) from cfg.Servers. Tests inject fakes via this interface.

type RepoEntry

type RepoEntry struct {
	Arch     string
	Modules  map[string]string
	Commands map[string]string
	// Dependencies is the resolved name→version map of the rock's installed
	// dependencies, mirroring upstream repo.dependencies (writer.lua
	// update_dependencies → scan_deps). Always emitted for an installed entry
	// (as {} when the rock has no deps).
	Dependencies map[string]string
}

RepoEntry is one entry under `repository.<name>.<version>`. Arch is typically "installed" for a Tarantool tree.

Modules and Commands carry the per-arch index that upstream luarocks emits inside each repository entry: module name → on-disk slashed path for Modules, command-binary name → relative install path for Commands. Populated by the facade after `tree.Deploy` returns the *RockManifest* for the installed rock. Empty for a freshly-installed entry only if the rock genuinely has no modules / commands.

type RockManifest

type RockManifest struct {
	// RockspecFile is the versioned rockspec filename (`<name>-<version>.rockspec`)
	// under which the rockspec's md5 is keyed, matching upstream make_rock_manifest
	// (which walks fs.find(install_dir) where the rockspec was copied under that
	// name). Empty when no rockspec was recorded.
	RockspecFile string
	// Rockspec is the md5 of the rockspec file (keyed by RockspecFile).
	Rockspec string
	Lua      map[string]string
	Lib      map[string]string
	Bin      map[string]string
	Conf     map[string]string
	Doc      map[string]string
}

RockManifest is the per-rock manifest at `<tree>/share/tarantool/rocks/<name>/<ver>/rock_manifest`. Each map is path → md5 hex.

type Rockspec

type Rockspec struct {
	// Package is the rock name.
	Package string
	// Version is the version-revision string (e.g. "1.0-1").
	Version string
	// RockspecFormat is the declared rockspec_format (e.g. "3.0"), if any.
	RockspecFormat string
	// Description mirrors the rockspec description table.
	Description Description
	// SupportedPlatforms restricts the rock to the listed platforms, if set.
	SupportedPlatforms []string
	// Dependencies are the runtime dependencies.
	Dependencies []Dep
	// BuildDependencies are needed only to build the rock.
	BuildDependencies []Dep
	// ExternalDependencies are non-Lua libraries/headers, keyed by symbolic name.
	ExternalDependencies map[string]ExternalDep
	// TestDependencies are needed only to run the rock's tests.
	TestDependencies []Dep
	// Source describes where the rock's source is fetched from.
	Source Source
	// Build describes how the rock is built and installed.
	Build Build
	// Deploy mirrors the rockspec top-level `deploy = {...}` table.
	Deploy Deploy
	// HasBuild records whether a `build` table was present in the rockspec at
	// all. Upstream distinguishes an absent build table ("build table not
	// specified") from one present without a type ("build type not
	// specified") for pre-3.0 formats; an empty Build.Type alone cannot tell
	// the two apart.
	HasBuild bool
	// RawSource is the exact bytes of the `.rockspec` file this spec was
	// evaluated from. Eval populates it so the installer can copy the rockspec
	// verbatim into the per-rock install dir (as upstream does). Empty for
	// hand-constructed specs.
	RawSource []byte
	// Per-list dependency platform overrides (dependencies.platforms etc.),
	// keyed by platform name. MergePlatforms index-merges them into the
	// corresponding flat lists and clears these. External overrides overwrite
	// entries by symbolic name.
	DependenciesPlatforms         map[string][]Dep
	BuildDependenciesPlatforms    map[string][]Dep
	TestDependenciesPlatforms     map[string][]Dep
	ExternalDependenciesPlatforms map[string]map[string]ExternalDep
}

Rockspec is the parsed and validated contents of a `.rockspec` file. `name-version-revision.rockspec` filenames serialize Package, Version joined by `-`.

type RockspecConfig

type RockspecConfig struct{}

RockspecConfig governs the sandboxed evaluator. It carries no options today (the evaluator runs the rockspec in an all-nil environment matching upstream, with no host env access); it is retained as the Eval configuration seam.

type ShowInfo

type ShowInfo struct {
	Package      string
	Version      string
	Summary      string
	License      string
	Homepage     string
	Modules      []string
	Dependencies []Dep
}

ShowInfo is the projection returned by Rocks.Show — the human-readable summary upstream's `luarocks show` prints.

type Source

type Source struct {
	// URL drives Fetcher selection (http/git/file).
	URL string
	// Tag is the git tag to check out (git-only).
	Tag string
	// Branch is the git branch to check out (git-only).
	Branch string
	// MD5 is the expected archive checksum, verified for http downloads.
	MD5 string
	// File overrides the downloaded archive's basename (else derived from URL).
	File string
	// Dir overrides the directory the archive is expected to unpack into.
	Dir string
	// Module is the legacy SCM module name (cvs/svn); rarely used.
	Module string
	// Platforms carries per-platform Source overrides from source.platforms.
	// MergePlatforms folds them into the top-level Source fields and clears this.
	Platforms map[string]Source
	// Identifier pins a scm-/dev- version to a concrete git commit, formatted
	// "YYYYMMDD.HHMMSS.<shorthash>" from the HEAD commit (upstream
	// fetch/git.lua:60-77). Set by the git fetcher; empty otherwise.
	Identifier string
}

Source mirrors the rockspec `source = {...}` table.

type TarantoolConfig

type TarantoolConfig struct {
	Executable string
	Prefix     string
	IncludeDir string
	Version    string
}

TarantoolConfig describes the Tarantool installation rocks are built against. Executable is the path to the `tarantool` binary; Prefix and IncludeDir come from the caller (typically tt's GetTarantoolPrefix). Version is the "x.y.z" string for diagnostic / golden-file headers.

type Version

type Version struct {
	Raw        string
	Components []float64
	Revision   int
	// HasRevision distinguishes an explicit "-N" suffix from a defaulted
	// Revision of 0. Upstream's __eq/__lt compare revisions only when BOTH
	// operands carry one; the Go API needs this flag to tell "absent" from 0.
	HasRevision bool
	IsSCM       bool
	IsDev       bool
}

Version is a parsed rock version.

Components holds the numeric dot-separated parts ("1.2.3" → [1, 2, 3]). Revision is the trailing "-N" (defaults to 0 if absent). IsSCM is true for "scm-N"; IsDev is true for "dev-N". Both sort ABOVE numeric releases per upstream `core/vers.compare_versions`. Raw is the original string for round-trip serialization (e.g. into rock_manifest filenames).

type VersionConstraint

type VersionConstraint struct {
	Op      string
	Version Version
}

VersionConstraint is one operator+version pair from a constraint expression. Op ∈ {"==", "~=", ">", "<", ">=", "<=", "~>"}.

type VersionedRock

type VersionedRock struct {
	Name    string
	Version Version
	URL     string
	Spec    *Rockspec
}

VersionedRock is one row from a RemoteIndex.Query result: a (name, version) tuple and the URL of the resource (.rock or .rockspec) on the originating server. Spec, if non-nil, is the preloaded rockspec for that version — the resolver uses it instead of re-fetching when present.

Directories

Path Synopsis
Package build implements the four supported rockspec build backends — builtin, cmake, make, command — plus the none no-op.
Package build implements the four supported rockspec build backends — builtin, cmake, make, command — plus the none no-op.
Package client implements the Rocks facade — the keystone public API that composes the manif, rockspec, fetch, build, tree, deps and remote subsystems into LuaRocks operations.
Package client implements the Rocks facade — the keystone public API that composes the manif, rockspec, fetch, build, tree, deps and remote subsystems into LuaRocks operations.
Package deps implements version parsing, constraint matching and the transitive dependency resolver used by the Rocks facade.
Package deps implements version parsing, constraint matching and the transitive dependency resolver used by the Rocks facade.
Package fetch retrieves a rock's `source.url` into a working directory.
Package fetch retrieves a rock's `source.url` into a working directory.
internal
luarocks
Package luarocksembed exposes the vendored Tarantool LuaRocks fork (luarocks-3.9.2-tarantool) (under src/) and the extra shims (under extra/) as an embedded filesystem.
Package luarocksembed exposes the vendored Tarantool LuaRocks fork (luarocks-3.9.2-tarantool) (under src/) and the extra shims (under extra/) as an embedded filesystem.
Package manif reads and writes LuaRocks tree- and rock-manifest files.
Package manif reads and writes LuaRocks tree- and rock-manifest files.
Package remote implements the default rocks.RemoteIndex against an HTTP(S) rock server.
Package remote implements the default rocks.RemoteIndex against an HTTP(S) rock server.
Package rockspec implements the sandboxed gopher-lua evaluator for .rockspec source files.
Package rockspec implements the sandboxed gopher-lua evaluator for .rockspec source files.
Package tree implements the on-disk Tarantool-rocks layout: the path scheme under <tree>/share/tarantool, <tree>/lib/tarantool, <tree>/bin, and the Deploy operation that copies a built rock's artifacts into it.
Package tree implements the on-disk Tarantool-rocks layout: the path scheme under <tree>/share/tarantool, <tree>/lib/tarantool, <tree>/bin, and the Deploy operation that copies a built rock's artifacts into it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL