basher
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:
--versionprints the installed basher version.-v,--verboseprints the resolved project and artifact directories for mutating project commands.-q,--quietsuppresses non-error output.--color {auto,always,never}controls rich styling.autofollows terminal detection, andNO_COLORselectsneverunless 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 (defaulttools).-l DIR,--lib-dir DIR: library destination relative to the project (defaultlib).--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 withproject,stale, andartifactsfields instead of the rich table. Each artifact reportsartifact,kind,source,vendored_date,vendored_version,latest,state, andlegacy.
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:
- Built-in defaults:
tools_dir = "tools",lib_dir = "lib", andcolor = "auto". - User config at
${XDG_CONFIG_HOME:-~/.config}/basher/config.toml. [tool.basher]in the projectpyproject.toml, then.basher.tomlin the project root.BASHER_TOOLS_DIR,BASHER_LIB_DIR, andBASHER_COLOR;NO_COLORacts asBASHER_COLOR=neverwhen that variable is unset.- 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:statusfound 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, andlog::erroraccept printf-style arguments and-u N/--up Nfor caller traversal. Messages include caller metadata, go to standard error, and are sent to syslog withlogger.log::debughonorsDEBUG=trueorVERBOSE>0;DISABLE_LOG_COLOR=truedisables log coloring.pyprintf FORMAT [ARGS...]applies Pythonstr.formatsyntax without adding a newline.urlencode STRING [SAFE_CHARS]percent-encodes a string while preserving the optional safe characters.usageprints one usage line per entry in the caller-providedUSAGE_GRAMMARarray.
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
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6220d6b83385eb2c9a6c7ac19c400f4a90d6b1f4b0eda6f5481a7226bab98dc9
|
|
| MD5 |
0b628ca6bfa7fb93e7725d688ca6fd50
|
|
| BLAKE2b-256 |
1e7657ece06170c39a109b038e1e917859b5d88892808ddb258cfc85a95650da
|
Provenance
The following attestation bundles were made for basher-0.2.0.tar.gz:
Publisher:
publish.yml on bbugyi200/basher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
basher-0.2.0.tar.gz -
Subject digest:
6220d6b83385eb2c9a6c7ac19c400f4a90d6b1f4b0eda6f5481a7226bab98dc9 - Sigstore transparency entry: 2162815827
- Sigstore integration time:
-
Permalink:
bbugyi200/basher@ab1a14acf8389440e6814496647ab49316e1a17d -
Branch / Tag:
refs/heads/master - Owner: https://github.com/bbugyi200
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ab1a14acf8389440e6814496647ab49316e1a17d -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d47ee5f8438a6f6c3a696d12f3bb99c104c40adcc23f417e1e0e90d43e67585b
|
|
| MD5 |
ae2191e312757e934378eb46df5cf6c3
|
|
| BLAKE2b-256 |
fe8594ca100b304589e92101f1f94535801add3799c0e6c6d61848062b3aed68
|
Provenance
The following attestation bundles were made for basher-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on bbugyi200/basher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
basher-0.2.0-py3-none-any.whl -
Subject digest:
d47ee5f8438a6f6c3a696d12f3bb99c104c40adcc23f417e1e0e90d43e67585b - Sigstore transparency entry: 2162815904
- Sigstore integration time:
-
Permalink:
bbugyi200/basher@ab1a14acf8389440e6814496647ab49316e1a17d -
Branch / Tag:
refs/heads/master - Owner: https://github.com/bbugyi200
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ab1a14acf8389440e6814496647ab49316e1a17d -
Trigger Event:
push
-
Statement type: