bash.d/README.md
Ole-Morten Duesund 6a00847d4e Speed up shell startup 9x (1.9s → 0.2s)
Lazy-load Intel oneAPI setvars.sh (~1.5s) via wrapper functions that
source the environment on first use of icc/icx/ifort/etc.

Cache all shell completion outputs to ~/.cache/bash.d/ so they are
sourced from disk instead of regenerated via subprocess on every
shell start.  Cache invalidates automatically when the tool binary
is updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:27:12 +01:00

128 lines
6 KiB
Markdown

# ~/.bash.d — Modular Bash Configuration
This directory contains modular shell configuration files that are automatically sourced by `~/.bashrc` on every new shell session. Instead of maintaining one monolithic `.bashrc`, each concern lives in its own file — making the setup easier to understand, maintain, and extend.
## How It Works
Add the following to the end of your `~/.bashrc`:
```bash
for file in $HOME/.bash.d/*; do
[[ -x $file ]] && source $file
done
```
This iterates over all files in `~/.bash.d/` and sources each one that has the **executable bit** set. Non-executable files are silently skipped, which provides a simple way to temporarily disable a configuration (just `chmod -x` the file).
Files are sourced in **lexicographic order**, so numeric prefixes control the load sequence. This matters because later files may depend on functions or variables defined by earlier ones.
## File Naming Convention
| Prefix | Purpose | Example |
|--------|----------------------------------|---------------------------|
| `00-` | Helper functions (loaded first) | `00-path-helper` |
| `10-` | PATH configuration | `10-go-path`, `10-rust-path` |
| `20-` | Build tool settings | `20-ninja` |
| `30-` | Shell/prompt setup | `30-starship` |
| `50-` | Shell completions | `50-claude-completion` |
| `99-` | Credentials (last) | `99-gemini`, `99-huggingface` |
Lower numbers load first, so foundational pieces like helper functions (`00-`) and PATH entries (`10-`) are available before anything that depends on them.
## Current Files
### Helpers (`00-`)
- **`00-path-helper`** — Defines `path_append` and `path_prepend` functions that safely add directories to `$PATH` (checking the directory exists and avoiding duplicates).
- **`00-credential-guard`** — Defines `require_private`, which warns at shell startup if a credential file has overly permissive permissions (anything beyond owner-only access).
- **`00-completion-cache`** — Defines `cached_completion`, which caches shell completion scripts to `~/.cache/bash.d/` so they are only regenerated when the tool binary is updated. Used by all `50-*-completion` scripts.
### PATH (`10-`)
- **`10-bun-path`** — Adds Bun (JavaScript runtime) binaries to PATH.
- **`10-go-path`** — Sets `$GOPATH` and adds Go binaries to PATH.
- **`10-rust-path`** — Sets `$CARGO_HOME` and adds Cargo binaries to PATH.
### Build Tools (`20-`)
- **`20-android`** — Sets `$ANDROID_HOME`, `$JAVA_HOME`, and adds Android SDK tools to PATH (only when installed).
- **`20-ninja`** — Configures Ninja as the default CMake generator and enables ccache for C/C++ builds (only when installed).
- **`20-oneapi`** — Lazy-loads the Intel oneAPI environment. Because `setvars.sh` takes ~1.5 seconds, the full environment is deferred until an Intel tool (`icc`, `icx`, `ifort`, etc.) is first invoked. Call `_load_oneapi` to trigger it manually.
### Prompt (`30-`)
- **`30-starship`** — Initialises the [Starship](https://starship.rs) cross-shell prompt, if installed.
### Completions (`50-`)
All completions (except `50-claude-completion`) use `cached_completion` to avoid subprocess overhead on every shell start. Cache files live in `~/.cache/bash.d/` and are automatically regenerated when the tool binary is updated.
- **`50-asdf-completion`** — Tab completion for the asdf version manager.
- **`50-bun-completion`** — Tab completion for Bun (JavaScript runtime & package manager).
- **`50-claude-completion`** — Tab completion for [Claude Code](https://claude.com/claude-code) CLI.
- **`50-fj-completion`** — Tab completion for the Forgejo CLI (`fj`).
- **`50-gh-completion`** — Tab completion for the GitHub CLI (`gh`).
- **`50-podman-completion`** — Tab completion for Podman (rootless container engine).
- **`50-tailscale-completion`** — Tab completion for Tailscale.
- **`50-uv-completion`** — Tab completion for [uv](https://github.com/astral-sh/uv) (Python package manager).
### Credentials (`99-`)
- **`99-claude.example`** — Template for Forgejo issue token for Claude Code integrations.
- **`99-gemini.example`** — Template for Gemini API key.
- **`99-google.example`** — Template for Google API key.
- **`99-huggingface.example`** — Template for HuggingFace token (`$HF_TOKEN`).
- **`99-replicate.example`** — Template for Replicate API token.
## Adding a New File
1. Create the file with the appropriate numeric prefix:
```bash
vim ~/.bash.d/50-mytool-completion
```
2. Start the file with the shellcheck directive:
```bash
# shellcheck shell=bash
```
3. Make it executable (required for it to be sourced):
```bash
chmod +x ~/.bash.d/50-mytool-completion
```
4. For credential files, create a `.example` template and the real file:
```bash
# Create the template (tracked in git)
cat > ~/.bash.d/99-mytool.example << 'EOF'
# shellcheck shell=bash
# Copy to 99-mytool and fill in your token, then: chmod 700 99-mytool
require_private "${BASH_SOURCE[0]}"
export MY_SECRET_TOKEN=your-token-here
EOF
# Create the real file from the template
cp ~/.bash.d/99-mytool.example ~/.bash.d/99-mytool
# Edit 99-mytool and fill in your real secret
chmod 700 ~/.bash.d/99-mytool
```
Then add `99-mytool` to `.gitignore`.
5. Validate with shellcheck:
```bash
shellcheck ~/.bash.d/50-mytool-completion
```
## Security
Credential files (`99-*`) are **excluded from git** via `.gitignore` and are never committed. Each credential has a tracked `.example` template with placeholder values. To set up credentials:
1. Copy the template: `cp 99-foo.example 99-foo`
2. Fill in your real secret
3. Restrict permissions: `chmod 700 99-foo`
Additional protections:
- **File permissions** — Credential files use mode `700` (owner-only).
- **Runtime checks** — The `require_private` helper warns on shell startup if a credential file has been accidentally loosened.
- **Never commit real secrets** — Only `.example` templates are tracked in git.