Skip to content

Latest commit

 

History

24 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

0k-pkg

Overview

0k-pkg is a shell-based package management and release automation toolkit. It provides a unified interface for building, testing, and publishing packages across multiple ecosystems (Rust/Cargo, Python/Hatch, TypeScript, JavaScript/npm, and shell packages).

The toolkit auto-detects the package type from project files and routes commands to the appropriate handler, enabling consistent workflows regardless of the underlying technology.

Features

  • Automatic package type detection based on project files
  • Unified command interface across different package ecosystems
  • Safe testing using unionfs-fuse overlays (source remains unmodified)
  • Multiple source selection modes: committed, workdir, staged, intermediate commits
  • Caching of test results and build artifacts
  • Coverage report aggregation
  • Integration with dovis for cross-platform testing
  • Extensible architecture for adding new package types

Supported Package Types

TypeDetectionBuild / publishExtra checks and scripts
allalways(drives the pipeline)license, debugger, merge markers, YYY, vendored scripts; git hooks
autogenautogen.sh-package-name check; 90-prepare.sh
cargoCargo.tomlcrate → crates.iodocs.rs check; clippy/fmt/doc/org lint; 20-org-to-md.sh
hatchpyproject.toml with hatchling + hatch-vcswheel, sdist → PyPIpdb/rst checks; 25-changelog.sh
pythonsetup.pywheel, sdist → PyPIpdb/rst checks; flake8 lint; 25-changelog.sh
npmpackage.jsonnpm pack → npmtranspile check
tstsconfig.jsonnpm pack → npmtranspile check
kal-shpkg/ + test/testdeb-
charmmetadata.yml--

The pkg, lint, vendor and pkg-git-hook verbs are implemented once, by all; the per-type directories only contribute hook scripts. t (tests) is implemented per type.

Installation

Ensure dependencies are installed:

# Core dependency
apt install unionfs-fuse

# For YAML config parsing (Rust implementation, not the legacy PyPI one)
cargo install shyaml-rs

# Per-language dependencies
cargo install toml-cli   # For Cargo/Hatch packages
apt install jq           # For npm/Hatch packages

Install 0k-pkg itself. Nothing is copied: the clone is the installation, and everything else is a symlink into it, so a git pull is an upgrade.

# bash-shlib must be available (provides /etc/shlib)
# dovis must be installed for testing

git clone https://github.com/0k/0k-pkg.git ~/dev/sh/0k-pkg   # or wherever you keep sources

Point pkgcmd at the plugin tree. pkgcmd loads its package types from \~/.pkgcmd.d/; without this symlink no command is found:

ln -s ~/dev/sh/0k-pkg/src/etc/pkgcmd.d ~/.pkgcmd.d

Expose the commands on your PATH. pkgcmd dispatches on the name it was invoked by (${0##*/}), so each verb is a symlink named after it, placed in a directory that is already on PATH:

ln -s ~/dev/sh/0k-pkg/src/bin/pkgcmd ~/.local/bin/pkgcmd
for verb in pkg t lint pkg-git-hook; do
    ln -s pkgcmd ~/.local/bin/$verb
done

(vendor is reached as pkg vendor.)

Usage

Main Commands

pkg (alias for pkg-common)

The primary release workflow command. Runs sanity checks, tests, builds distribution files, validates them, and optionally publishes.

# Run full release workflow on committed code (default)
pkg

# Run on working directory state
pkg -w

# Run on staged changes only
pkg --staged

# Test all intermediate commits since last push
pkg -i

# Publish after successful validation
pkg --publish

# Skip tests (for quick iteration)
pkg --no-tests

# Skip distribution building
pkg --no-dist

pkg vendor - Ship build scripts with the project

A fresh git clone of a project must be buildable without 0k-pkg installed: only git, sed, date and grep should be needed to run ./autogen.sh and then the language’s own build tool. The autotools model is followed: 0k-pkg plays the role of autoreconf (private, developer-side), and autogen.sh plus .package.d/autogen.d/* play the role of configure (public, committed, self-sufficient).

pkg vendor           # copy canonical autogen.sh + plugin autogen.d/ scripts
pkg vendor --dry-run # report what would change

What gets copied depends on the package types that apply to the project (see each plugin’s autogen.d/ directory):

PluginScriptPurpose
autogen90-prepare.sh%%version%% substitution in $FILES
cargo20-org-to-md.shREADME.org → README.md (pandoc) + changelog appended, since crates.io only renders the README
hatch25-changelog.shCHANGELOG.rst from git history (gitchangelog)
python25-changelog.shidem

Provenance is written to .package.d/autogen.d/MANIFEST (source commit and a sha256 per file). Commit the vendored files together with the manifest.

To keep a local modification of a vendored script, add the line ## pkgcmd: local-override within its first five lines; pkg vendor then leaves it alone. Scripts that no longer exist upstream are removed on the next pkg vendor unless modified or overridden.

At release time all/source/check/vendored verifies that every vendored file matches its manifest entry (error on drift without the override marker, error on a project script silently shadowing a plugin one), and warns when 0k-pkg now ships a newer copy: run pkg vendor, re-test, commit.

Optional tools used by vendored scripts (pandoc, gitchangelog) are checked with depends_soft: missing, the step is skipped with a warning so that ./autogen.sh still succeeds for someone who only wants to build. pkg exports AUTOGEN_STRICT=1 for the whole release pipeline, which turns those into hard errors.

t - Run Tests

Runs the test suite for the detected package type.

# Run all tests
t

# Pass arguments to the test runner
t -v   # verbose (if supported by test runner)

lint - Linting

Runs linting checks appropriate for the package type.

lint

pkg-git-hook - Global git hooks

Runs the git hook check scripts for a given hook name. This is the entry point used by the global core.hooksPath trampolines (see the git charm in 0k-cfg): each trampoline is a two-line script that exec~s ~pkg-git-hook <HOOK> "$@".

# Called by git through ~/.config/git/hooks/pre-commit
pkg-git-hook pre-commit

# Called by git through ~/.config/git/hooks/commit-msg
pkg-git-hook commit-msg .git/COMMIT_EDITMSG

For a hook HOOK, every executable in git-hook/HOOK/ of each applying package type is run in sorted order with the arguments git passed to the hook. The first non-zero exit aborts and becomes the hook’s exit code. On success the repository-local .git/hooks/HOOK is exec‘d if present, so per-repository hooks keep working underneath the global ones.

The command is silent on success, as git hooks are expected to be.

It is not named git-hook: git prepends its --exec-path to PATH when running hooks, and git-core/git-hook (the git hook builtin) would shadow it.

Checks shipped for the all package type:

HookScriptPurpose
pre-commit10-readme-mdBlock README.md when README.org exists
pre-commit20-opencode-pathsOpenCode sessions: block doc/admin.org, .sisyphus/, *.md
pre-commit30-org-clockorg-cli clock check on staged *.org
commit-msg10-opencode-msgOpenCode sessions: block Co-authored-by / “Ultraworked”
commit-msg20-conventionCommit message convention audit
commit-msg90-assisted-byAdd Assisted-by: trailer from ai-audit

The *-opencode-* hooks only act when the $OPENCODE environment variable is 1 (set by the OpenCode agent harness) and are inert otherwise. 30-org-clock needs org-cli and 90-assisted-by needs ai-audit; both hooks skip silently when the tool is absent.

Source Selection

The pkg command supports multiple source selection modes:

OptionDescription
-c REVUse specific commit (default: HEAD)
-wUse current working directory state
--stagedUse only staged changes
-iTest each commit since last push
--since REVTest each commit since specified revision

Workflow Options

OptionDescription
--no-testsSkip test execution
--no-distSkip distribution file creation
--no-dist-checkSkip distribution file validation
--no-sanity-checkSkip pre-release sanity checks
--no-coverDisable coverage collection
--no-cachePrevent caching of results
--publishPublish to package registry after checks
-sOpen interactive shell in test environment

Configuration

User Configuration

Global configuration is stored in:

  • \~/.config/pkg/main.yml - Main configuration file (YAML format)
  • \~/.pkgcmd.d/ - Package types and their hook scripts. Normally a symlink to the clone’s src/etc/pkgcmd.d (see Installation); a real directory holding your own <type>/cmd.sh works too.

Project Configuration

Projects can provide local overrides in:

  • .pkgcmd.d/ - Project-specific command handlers
  • .package - Legacy package configuration (shell script, sourced)

How It Works

Package Detection

Package types are not exclusive: several apply to one project at the same time and their contributions stack. On a Rust project with an autogen.sh, all, autogen and cargo all apply.

When a command is invoked, pkgcmd walks up the directory tree until at least one package type’s applies function returns true, then:

  1. Verb dispatch. Among the applying types that implement pkgcmd:<verb>:<type>:run, the first one (alphabetical order of the function table) runs the verb. all implements pkg, lint, pkg-git-hook and vendor; cargo implements t; and so on. In practice each verb has one implementation and the choice is unambiguous.
  2. Hook scripts. The verb implementation exports $PKGCMD_ALTERNATIVES, the list of every applying type, and pkg._list_scripts <hook> unions the project’s .package.d/<hook>/* with <type>/<hook>/* for each of those types. So the release checks, lint scripts, git hooks and autogen.d/ scripts from all, autogen and cargo all run, sorted by file name. all is therefore not a fallback but the layer shared by every project.

Handlers are loaded from \~/.pkgcmd.d/*.sh and \~/.pkgcmd.d/*/cmd.sh, then from any .pkgcmd.d/ found in the current directory or its parents.

Release Workflow (pkg)

  1. Source Preparation: Clone/copy source to temporary directory based on selected source mode
  2. Sanity Checks: Run scripts from source/check/ (license, debugger statements, merge markers, vendored scripts in sync)
  3. Tests: Execute via dovis across configured platforms
  4. Coverage: Aggregate coverage reports if enabled
  5. Dist Checks: Run scripts from dist/check/
  6. Build: Execute scripts from release/build/ (wheel, sdist, npm pack, etc.)
  7. Release Checks: Validate built artifacts via release/check/
  8. Publish: If --publish, execute scripts from release/publish/

Safe Testing with Overlays

pkg uses unionfs-fuse to create copy-on-write overlays. Tests run in an isolated environment where:

  • The original source remains untouched
  • Modifications are written to a temporary upper layer
  • Each test run starts from a clean state

Build artifacts are placed in /tmp by default.

Testing

bin/test            # runs the sunit suite in test/
bin/test vendor     # one file

Dependencies

Required

  • bash-shlib - Shell library framework
  • unionfs-fuse - Filesystem overlay support
  • dovis - Cross-platform test runner
  • shyaml - YAML query tool, from the shyaml-rs crate (cargo install shyaml-rs)

Per-Language

LanguageDependencies
Cargo/Rusttoml-cli, pandoc, gitchangelog (release only)
Hatchtoml-cli, jq, hatch, docutils (rst check), gitchangelog
Pythondocutils (rst check), flake8 (lint), gitchangelog, nosetests (if configured)
npm/TSjq, npm

Extending

To add support for a new package type, create a handler in \~/.pkgcmd.d/<type>/cmd.sh or \~/.pkgcmd.d/<type>.sh:

# Detection function - return 0 if this type applies
pkgcmd:<type>:applies() {
    [ -e "marker-file" ]
}

# Optional: mark as base type for inheritance
pkgcmd:<type>:is_base() {
    [ -e "marker-file" ]
}

# Command implementations
pkgcmd:t:<type>:run() {
    # Run tests
    your-test-command "$@"
}

pkgcmd:pkg-name:<type>:run() {
    # Output package name
    echo "my-package"
}

pkgcmd:pkg-version:<type>:run() {
    # Output package version
    echo "1.0.0"
}

For release workflow integration, create executable scripts in:

  • <type>/source/check/ - Pre-test sanity checks
  • <type>/dist/check/ - Distribution validation
  • <type>/release/build/ - Build distribution files
  • <type>/release/check/ - Post-build validation
  • <type>/release/publish/ - Publishing commands
  • <type>/git-hook/<hook>/ - Git hook checks run by pkg-git-hook
  • <type>/autogen.d/ - Scripts copied into projects by pkg vendor and sourced by ./autogen.sh (not executed: no shebang, not executable; return 1 aborts the step). Use depends_soft for optional tools.

License

See LICENSE file for details.

About

Per-technology packaging, test and release commands (pkg, lint, t) with vendored self-sufficient build scripts

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages