Helper System - jvPalma/dotrun GitHub Wiki

Helper System

Table of Contents

Introduction

The loadHelpers system is DotRun's flexible, pattern-based helper loading mechanism introduced in v3.0.0 and enhanced in v3.1.0. It enables collection scripts to load shared helper modules without relying on hardcoded paths, solving the critical problem of dynamic namespace prefixes in imported collections.

What is the loadHelpers System?

The loadHelpers system provides:

  • Pattern-based loading: Load helpers using flexible patterns instead of exact paths
  • Collection independence: Scripts work regardless of namespace prefix changes
  • Dual-mode support: Works both via dr scriptname and direct bash script.sh execution
  • Smart matching: 5-level specificity hierarchy finds the right helper automatically
  • Security: Path traversal prevention, circular dependency detection, and de-duplication

Core Concepts

  1. Pattern Matching: Instead of exact paths, use patterns like gcp/workstation or @dotrun-anc
  2. Specificity Hierarchy: More specific patterns take precedence (absolute path > exact path > filename search)
  3. Collection Namespacing: Collections install to numbered directories (e.g., 01-dotrun-anc/) but scripts don't need to know the prefix
  4. Transparent Integration: Helper loading works seamlessly in both dr command execution and direct script execution

The Namespace Problem

The Challenge

Collection scripts traditionally used static imports:

source "$DR_CONFIG/helpers/gcp/workstation.sh"

But collections import to namespace directories with numeric prefixes:

~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

The numeric prefix (01-) controls load order and can be changed by users, breaking static import paths.

The Solution

The loadHelpers function provides flexible, pattern-based helper loading that works regardless of namespace prefixes:

loadHelpers gcp/workstation # Finds helper in ANY collection namespace

The system automatically searches across all collection directories, matching patterns intelligently while respecting a 5-level specificity hierarchy.

Core Location and Integration

File Location

The loadHelpers system is implemented in:

~/.local/share/dotrun/helpers/loadHelpers.sh

Integration Points

The loadHelpers function integrates at three levels:

1. Shell Integration

~/.drrc exports the DR_LOAD_HELPERS environment variable and sources the function for interactive shells:

# In ~/.drrc
export DR_LOAD_HELPERS="$HOME/.local/share/dotrun/helpers/loadHelpers.sh"
source "$DR_LOAD_HELPERS"

2. Script Execution

The dr binary exports DR_LOAD_HELPERS before running scripts, ensuring all executed scripts have access:

# In dr binary (simplified)
export DR_LOAD_HELPERS="$SHARE_DIR/helpers/loadHelpers.sh"
bash "$script_path" "$@"

3. Dual-Mode Support

Scripts work both via:

  • DotRun execution: dr myscript (environment pre-configured)
  • Direct execution: bash ~/.config/dotrun/scripts/myscript.sh (sources function via DR_LOAD_HELPERS)

Basic Usage

Standard Pattern

All collection scripts should follow this pattern:

#!/usr/bin/env bash
### DOC
# myscript - Example collection script
### DOC
set -euo pipefail

# Load the loadHelpers function (works in both dr and direct execution)
[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"

# Load required helpers using flexible patterns
loadHelpers gcp/workstation         # Load from any collection
loadHelpers dotrun-anc/utils/common # Specify collection name
loadHelpers @dotrun-anc             # Load all helpers from collection

main() {
  # Use functions from loaded helpers
  echo "Script using helpers..."
}

main "$@"

Key Components

  1. Function Sourcing: [[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"

    • Checks if environment variable is set
    • Sources the loadHelpers function definition
    • Safe for both execution modes
  2. Helper Loading: loadHelpers pattern

    • Use flexible patterns (see 5-Level Specificity)
    • Load at script top, before main logic
    • Multiple patterns for multiple helpers
  3. Helper Usage: Call functions defined in loaded helpers

    • Helpers export functions and variables
    • Available throughout script after loading

5-Level Specificity Matching

The loadHelpers system uses a 5-level specificity hierarchy to resolve patterns, from most specific to least specific. More specific patterns always take precedence.

Level 1: Absolute Path

Pattern: /full/path/to/helper.sh

Behavior: Exact file path, no search performed.

Use Case: Loading helpers outside DotRun's helper directory (advanced use).

Example:

loadHelpers /opt/custom/helpers/special.sh

Result: Loads the exact file /opt/custom/helpers/special.sh if it exists.


Level 2: Exact Path

Pattern: helpers/01-dotrun-anc/gcp/workstation.sh

Behavior: Exact relative path from $DR_CONFIG (usually ~/.config/dotrun).

Use Case: When you know the exact namespace prefix and want guaranteed match.

Example:

loadHelpers helpers/01-dotrun-anc/gcp/workstation.sh

Result: Loads ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh exactly.


Level 3: With Extension

Pattern: 01-dotrun-anc/gcp/workstation

Behavior: Auto-adds .sh extension and searches from $DR_CONFIG/helpers.

Use Case: Slightly cleaner syntax than Level 2 while maintaining specificity.

Example:

loadHelpers 01-dotrun-anc/gcp/workstation

Result: Same as Level 2, but you omit the .sh extension.


Level 4: Path Search (Most Common)

Pattern: gcp/workstation or dotrun-anc/gcp/workstation

Behavior: Searches all collections, with collection name normalization.

Collection Name Normalization:

  • Pattern dotrun-anc matches *-dotrun-anc directories
  • Example: 01-dotrun-anc, 02-dotrun-anc, etc.

Search Patterns:

  1. */[0-9]*-collection/path.sh (numbered namespace directories)
  2. */collection/path.sh (non-numbered directories)

Use Case: Standard usage in collection scripts (namespace-agnostic).

Examples:

# Path without collection name - searches all collections
loadHelpers gcp/workstation

# Path with collection name - searches specific collection(s)
loadHelpers dotrun-anc/gcp/workstation

Result: Finds ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh regardless of numeric prefix.


Level 5: Filename Only

Pattern: workstation

Behavior: Least specific - searches all helpers recursively by filename.

Use Case: Quick loading when filename is unique across all collections.

Warning: May load multiple files with the same name from different collections.

Example:

loadHelpers workstation

Result: Searches entire ~/.config/dotrun/helpers/ tree for workstation.sh, may find multiple matches.


Specificity Hierarchy Summary

Level Pattern Example Specificity Guaranteed Unique?
1 /opt/custom/helpers/special.sh Absolute path ✅ Yes
2 helpers/01-dotrun-anc/gcp/workstation.sh Exact relative ✅ Yes
3 01-dotrun-anc/gcp/workstation Exact + auto .sh ✅ Yes
4 dotrun-anc/gcp/workstation Path search ⚠️ Usually
4 gcp/workstation Path search ⚠️ Usually
5 workstation Filename only ❌ No

Recommendation: Use Level 4 (path search with collection name) for collection scripts - it balances flexibility and specificity.

Pattern Examples

For a helper located at:

~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

All of these patterns will successfully load the same helper:

# Level 2: Exact path with namespace
loadHelpers helpers/01-dotrun-anc/gcp/workstation.sh

# Level 3: Exact without .sh extension
loadHelpers 01-dotrun-anc/gcp/workstation

# Level 4: Collection name normalized (preferred)
loadHelpers dotrun-anc/gcp/workstation

# Level 4: Path in any collection
loadHelpers gcp/workstation

# Level 5: Filename only (least specific)
loadHelpers workstation

Multiple Helpers Example

To load multiple helpers in a script:

#!/usr/bin/env bash
set -euo pipefail

[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"

# Load helpers using different specificity levels
loadHelpers dotrun-anc/gcp/workstation # Level 4: Collection + path
loadHelpers utils/logging              # Level 4: Path in any collection
loadHelpers @dotrun-anc                # Special: All helpers from collection

main() {
  # Functions from all loaded helpers now available
  setup_gcp_workstation
  log_info "Starting operation..."
}

main "$@"

Special Features

Collection Scope Loading

Load all helpers from a specific collection using the @collection syntax:

loadHelpers @dotrun-anc

Behavior:

  • Finds collection directory matching *-dotrun-anc
  • Recursively loads all .sh files in that directory
  • Useful for collections with many interdependent helpers

Example Output:

Loading all helpers from collection: dotrun-anc
  ✓ Loaded: ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh
  ✓ Loaded: ~/.config/dotrun/helpers/01-dotrun-anc/utils/logging.sh
  ✓ Loaded: ~/.config/dotrun/helpers/01-dotrun-anc/utils/validation.sh
Loaded 3 helper(s) from collection dotrun-anc

List Mode

Preview matches without actually loading helpers using the --list flag:

loadHelpers gcp/workstation --list

Example Output:

Found 1 helper(s) matching 'gcp/workstation' (level: path):
  /home/user/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

Use Cases:

  • Verify pattern matches expected helper
  • Debug pattern specificity issues
  • Discover available helpers during development
  • Preview collection scope loading

List Mode with Collection Scope:

loadHelpers @dotrun-anc --list

# Output:
# Found 3 helper(s) in collection 'dotrun-anc':
#   /home/user/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh
#   /home/user/.config/dotrun/helpers/01-dotrun-anc/utils/logging.sh
#   /home/user/.config/dotrun/helpers/01-dotrun-anc/utils/validation.sh

Multiple Matches

Less specific patterns (Level 5) may match multiple helpers with the same filename:

loadHelpers utils

Behavior:

  • Searches all collections for utils.sh
  • Loads all matches if multiple found
  • Displays warning with list of matches

Example Output:

Warning: Multiple helpers matched 'utils' (level: filename)
  - /home/user/.config/dotrun/helpers/01-dotrun-anc/utils.sh
  - /home/user/.config/dotrun/helpers/02-myteam/utils.sh
Loading all matches...

When This Is Useful:

  • Intentionally loading helpers with same name from different collections
  • Collecting utilities from multiple sources

When to Avoid:

  • Use more specific pattern if you need only one helper
  • Example: loadHelpers dotrun-anc/utils instead of loadHelpers utils

Environment Modes

Control loadHelpers output verbosity using environment variables.

Verbose Mode

Variable: DR_HELPERS_VERBOSE=1

Effect: Enable detailed output showing the search process.

Example:

DR_HELPERS_VERBOSE=1 loadHelpers gcp/workstation

Output:

[DEBUG] Searching for helper: gcp/workstation
[DEBUG] Trying level 4 (path search)
[DEBUG] Search pattern: */gcp/workstation.sh
[DEBUG] Found match: /home/user/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh
[DEBUG] Validating canonical path...
[DEBUG] Loading helper: /home/user/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh
✓ Loaded: gcp/workstation (level: path)

Use Cases:

  • Debugging pattern matching issues
  • Understanding search behavior
  • Troubleshooting failed loads

Quiet Mode

Variable: DR_HELPERS_QUIET=1

Effect: Suppress all output except errors.

Example:

DR_HELPERS_QUIET=1 loadHelpers gcp/workstation

Output: (none, unless error occurs)

Use Cases:

  • Production scripts where output clutters logs
  • Automated workflows
  • Scripts that load many helpers

Combining Modes

You can combine environment variables with other features:

# Verbose list mode
DR_HELPERS_VERBOSE=1 loadHelpers gcp/workstation --list

# Quiet collection scope loading
DR_HELPERS_QUIET=1 loadHelpers @dotrun-anc

Security Features

The loadHelpers system implements three critical security mechanisms to prevent malicious or accidental misuse.

1. Path Traversal Prevention

Protection: All resolved helper paths are validated to ensure they exist within $DR_CONFIG/helpers.

Implementation:

# Security check using canonical paths
allowed_base="$(readlink -f "$DR_CONFIG/helpers")"
canonical_path="$(readlink -f "$helper")"

if [[ "$canonical_path" != "$allowed_base"* ]]; then
  echo "Error: Helper outside allowed directory: $helper" >&2
  return 1
fi

What This Prevents:

  • Path traversal attacks: loadHelpers ../../../../etc/passwd
  • Symlinks pointing outside helper directory
  • Absolute paths to sensitive system files

Example Error:

loadHelpers /etc/passwd

# Output:
# Error: Helper outside allowed directory: /etc/passwd
# Allowed directory: /home/user/.config/dotrun/helpers

2. Circular Dependency Detection

Protection: Maximum loading depth of 10 prevents infinite recursion when helpers load each other.

Implementation:

# Global counter and limit
declare -gri _DR_LOAD_DEPTH_MAX=10
declare -gi _DR_LOAD_DEPTH=0

# Check before loading
((_DR_LOAD_DEPTH++))
if [[ $_DR_LOAD_DEPTH -gt $_DR_LOAD_DEPTH_MAX ]]; then
  echo "Error: Maximum helper loading depth exceeded" >&2
  echo "Possible circular dependency detected" >&2
  return 1
fi

# Decrement after loading
source "$helper"
((_DR_LOAD_DEPTH--))

What This Prevents:

  • Circular dependencies: Helper A loads Helper B, Helper B loads Helper A
  • Deep chains: Helper A → B → C → D → E → F → G → H → I → J → K (exceeds limit)
  • Stack overflow: Infinite recursion crashing the shell

Example Scenario:

# In helpers/01-dotrun-anc/utils/a.sh
loadHelpers utils/b

# In helpers/01-dotrun-anc/utils/b.sh
loadHelpers utils/a # Circular dependency!

# Loading either helper triggers:
# Error: Maximum helper loading depth exceeded
# Possible circular dependency detected

Depth Limit Rationale:

  • 10 levels allows complex helper chains
  • Prevents runaway recursion
  • Constant _DR_LOAD_DEPTH_MAX can be adjusted if needed (requires modifying loadHelpers.sh)

3. De-duplication

Protection: Tracks loaded helpers by canonical path to prevent re-sourcing the same file.

Implementation:

# Global associative array tracking loaded helpers
declare -gA _DR_LOADED_HELPERS

# Check if already loaded
canonical_path="$(readlink -f "$helper")"
if [[ -n "${_DR_LOADED_HELPERS[$canonical_path]:-}" ]]; then
  [[ -z "${DR_HELPERS_QUIET:-}" ]] && echo "Skipping (already loaded): $helper" >&2
  return 0
fi

# Source the helper
source "$helper"

# Mark as loaded
_DR_LOADED_HELPERS["$canonical_path"]=1

What This Prevents:

  • Redundant sourcing: Loading same helper multiple times wastes time
  • Variable redefinition: Helpers re-defining functions/variables unnecessarily
  • Side effects: Helpers with side effects executing multiple times

Example Scenario:

# Script loads same helper via different patterns
loadHelpers dotrun-anc/utils/logging    # Loaded
loadHelpers utils/logging               # Skipped (already loaded)
loadHelpers 01-dotrun-anc/utils/logging # Skipped (already loaded)

# Output:
# ✓ Loaded: dotrun-anc/utils/logging
# Skipping (already loaded): utils/logging
# Skipping (already loaded): 01-dotrun-anc/utils/logging

Canonical Path Resolution:

  • readlink -f resolves symlinks and relative paths to absolute paths
  • Same file loaded via different patterns resolves to same canonical path
  • Associative array key is canonical path, ensuring true uniqueness

Security Best Practices

  1. Never load untrusted helpers: Only load helpers from known collections or your own scripts
  2. Validate collection sources: Use dr -col list to review installed collections
  3. Audit helper contents: Review helper files before loading (especially from new collections)
  4. Use specific patterns: Avoid Level 5 (filename-only) which may match unexpected files
  5. Monitor depth warnings: If you hit the depth limit, review helper dependencies

Security Limitations

What loadHelpers Does NOT Protect Against:

  • Malicious helper code: If a helper contains malicious commands, they will execute
  • Collection supply-chain attacks: Compromised collection repositories
  • User error: Loading wrong helper due to typo (use --list to verify)

Recommendation: Treat helper loading like sourcing any shell script - only load trusted code.

Collection Author Guidelines

If you're creating a collection that other users will import, follow these guidelines to ensure your scripts work reliably with the loadHelpers system.

Standard Collection Script Template

#!/usr/bin/env bash
### DOC
# myscript - Brief description of what this script does
#
# Usage:
#   dr myscript [options] [arguments]
#
# Options:
#   -h, --help    Show this help message
#
# Examples:
#   dr myscript --example
### DOC
set -euo pipefail

# Load the loadHelpers function (works in both dr and direct execution)
[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"

# Load required helpers using flexible patterns
loadHelpers gcp/workstation # Works regardless of namespace prefix
loadHelpers utils/common    # Another helper
loadHelpers @dotrun-anc     # Or load all from collection at once

# Script functions
show_help() {
  sed -n '/^### DOC$/,/^### DOC$/p' "$0" | sed '1d;$d' | sed 's/^# //; s/^#//'
}

main() {
  # Parse arguments
  if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
    show_help
    exit 0
  fi

  # Use functions from loaded helpers
  echo "Script using helpers..."

  # Your script logic here
}

main "$@"

Key Components Explained

1. Shebang and DOC Block

#!/usr/bin/env bash
### DOC
# Your documentation here
### DOC
  • Shebang: Always use #!/usr/bin/env bash for portability
  • DOC block: DotRun's documentation system (shown by dr help scriptname)
  • Include usage, options, and examples

2. Strict Error Handling

set -euo pipefail
  • set -e: Exit on error
  • set -u: Error on undefined variables
  • set -o pipefail: Pipelines fail if any command fails

3. LoadHelpers Function Sourcing

[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"
  • Critical: This line must appear before any loadHelpers calls
  • Checks if DR_LOAD_HELPERS environment variable is set
  • Sources the function definition if available
  • Safe for both dr execution and direct bash script.sh execution

4. Helper Loading

loadHelpers gcp/workstation
loadHelpers utils/common
  • Load all dependencies before defining script functions
  • Use Level 4 patterns (path with collection name) for reliability
  • Group related helper loads together

5. Main Function Pattern

main() {
  # Your script logic
}

main "$@"
  • Encapsulate logic in main() function
  • Pass all arguments: main "$@"
  • Allows easier testing and helper function organization

Helper Organization in Collections

Structure your collection's helpers directory logically:

my-collection/
├── dotrun.collection.yml
├── scripts/
│   ├── deploy.sh         # Uses helpers below
│   └── monitor.sh
└── helpers/
    ├── gcp/
    │   ├── auth.sh       # GCP authentication utilities
    │   └── workstation.sh # GCP Workstation management
    ├── kubernetes/
    │   ├── context.sh    # K8s context switching
    │   └── deploy.sh     # K8s deployment helpers
    └── utils/
        ├── logging.sh    # Logging utilities
        └── validation.sh # Input validation helpers

Benefits:

  • Logical categorization by domain (gcp, kubernetes, utils)
  • Easier discovery for users
  • Predictable patterns for loading (gcp/auth, kubernetes/context)

Loading Patterns for Collection Authors

Recommended: Path with Collection Name (Level 4)

loadHelpers my-collection/gcp/auth
loadHelpers my-collection/utils/logging

Pros:

  • Explicit about source collection
  • Avoids conflicts with other collections
  • Works regardless of namespace prefix

Cons:

  • Slightly more verbose than path-only pattern

Alternative: Path Without Collection Name (Level 4)

loadHelpers gcp/auth
loadHelpers utils/logging

Pros:

  • Cleaner, shorter syntax
  • Works if helper path is unique across collections

Cons:

  • May load wrong helper if multiple collections have same path
  • Less explicit about dependencies

When to Use: Your collection has unique helper paths unlikely to conflict.


Advanced: Collection Scope (Special)

loadHelpers @my-collection

Pros:

  • Loads all helpers from collection at once
  • Simplifies scripts with many helper dependencies

Cons:

  • Loads helpers that may not be needed
  • Slower for scripts needing only specific helpers
  • Less clear what dependencies script actually uses

When to Use: Scripts need most/all helpers from the collection.


Testing Collection Scripts

Before publishing your collection, test scripts with different namespace prefixes:

# Install collection with different prefixes
dr -col add https://github.com/you/my-collection # May install as 01-my-collection

# Manually rename namespace directory
cd ~/.config/dotrun/helpers
mv 01-my-collection 05-my-collection

# Test scripts still work
dr myscript # Should load helpers correctly despite prefix change

What to Verify:

  • All loadHelpers calls succeed
  • Script functions work as expected
  • No hardcoded paths break due to namespace change

Documentation for Collection Users

In your collection's README.md, document helper dependencies:

## Helper Dependencies

Scripts in this collection use the following helpers:

- `gcp/auth.sh` - GCP authentication utilities
- `gcp/workstation.sh` - GCP Workstation management
- `utils/logging.sh` - Logging utilities

These helpers are automatically loaded when scripts are executed via `dr scriptname`.

Why This Matters:

  • Users understand what helpers do
  • Easier debugging if helper loading fails
  • Clear documentation of collection structure

Avoiding Common Pitfalls

❌ Don't Use Absolute Paths

# BAD: Hardcoded absolute path breaks on other systems
source /home/you/.config/dotrun/helpers/01-my-collection/gcp/auth.sh

Why: Breaks on other users' systems, defeats purpose of loadHelpers.

❌ Don't Use Static Namespace Prefixes

# BAD: Assumes namespace prefix never changes
source "$DR_CONFIG/helpers/01-my-collection/gcp/auth.sh"

Why: Breaks if user changes namespace prefix (e.g., 01-05-).

✅ Do Use LoadHelpers Patterns

# GOOD: Works regardless of namespace prefix
loadHelpers my-collection/gcp/auth

Why: Flexible, robust, namespace-agnostic.


Helper Best Practices

When writing helpers for your collection:

  1. Export Functions: Use function_name() { ... } or export -f function_name
  2. Avoid Global Side Effects: Don't execute code at helper source time
  3. Document Functions: Use comments to describe what functions do
  4. Namespace Functions: Prefix with collection name to avoid conflicts (e.g., myteam_deploy())
  5. Handle Dependencies: If helper depends on another helper, load it inside the helper

Example Helper (helpers/gcp/auth.sh):

#!/usr/bin/env bash
# GCP authentication utilities

# myteam_gcp_login - Authenticate to GCP project
# Usage: myteam_gcp_login [project-id]
myteam_gcp_login() {
  local project="${1:-default-project}"
  echo "Authenticating to GCP project: $project"
  gcloud auth login --project="$project"
}

# myteam_gcp_set_project - Set active GCP project
# Usage: myteam_gcp_set_project <project-id>
myteam_gcp_set_project() {
  local project="$1"
  [[ -z "$project" ]] && {
    echo "Error: Project ID required" >&2
    return 1
  }
  gcloud config set project "$project"
}

# Export functions for use in scripts
export -f myteam_gcp_login myteam_gcp_set_project

Why These Practices Matter:

  • Exported functions: Available in scripts that load the helper
  • No side effects: Helper can be sourced without unexpected behavior
  • Documentation: Users understand function purpose
  • Namespacing: Prevents conflicts with other collections' functions
  • Dependency handling: Helpers can load other helpers if needed

Best Practices

Follow these practices to use the loadHelpers system effectively and avoid common pitfalls.

1. Prefer Specific Patterns

Rule: More specific patterns guarantee unique matches and prevent surprises.

✅ Good:

loadHelpers dotrun-anc/gcp/workstation # Level 4: Collection + path

⚠️ Risky:

loadHelpers workstation # Level 5: Filename only - may match multiple files

Why: Filename-only patterns may load helpers from unexpected collections if multiple collections have helpers with the same name.


2. Use Collection Names When Possible

Rule: Include collection name in pattern for clarity and reliability.

✅ Good:

loadHelpers dotrun-anc/utils/common # Explicit about source collection

⚠️ Less Specific:

loadHelpers utils/common # Works, but may match wrong collection

Why: Including collection name makes dependencies explicit and prevents loading helpers from unintended collections.


3. Test Patterns with --list

Rule: Preview pattern matches during development before loading.

Example:

loadHelpers gcp/workstation --list

# Output:
# Found 1 helper(s) matching 'gcp/workstation' (level: path):
#   /home/user/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

Why: Verifies pattern matches expected helper, catches typos early, prevents unexpected multiple matches.

When to Use:

  • Developing new scripts with helper dependencies
  • Debugging failed helper loads
  • Verifying pattern specificity before committing code

4. Load Helpers at Script Top

Rule: Source loadHelpers function and load dependencies before defining main logic.

✅ Good Structure:

#!/usr/bin/env bash
set -euo pipefail

# 1. Source loadHelpers function
[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"

# 2. Load all dependencies
loadHelpers dotrun-anc/gcp/workstation
loadHelpers utils/logging

# 3. Define script functions
main() {
  # Use helper functions
}

# 4. Execute main
main "$@"

❌ Bad Structure:

#!/usr/bin/env bash
set -euo pipefail

main() {
  # Loading helpers inside main function
  [[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"
  loadHelpers dotrun-anc/gcp/workstation # Too late if main uses helpers
}

main "$@"

Why: Loading at top ensures dependencies are available throughout script, makes dependencies visible upfront, follows shell scripting conventions.


5. Group Related Helper Loads

Rule: Organize helper loads logically by category or purpose.

✅ Good:

# GCP helpers
loadHelpers dotrun-anc/gcp/auth
loadHelpers dotrun-anc/gcp/workstation

# Kubernetes helpers
loadHelpers dotrun-anc/kubernetes/context
loadHelpers dotrun-anc/kubernetes/deploy

# Utility helpers
loadHelpers dotrun-anc/utils/logging
loadHelpers dotrun-anc/utils/validation

❌ Unorganized:

loadHelpers dotrun-anc/gcp/auth
loadHelpers dotrun-anc/utils/logging
loadHelpers dotrun-anc/kubernetes/context
loadHelpers dotrun-anc/gcp/workstation
loadHelpers dotrun-anc/utils/validation
loadHelpers dotrun-anc/kubernetes/deploy

Why: Improves readability, makes dependencies easier to understand, simplifies maintenance.


6. Document Helper Dependencies

Rule: Add comments explaining what each helper provides.

✅ Good:

# Load GCP authentication utilities (gcp_login, gcp_set_project)
loadHelpers dotrun-anc/gcp/auth

# Load Kubernetes context management (k8s_switch_context, k8s_get_current)
loadHelpers dotrun-anc/kubernetes/context

# Load logging utilities (log_info, log_error, log_debug)
loadHelpers dotrun-anc/utils/logging

Why: Clarifies what functions each helper provides, helps other developers understand dependencies, simplifies debugging.


7. Avoid Over-Using Collection Scope

Rule: Use loadHelpers @collection sparingly - prefer explicit helper loads.

⚠️ Use With Caution:

loadHelpers @dotrun-anc # Loads ALL helpers - may be slow

✅ Prefer Explicit:

loadHelpers dotrun-anc/gcp/auth
loadHelpers dotrun-anc/utils/logging
# Only load what you need

Why: Collection scope loads all helpers (slow for large collections), makes actual dependencies unclear, may load helpers with side effects.

When Collection Scope Is Appropriate:

  • Script genuinely needs most/all helpers from collection
  • Development/debugging (temporary usage)
  • Collection has few helpers (< 5)

8. Handle Missing Helpers Gracefully

Rule: Check if critical helpers loaded successfully.

✅ Good:

if ! loadHelpers dotrun-anc/gcp/auth 2>/dev/null; then
  echo "Error: Required helper not found: dotrun-anc/gcp/auth" >&2
  echo "Install the dotrun-anc collection: dr -col add <url>" >&2
  exit 1
fi

Why: Provides clear error message to user, explains how to fix (install collection), prevents cryptic errors later when helper function is called.


9. Use Quiet Mode in Production Scripts

Rule: Suppress helper loading output in scripts running in production/automation.

✅ Production Script:

#!/usr/bin/env bash
set -euo pipefail

# Quiet mode for cleaner logs
export DR_HELPERS_QUIET=1

[[ -n "${DR_LOAD_HELPERS:-}" ]] && source "$DR_LOAD_HELPERS"
loadHelpers dotrun-anc/gcp/auth
loadHelpers dotrun-anc/utils/logging

# Script logic

Why: Reduces log clutter, keeps output focused on script actions, errors still shown (quiet only suppresses success messages).


10. Verify Pattern Specificity

Rule: Understand which specificity level your pattern uses.

Use --list to Check:

loadHelpers gcp/workstation --list

# Output shows specificity level:
# Found 1 helper(s) matching 'gcp/workstation' (level: path)

Level Priority:

  • Level 1-3: Guaranteed unique match
  • Level 4: Usually unique, but verify with --list
  • Level 5: Often matches multiple files - avoid unless intentional

Quick Reference: Pattern Selection

Scenario Recommended Pattern Specificity Level
Collection script loading own helper collection/path/helper Level 4
Script loading from specific collection collection/path/helper Level 4
Loading unique helper (any collection) path/helper Level 4
Loading all helpers from collection @collection Special
Debugging/development path/helper --list Level 4 + list
Loading external helper /absolute/path.sh Level 1
Avoid (too broad) helper Level 5 ❌

Troubleshooting

Common errors and how to resolve them.

Error: No helpers found matching 'pattern'

Symptom:

loadHelpers gcp/workstation

# Output:
# Error: No helpers found matching 'gcp/workstation'

Possible Causes:

  1. Helper doesn't exist: The helper file isn't in any collection's helpers directory
  2. Wrong pattern: Pattern doesn't match actual file path
  3. Collection not installed: Collection containing helper not imported yet
  4. Typo in pattern: Pattern has spelling or path error

Solutions:

1. Use --list to Verify Pattern

loadHelpers gcp/workstation --list

# If nothing found, try broader pattern:
loadHelpers workstation --list

# Or search for similar patterns:
find ~/.config/dotrun/helpers -name "*workstation*"

2. Check Helpers Directory

# List all helper files
ls -R ~/.config/dotrun/helpers/

# Search for specific helper
find ~/.config/dotrun/helpers -name "workstation.sh"

3. Verify Collection Installation

# List installed collections
dr -col list

# If collection missing, install it:
dr -col add https://github.com/user/collection-repo

4. Enable Verbose Mode

DR_HELPERS_VERBOSE=1 loadHelpers gcp/workstation

# Shows detailed search process:
# [DEBUG] Searching for helper: gcp/workstation
# [DEBUG] Trying level 4 (path search)
# [DEBUG] Search pattern: */gcp/workstation.sh
# [DEBUG] No matches found

Error: Maximum helper loading depth exceeded

Symptom:

loadHelpers utils/a

# Output:
# Error: Maximum helper loading depth exceeded
# Possible circular dependency detected

Cause: Circular dependency between helpers (Helper A loads Helper B, Helper B loads Helper A).

Example Scenario:

File: ~/.config/dotrun/helpers/01-myteam/utils/a.sh

loadHelpers utils/b # Loads helper B

File: ~/.config/dotrun/helpers/01-myteam/utils/b.sh

loadHelpers utils/a # Loads helper A - CIRCULAR!

Solutions:

1. Review Helper Dependencies

Identify which helpers load each other:

# Search for loadHelpers calls in all helpers
grep -r "loadHelpers" ~/.config/dotrun/helpers/

2. Break the Circular Dependency

Option A: Extract shared logic to third helper

Before (circular):

# utils/a.sh
loadHelpers utils/b
do_a_thing() { do_b_thing; }

# utils/b.sh
loadHelpers utils/a
do_b_thing() { do_a_thing; }

After (refactored):

# utils/common.sh (new)
shared_function() { ... }

# utils/a.sh
loadHelpers utils/common
do_a_thing() { shared_function; }

# utils/b.sh
loadHelpers utils/common
do_b_thing() { shared_function; }

Option B: Merge helpers if closely related

If helpers are tightly coupled, combine into single helper.

3. Increase Depth Limit (Advanced)

If legitimate deep helper chain (rare), edit loadHelpers.sh:

# In ~/.local/share/dotrun/helpers/loadHelpers.sh
declare -gri _DR_LOAD_DEPTH_MAX=20 # Increase from 10 to 20

Warning: Only increase if you're certain there's no circular dependency. Deep chains often indicate poor helper design.


Warning: Multiple helpers matched

Symptom:

loadHelpers utils

# Output:
# Warning: Multiple helpers matched 'utils' (level: filename)
#   - /home/user/.config/dotrun/helpers/01-dotrun-anc/utils.sh
#   - /home/user/.config/dotrun/helpers/02-myteam/utils.sh
# Loading all matches...

Cause: Pattern is too broad (Level 5 - filename only) and multiple collections have helpers with the same name.

Impact:

  • All matching helpers are loaded
  • May load unintended helper from different collection
  • Functions may be redefined if helpers export same function names

Solutions:

1. Use More Specific Pattern

Instead of:

loadHelpers utils # Too broad - matches multiple files

Use:

loadHelpers dotrun-anc/utils # Specific to collection

Or even more specific:

loadHelpers dotrun-anc/utils/logging # Exact path

2. Verify Which Helper You Need

# List all matches
loadHelpers utils --list

# Output shows all matches:
# Found 2 helper(s) matching 'utils' (level: filename):
#   /home/user/.config/dotrun/helpers/01-dotrun-anc/utils.sh
#   /home/user/.config/dotrun/helpers/02-myteam/utils.sh

# Review each helper to determine which you need
cat ~/.config/dotrun/helpers/01-dotrun-anc/utils.sh
cat ~/.config/dotrun/helpers/02-myteam/utils.sh

3. Load All Intentionally

If you genuinely need helpers from multiple collections:

# Load all helpers named "utils" from all collections
loadHelpers utils

# Or be explicit:
loadHelpers dotrun-anc/utils
loadHelpers myteam/utils

When This Is Appropriate:

  • Helpers provide different, non-conflicting functions
  • You intentionally want utilities from multiple collections

Error: Helper outside allowed directory

Symptom:

loadHelpers /etc/passwd

# Output:
# Error: Helper outside allowed directory: /etc/passwd
# Allowed directory: /home/user/.config/dotrun/helpers

Cause: Security violation - attempted to load file outside $DR_CONFIG/helpers directory.

Possible Triggers:

  1. Absolute path to sensitive file: Attempting to load system files
  2. Symlink to external file: Helper is symlink pointing outside allowed directory
  3. Path traversal attempt: Using ../ to escape helpers directory
  4. Incorrect DR_CONFIG: DR_CONFIG environment variable misconfigured

Solutions:

1. Verify Path

Ensure helper path is within ~/.config/dotrun/helpers/:

# Check allowed directory
echo "$DR_CONFIG/helpers"
# Output: /home/user/.config/dotrun/helpers

# Verify helper exists in allowed directory
ls "$DR_CONFIG/helpers/01-dotrun-anc/gcp/workstation.sh"

2. Check Symlinks

# Check if helper is a symlink
ls -l ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

# If symlink, verify target is within allowed directory:
readlink -f ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh

If symlink points outside ~/.config/dotrun/helpers/, either:

  • Copy the actual file instead of symlinking
  • Move target file into helpers directory

3. Verify DR_CONFIG

# Check DR_CONFIG environment variable
echo "$DR_CONFIG"
# Should output: /home/user/.config/dotrun

# If wrong or empty, source ~/.drrc:
source ~/.drrc

4. Use Relative Path

If trying to load external helper for legitimate reason, copy it into helpers directory:

# Instead of:
loadHelpers /opt/custom/helper.sh # Error: outside allowed directory

# Copy to helpers first:
cp /opt/custom/helper.sh ~/.config/dotrun/helpers/custom-helper.sh
loadHelpers custom-helper # Now works

Debugging Helper Loading Issues

General Debugging Steps:

  1. Enable Verbose Mode:

    DR_HELPERS_VERBOSE=1 loadHelpers pattern
  2. Use List Mode:

    loadHelpers pattern --list
  3. Check Environment:

    echo "$DR_CONFIG"       # Should be ~/.config/dotrun
    echo "$DR_LOAD_HELPERS" # Should be ~/.local/share/dotrun/helpers/loadHelpers.sh
  4. Verify Helper Exists:

    find ~/.config/dotrun/helpers -name "helper.sh"
  5. Check Helper Syntax:

    bash -n ~/.config/dotrun/helpers/01-dotrun-anc/utils.sh # Check for syntax errors
  6. Test Direct Source:

    source ~/.config/dotrun/helpers/01-dotrun-anc/utils.sh # Test if helper sources cleanly

Getting Help

If issues persist after troubleshooting:

  1. Check DotRun version:

    dr --help # Shows version
  2. Review helper file for syntax errors or issues:

    cat ~/.config/dotrun/helpers/01-dotrun-anc/gcp/workstation.sh
  3. Check collection metadata:

    cat ~/.config/dotrun/collections/dotrun-anc/dotrun.collection.yml
  4. File an issue on the collection or DotRun repository with:

    • Error message
    • Pattern attempted
    • Output of DR_HELPERS_VERBOSE=1 loadHelpers pattern
    • Helper file path and permissions

See Also


Related Topics:


Quick Links:

⚠️ **GitHub.com Fallback** ⚠️