Skip to main content

basher

CI PyPI Python versions License: MIT

basher vendors shell scripts and the bugyi.sh Bash library into projects. It keeps provenance beside every artifact, replaces stale copies safely, updates references without regular-expression surprises, and makes freshness visible to both humans and CI.

The original pyvendor script used date-stamped filenames for everything and depended on a dotfiles checkout. basher packages the library, gives library copies meaningful version suffixes, retains compatible handling for legacy artifacts, and adds dry runs, layered configuration, status reporting, and rich terminal output.

Install

Python 3.11 or newer is required. Installing as a standalone tool keeps basher's dependencies out of your project:

uv tool install basher
basher --version

Upgrade later with uv tool upgrade basher.

Quick start

Vendor a script into a project:

basher vendor ~/bin/my-script /path/to/project

If the script contains the exact line source ~/lib/bugyi.sh, basher also vendors its packaged library and rewrites the source line to a path relative to the vendored script. A script is executable after vendoring, even when its source mode was not.

When PROJECT is omitted, basher uses the enclosing Git repository root, or the current directory when outside a Git repository:

cd /path/to/project
basher status
basher update --dry-run
basher update

To use the packaged library without vendoring it into a project:

source "$(basher path)"
# or materialize the conventional per-user copy:
basher export ~/lib

Commands

Global options must appear before the command:

  • --version prints the installed basher version.
  • -v, --verbose prints the resolved project and artifact directories for mutating project commands.
  • -q, --quiet suppresses non-error output.
  • --color {auto,always,never} controls rich styling. auto follows terminal detection, and NO_COLOR selects never unless a higher-precedence setting overrides it.

basher vendor SCRIPT [PROJECT]

Copies SCRIPT to PROJECT/tools/<name>-<YYMMDD>, removes stale copies, and rewrites literal references to removed filenames throughout the project. Files inside CHEZMOI_SOURCE_ROOT (default ~/.local/share/chezmoi) lose a leading executable_ filename prefix. When the script sources ~/lib/bugyi.sh, the library is bundled unless --no-lib is set.

  • -t DIR, --tools-dir DIR: script destination relative to the project (default tools).
  • -l DIR, --lib-dir DIR: library destination relative to the project (default lib).
  • --no-lib: do not bundle or rewrite the library source line.
  • --suffix TEXT: override the script date suffix and, when bundled, the library version suffix.
  • -n, --dry-run: show copies, removals, reference rewrites, and unified diffs without writing anything.
  • --force: allow removal or replacement of files that do not carry a recognized provenance line.

basher lib [PROJECT]

Vendors or refreshes only the packaged library. The destination is PROJECT/lib/bugyi-<BASHER_VERSION>.sh. It removes legacy date-stamped and old version-stamped copies and rewrites their literal filename references.

  • -l DIR, --lib-dir DIR: library destination relative to the project.
  • --suffix TEXT: override the package-version suffix.
  • -n, --dry-run: preview the complete operation without writes.
  • --force: permit replacing an unrecognized existing copy.

basher update [PROJECT]

Finds recognized vendored artifacts and refreshes stale ones. Modern script provenance contains the source path, so the script can be re-vendored. A legacy library is refreshed from the packaged copy; a legacy script has no recoverable source path and is skipped with a warning.

  • -t DIR, --tools-dir DIR: directory to scan for scripts.
  • -l DIR, --lib-dir DIR: directory to scan for the library.
  • -n, --dry-run: preview the refresh and diffs without writes.
  • --force: permit replacement of artifacts without recognized provenance.

An already-current project is a successful no-op.

basher status [PROJECT]

Shows each recognized artifact's kind, vendored version or date, latest value, and state. It exits with status 3 if any artifact is stale, legacy, or has a missing source.

  • -t DIR, --tools-dir DIR: directory to scan for scripts.
  • -l DIR, --lib-dir DIR: directory to scan for the library.
  • --json: emit a stable object with project, stale, and artifacts fields instead of the rich table. Each artifact reports artifact, kind, source, vendored_date, vendored_version, latest, state, and legacy.

basher cat

Prints the raw packaged bugyi.sh to standard output without adding a provenance line.

basher path

Prints the filesystem path of the packaged bugyi.sh, suitable for source "$(basher path)".

basher export [DESTINATION]

Writes an executable, unversioned bugyi.sh with basher provenance into DESTINATION (default ~/lib). Existing identical content is left untouched.

Configuration

Basher resolves its three settings from lowest to highest precedence:

  1. Built-in defaults: tools_dir = "tools", lib_dir = "lib", and color = "auto".
  2. User config at ${XDG_CONFIG_HOME:-~/.config}/basher/config.toml.
  3. [tool.basher] in the project pyproject.toml, then .basher.toml in the project root.
  4. BASHER_TOOLS_DIR, BASHER_LIB_DIR, and BASHER_COLOR; NO_COLOR acts as BASHER_COLOR=never when that variable is unset.
  5. Command-line flags.

User and .basher.toml files use top-level keys:

tools_dir = "vendor/tools"
lib_dir = "vendor/lib"
color = "auto"

The project pyproject.toml uses a table:

[tool.basher]
tools_dir = "vendor/tools"
lib_dir = "vendor/lib"
color = "never"

Directory values must be non-empty relative paths without ... Unknown keys, invalid values, and malformed TOML are errors rather than silent fallbacks.

Filenames and compatibility

Vendored scripts use a -YYMMDD suffix because scripts do not carry their own versions. Vendored library copies use the basher package version, such as bugyi-0.2.0.sh. --suffix provides an escape hatch for either scheme.

Cleanup recognizes the old bugyi-YYMMDD.sh/bugyi_*.sh names and current versioned names. Project-wide reference updates use literal string replacement, skip binary files and .git, and report every rewritten file.

Basher writes a machine-readable comment immediately after the shebang, or on line one when no shebang exists:

# Vendored by basher v<VERSION> from <SOURCE> on <YYYY-MM-DD>. Run 'basher update' to refresh.

Script sources are absolute paths with the home directory abbreviated to ~. The library source is https://github.com/bbugyi200/basher.

The legacy pyvendor comment is also recognized:

# Vendored from https://github.com/bbugyi200/dotfiles via pyvendor on <YYYY-MM-DD>

Legacy library copies can be migrated automatically. Legacy script comments do not contain an original path, so status marks those scripts as legacy and update asks you to re-vendor them manually.

Exit codes

  • 0: success, including an already-current no-op.
  • 1: runtime, filesystem, or configuration error.
  • 2: command-line usage error from argument parsing.
  • 3: status found at least one non-current artifact.

bugyi.sh reference

The library has a double-source guard, exports TZ=America/New_York, and exposes the following public helpers:

  • die [-x N|--exit-code N] MESSAGE [FORMAT_ARGS...] logs an error and exits; the default code is 1. Code 2 adds command-line parsing guidance.
  • log::debug, log::info, log::warn, and log::error accept printf-style arguments and -u N/--up N for caller traversal. Messages include caller metadata, go to standard error, and are sent to syslog with logger. log::debug honors DEBUG=true or VERBOSE>0; DISABLE_LOG_COLOR=true disables log coloring.
  • pyprintf FORMAT [ARGS...] applies Python str.format syntax without adding a newline.
  • urlencode STRING [SAFE_CHARS] percent-encodes a string while preserving the optional safe characters.
  • usage prints one usage line per entry in the caller-provided USAGE_GRAMMAR array.

Sourcing the library defines BUGYI_VERSION, BUGYI_HAS_BEEN_SOURCED, SCRIPTNAME, MY_SHELL, COLOR_GREEN, COLOR_PURPLE, COLOR_RED, COLOR_YELLOW, COLOR_RESET, XDG_RUNTIME, XDG_CONFIG, XDG_DATA, and the script-scoped MY_XDG_RUNTIME, MY_XDG_CONFIG, and MY_XDG_DATA paths. pyprintf and urlencode require python3; logging uses standard Unix tools including logger, perl, and tee.

Development

The project uses uv and just:

just install       # create .venv and install the package with dev tools
just fmt           # format and apply safe lint fixes
just fmt-check     # verify formatting
just lint          # ruff, strict mypy, symvision, toobig, and shellcheck
just test          # pytest with branch coverage and the 95% gate
just check         # all formatting, lint, shell, and test gates

Set BASHER_PYTHON to select the interpreter used for the virtual environment, for example BASHER_PYTHON=3.14 just check. CI checks Python 3.11 through 3.14.

Releases are managed by release-please. Conventional commit and pull-request titles determine version bumps, and published wheels are installed and exercised in a fresh environment before trusted PyPI publishing.

License

basher is released under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

basher-0.2.0.tar.gz (27.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

basher-0.2.0-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file basher-0.2.0.tar.gz.

File metadata

  • Download URL: basher-0.2.0.tar.gz
  • Upload date:
  • Size: 27.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for basher-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6220d6b83385eb2c9a6c7ac19c400f4a90d6b1f4b0eda6f5481a7226bab98dc9
MD5 0b628ca6bfa7fb93e7725d688ca6fd50
BLAKE2b-256 1e7657ece06170c39a109b038e1e917859b5d88892808ddb258cfc85a95650da

See more details on using hashes here.

Provenance

The following attestation bundles were made for basher-0.2.0.tar.gz:

Publisher: publish.yml on bbugyi200/basher

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file basher-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: basher-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for basher-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d47ee5f8438a6f6c3a696d12f3bb99c104c40adcc23f417e1e0e90d43e67585b
MD5 ae2191e312757e934378eb46df5cf6c3
BLAKE2b-256 fe8594ca100b304589e92101f1f94535801add3799c0e6c6d61848062b3aed68

See more details on using hashes here.

Provenance

The following attestation bundles were made for basher-0.2.0-py3-none-any.whl:

Publisher: publish.yml on bbugyi200/basher

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page