A quick and dirty personal package manager
  • Lua 81.3%
  • Go 14.1%
  • C 3.8%
  • Makefile 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Mike 'Fuzzy' Partin d7e0e36cdd
All checks were successful
PR Checks / lint-and-test (pull_request) Successful in 5m2s
docs(pkgdefs): document pkgdef_no_symlink, update for lfs_pkgdefs split
Closes #345, #346
2026-07-13 00:12:52 -07:00
.forgejo/workflows fix(cli): consistent <pkg>[-<ver>] semantics across all commands 2026-07-12 13:52:00 -07:00
bin refactor: replace shell command calls with fs module across codebase 2026-07-12 03:10:09 -07:00
lfs_pkgdefs chore: remove ROADMAP.md and add lfs_pkgdefs README 2026-07-13 00:06:25 -07:00
lib/qpkg feat(cli): rename commands to add/del/use/drop, dynamic help formatting 2026-07-12 17:03:43 -07:00
luapp chore: remove unused indirect dependency strftime 2026-07-12 03:10:57 -07:00
pkgdefs docs(pkgdefs): document pkgdef_no_symlink, update for lfs_pkgdefs split 2026-07-13 00:12:52 -07:00
src feat(structure): create src/ layout, defer config.lua I/O 2026-07-10 01:42:08 -07:00
templates feat(spec): add @tag syntax for version qualifiers, parse_spec() helper 2026-07-03 01:49:22 -07:00
tests test(relink): use readfile instead of exists for symlink assertions 2026-07-12 22:54:45 -07:00
.busted fix: add shared library search path to test configuration 2026-07-12 03:20:03 -07:00
.gitignore refactor(fs): split into native C module + pure-Lua fallback 2026-07-12 16:42:28 -07:00
.luacheckrc temp: test pre-commit hooks 2026-07-12 03:16:00 -07:00
.luacov feat(coverage): integrate luacov for code coverage measurement 2026-07-10 00:50:35 -07:00
.pre-commit-config.yaml ci: change pre-commit hooks from SSH to HTTPS 2026-07-03 22:40:10 -07:00
DESIGN.md feat(project): define project directory structure 2026-07-01 20:57:07 -07:00
golua.md docs(luapp): resolve #130 — allow safe built-in functions, drop formatting preservation 2026-07-03 14:36:25 -07:00
Makefile refactor(fs): split into native C module + pure-Lua fallback 2026-07-12 16:42:28 -07:00
packages.md feat(pkgdefs): add dialog, dinit, mkinitfs, runit, openrc 2026-07-03 04:48:48 -07:00
pkglist.txt docs(luapp): resolve #130 — allow safe built-in functions, drop formatting preservation 2026-07-03 14:36:25 -07:00
README.md feat(cli): rename commands to add/del/use/drop, dynamic help formatting 2026-07-12 17:03:43 -07:00
selene.toml test(busted): convert test harness to busted describe/it blocks 2026-07-09 23:03:30 +00:00
stylua.toml feat(hash): implement sha256 verification and retry logic 2026-07-01 21:12:36 -07:00

qpkg

qpkg is a source-based package manager that operates within a user's home directory. It downloads, builds, and installs software from source into hash-based install directories under ~/.thwap.d/pkgs/<os>/<arch>/, then symlinks the installed files into a unified root at .../root/ using lndir-style recursive directory mirroring.

Features

  • Source-based — builds everything from source with SHA256 verification
  • Isolated builds — each package builds in a temp directory, installed into a hash-based install directory keyed by os-arch-name-version
  • Unified root — installed files are symlinked into .../root/ via recursive lndir (directories created, files symlinked, subdirs handled)
  • Dependency resolutionpkgdef_dependencies with recursive install and automatic activation of dependencies so builds can find them via $rootdir/bin/, $rootdir/lib/, etc.
  • Binary packages-b flag creates/consumes .qpkg archives from ~/.thwap.d/qpkg/binpkgs/ for fast reinstall
  • Phase/tagged buildspkg@tag syntax for multi-phase builds (e.g. gcc-15.2.0@phase1, gcc-15.2.0@phase2) with $tag substitution in build commands
  • Meta packagessrcuri = "meta" for dependency-only umbrella packages
  • pkgdef_output — prints KEY=VALUE pairs to stdout for shell eval: eval "$(qpkg install go-1.26.4)"
  • Platform detection$platform, $arch, $rootdir substitution variables available in all build commands
  • Logging — all build output goes to ~/.thwap.d/qpkg/logs/; only status messages and errors go to stderr. --verbose for full output.
  • Registry — tracks installed packages in ~/.thwap.d/qpkg/packages.lua with active version management and activate/deactivate commands
  • Additive relinkrelink scans install directories and creates missing symlinks without touching existing ones. Safe for PROMPT_COMMAND: new files (e.g. pip-installed scripts) appear in .../root/bin/ instantly without a full deactivate/activate cycle

Installation

git clone https://git.lan.thwap.org/thwap/qpkg.git
cd qpkg
mkdir -p ~/.thwap.d/qpkg
ln -s "$PWD/bin/qpkg" ~/.thwap.d/qpkg/bin/qpkg

export PATH="$HOME/.thwap.d/qpkg/bin:$PATH"

Commands

Command Description
qpkg help Show help message
qpkg version Show version
qpkg add <pkg>[-<ver>][@tag] Build and install a package
qpkg del <pkg> Remove an installed package
qpkg search [query] Search available pkgdefs
qpkg show <pkg>[-<ver>][@tag] Show pkgdef details and status
qpkg profile [-a] Show active packages (all versions with -a)
qpkg info <pkg>[-<ver>] Show detailed package information
qpkg init Print shell integration snippet
qpkg use <pkg>[-<ver>][@tag] Activate a version (symlink into root)
qpkg drop <pkg>[-<ver>] Deactivate a version (remove symlinks)
qpkg relink [<pkg>[-<ver>]] Relink missing symlinks for active package(s)

Options

  • -r <path> — override pkgdef repository root
  • --verbose, -v — show full build output on stderr
  • -b — use binary packages (create on build, consume on install)

Usage

# Install a package
qpkg add lua-5.4.8
qpkg add -b gcc-15.2.0@phase1

# List/search
qpkg profile
qpkg search lua
qpkg show lua-5.4.8

# Activation management
qpkg profile
qpkg use gcc-15.2.0@phase2
qpkg drop python-3.13.14

# Relink (additive — skips already-present symlinks)
qpkg relink                  # relink all active packages
qpkg relink python           # relink only python's active version

# Fast enough for $PROMPT_COMMAND usage (pure C, no shell forks):
#   export PROMPT_COMMAND="$(qpkg relink)"

For package definition format and pkgdef development, see pkgdefs/README.md.

Directory Structure

~/.thwap.d/qpkg/
  qpkg.conf           -- user configuration (Lua)
  packages.lua        -- installed package registry
  binpkgs/            -- binary package archives (*.qpkg)
  logs/               -- build logs
  tmp/                -- temporary build directories
  pkgdefs/            -- package definition files
  pkgs/
    linux/
      x86_64/
        <hash>/       -- install dir for one package version
          bin/        -- compiled binaries
          lib/        -- shared libraries
          include/    -- header files
          .qpkg-meta  -- package metadata
        root/         -- unified symlink root
          bin/        -- symlinks to installed binaries
          lib/        -- symlinks to libraries
          include/    -- symlinks to headers

Configuration

qpkg reads ~/.thwap.d/qpkg/qpkg.conf as a Lua file. Defaults:

qpkg_install_root = os.getenv("HOME") .. "/.thwap.d/pkgs"
qpkg_pkgdef_root  = os.getenv("HOME") .. "/.thwap.d/qpkg/pkgdefs"

Development

lua bin/qpkg help          # run from checkout
lua tests/test.lua         # run tests
pre-commit run --all       # lint + format