Skip to content

brtlvrs/opscli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

opscli

repository opscli
version CHANGELOG.md
owner brtlvrs
license MIT

A BASH shell framework that is sourced into an interactive shell (via .bashrc) or into scripts. It provides a structured, reloadable function library with built-in logging, cheatsheet generation, and version management.

Fork this repository if you want to contribute to the framework. To add your own functions, create a separate extensions repo — see Extensions.

What's new

v2.4.0 — Extensions support: set OPSCLI_EXTENSIONS_PATH to a separate repo and the framework sources it automatically on load and reload. Use ops-init-extensions to bootstrap a new extensions repo with a demo function. See the full CHANGELOG.

Table of contents

How it works

opscli uses a two-repo model:

Repo Purpose
opscli (this repo) The framework — foundational functions, logging, aliases, version management. Updated via ops-update. Never edit directly.
your extensions repo Your custom functions. A separate git repo you own and version independently.

At shell startup, .bashrc sources the framework. If OPSCLI_EXTENSIONS_PATH points to your extensions repo, the framework automatically sources it too — so ops-reload reloads everything in one shot.

.bashrc
  └── source opscli/library.sh
        ├── loads framework functions
        └── sources $OPSCLI_EXTENSIONS_PATH  (if set)

Prerequisites

  • bash
  • git
  • jq (optional)
  • yq (optional)

Installation

  1. Clone this repo under $HOME/repos/:

    git clone <opscli-url> $HOME/repos/opscli
  2. Add the following to your ~/.bashrc:

    # point to your extensions repo (optional but recommended)
    export OPSCLI_EXTENSIONS_PATH="$HOME/repos/my-functions"
    
    # load the opscli framework
    source $HOME/repos/opscli/library.sh
  3. Reload your shell:

    source ~/.bashrc
  4. Update to the latest tagged version:

    ops-update

Extensions

Your custom functions live in a separate repo that you create and manage. The framework sources it automatically when OPSCLI_EXTENSIONS_PATH is set.

Setting up your extensions repo

mkdir -p $HOME/repos/my-functions
cd $HOME/repos/my-functions
git init

Add subfolders for your functions — any .sh file in any subfolder is sourced automatically:

my-functions/
├── kubernetes/
│   └── helpers.sh
├── aws/
│   └── helpers.sh
└── daily/
    └── shortcuts.sh

Set OPSCLI_EXTENSIONS_PATH in your .bashrc (before the source line) and reload. Your functions are now available alongside the framework functions.

Updating the framework without affecting extensions

ops-update          # updates the framework, leaves your extensions untouched
ops-reload          # reloads both framework and extensions

Because your extensions live in a separate repo, ops-update (which does a git reset --hard inside the framework repo) never touches your files.

Writing extension functions

Follow the same conventions as framework functions — see Writing functions. Use the ops::* namespace so your functions appear in ops-functions and are cleaned up correctly on ops-reload.

Key aliases

Alias Description
ops-reload Reload the framework and extensions from their respective paths
ops-functions Browse the full function cheatsheet (piped through less -R)
ops-alias Show alias summary only
ops-info [key] Show library metadata (path, version, git url, env, …)
ops-update [tag] Fetch tags and reset framework to a version
ops-init-extensions Initialize a new extensions repo at $OPSCLI_EXTENSIONS_PATH
shellTMPdir Create a hidden temp directory under $HOME
shellTMP Create a temp file inside a shellTMPdir directory

ops-info accepts a key argument to return a single value:

ops-info version      # current version tag or branch
ops-info git_url      # remote origin URL
ops-info prod_path    # path to the framework clone
ops-info env          # "prod" or "dev"
ops-info --all        # print all of the above

Using the library in scripts

When the library is sourced, library.sh exports $OPSCLI_PATH. Scripts can use this to reload the library and enforce a minimum version:

#!/bin/bash

[[ ! -d ${OPSCLI_PATH} ]] && { echo "WARNING: opscli not loaded."; exit 1; }

unset OPSCLI_LOADED
source ${OPSCLI_PATH}/library.sh
ops::version::isSupported v2.0.0 || exit 1

# ... rest of the script

If OPSCLI_EXTENSIONS_PATH is set in the environment, extensions are loaded automatically here too.

Pass -v <version> directly to library.sh to combine the source and version check:

source ${OPSCLI_PATH}/library.sh -v v2.0.0 || exit 1

Console logging

Use these functions instead of raw echo. All output goes to stderr.

Function Colour Notes
writeINF cyan General informational message
writeDBG grey Only printed when $DEBUG or $debug is set
writeWRN yellow Includes source file and line number
writeERR red Includes source file and line number
writeOK green Single-line pass / success result
writeFAIL red Single-line fail / validation result
writeNOTE grey Single-line subtle annotation
writeTODO yellow Includes function name, file, and line number

All functions accept a single string argument and support multi-line messages:

writeINF "Library loaded successfully."

writeINF \
"
Multi-line message:
  line one
  line two
"

writeWRN "Something unexpected happened."
writeERR "Fatal: could not connect."

Writing functions

Use templates/function.tmpl as a starting point. Every function must include a cheat block so it appears in ops-functions and ops-alias:

#-- START CHEAT --
#  Function: ops::namespace::functionname
#    Alias:  ops-myalias
#    Description: One-line description
#    Parameters:
#      -h | --help   Show help
#      $1            Some positional argument
#-- END CHEAT --

Standard function structure:

function ops::namespace::name() {
    function ops::namespace::name::_usage() { cat <<-EOF
        usage: ops-myalias [-h] <arg>
    EOF
    }
    function ops::namespace::name::_guardrails() { … }
    function ops::namespace::name::_process-arguments() {
        local arguments=($(ops::common::splitArgs "$@"))
        for (( i=0; i<${#arguments[@]}; i++ )); do
            case ${arguments[i]} in
                -h|--help) ops::namespace::name::_usage; return 1 ;;
            esac
        done
    }
    function ops::namespace::name::_main() { … }

    ops::namespace::name::_guardrails "$@" || return $?
    ops::namespace::name::_process-arguments "$@" || return $?
    ops::namespace::name::_main || return $?
}
alias ops-myalias='ops::namespace::name'

ops::common::splitArgs normalises --key=value into --key value pairs before the case loop.

Place the .sh file in any subfolder of your extensions repo; it is sourced automatically on the next ops-reload.

Debugging

set -x is safe to use interactively. ops::common::appendPromptCommand prepends set +x to PROMPT_COMMAND, so xtrace is silenced automatically before the next prompt — stray set -x calls will not pollute your interactive shell.

Enable writeDBG output:

export DEBUG=true   # or: debug=true

When DEBUG is set, temp directories created by shellTMPdir are not cleaned up on exit or CTRL-C, making it easier to inspect intermediate state.

Contributing to the framework

To contribute changes to the framework itself (not extensions), you need both the production and development clones.

Setup

ops-init-dev    # clones the framework repo to $HOME/repos/opscli.dev and creates the dev branch
ops-dev         # switch the active library to the dev clone

Switching between environments

Alias Description
ops-dev Reload from the development clone (opscli.dev)
ops-prod Reload from the production clone (opscli)

ops-update automatically switches from dev to prod when invoked from the dev clone, so you do not need to run ops-prod first.

Development cycle

  1. git merge main && git push — sync the dev branch with the latest release
  2. Add or modify .sh files in the dev clone
  3. ops-reload — pick up the changes in the current shell
  4. git commit the changes
  5. When ready to release, follow the release process in CLAUDE.md

About

A BASH framework for OPS scripts

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages