basecamp

Key global conf & tools

This file, ${SDD}/basecamp.sh is loaded first in the building process, so it's content is available to any Sh:erpa Bash script.

Constants

The three constant variables, used trougout the different commands and tools are SDD, SCD and EDITOR.

$SDD

This is the Core directory, the one that gets the updates when we run the sherpa self-update command.

readonly SDD="$HOME/.sherpa" # Sherpa Dot-Dir

$SCD

This one is the personnal mirror of the first one, that won't be touched by updates. Thins we have in $SCD/lib will override any library with the same name in $SDD/lib.

readonly SCD="$HOME/sherpa"  # Sherpa Custom-Dir

$EDITOR

This is the program command used when we invoke things like sherpa edit bin. By default it's vim, and expecting so far vim, nvim or code without breaking the tests from $SDD/tests. If you are useing something else as text editor command and it works, comme on Discord and let us know.

readonly EDITOR="vim"        # Default Editor

Shell Options

Because it's better to catch eventual errors the earliest we can, and being precocious never a waste, the basecamp sets the following options:

set -e  # Exit if a command ends with a non-zero status
set -u  # Threats unset variables as errors
set -o pipefail  # Hunt non-zero exits inside pipelines

use()

This function will source libraries into your project, following a certain pre-defined pattern.

src/bin.sh
use "std/fmt"

main() {
    p "Hello hikers!"
}

In this scenario, the function will try to source $SCD/lib/std/fmt.sh and if the file is not there, it will load $SDD/lib/std/fmt.sh instead.

Whatever .sh file we place in lib/dir/file.sh, either SDD or SCD, can be included with use "dir/file".

import()

Someting like a "remote use()", that will directly source a remote file from a Git platform into your script, without storing it locally. The same usage like including a CSS library with a CDN link.

src/bin.sh
use "std/fmt"
import -r "user/repo" -f "file.sh"
import -u "https://codeberg.org/AndiKod/someRepo/raw/branch/master/someFile.sh"

main() {
  h1 "Testing the import() function"
  p "Print $(stuff_from_importedFile "arg1")"
  p "Or $(stuff_from_Codeberg "arg1" "arg2")"
}

It first check if a full url is provided via the -u option and source it, if not it will check if a repository is provided via -r and it will assume Github and master brach. For main, we can -b main.

confirm()

We sometimes needs to ask for a confirmation before proceding with certain actions like deleting an item or updating a package.

src/bin.sh
use "std/fmt"

if [[ "$1" == "rmBox" ]]; then

  # Some logic here, to get $box

  confirm "Do you really want to delete ${box}"

# Output:
# Do you really want to delete boxName? (y/n):_

# If `y` was pressed, we can delete.

fi

If anything else was pressed, a feedback message is printed with the btnWarning style, to confirm that the action was Canceled!

YAML CRUD

A serie or wrapers around yq actions to make the data manipulation simple and intuitive. It revolves around a pair of key/value in a .yaml file as a 'yaml item'.

add_yaml_parent()

We sometimes need a parent item like doggo: with child items like doggo.name and doggo.color, so we first need a way to add the parent.

src/bin.sh
pets="${ROOT}/data/pets.yaml"

add_yaml_parent "doggo" "$pets"

Then add the child items...

add_yaml_item()

This time we will insert a key/value pair, either directly or as a child of an existing parent key.

src/bin.sh
pets="${ROOT}/data/pets.yaml"

add_yaml_item "doggo.name" "Max" "$pets"
add_yaml_item "mood" "Playful" "$pets"

Now, pets.yaml will look like this:

${ROOT}/data/pets.yaml
doggo:
  name: Max
mood: Playful

get_yaml_item()

We haven't did all that just to write some text in a file, so let's use it. We will print the mood of our doggo.

src/bin.sh
use "std/fmt"
pets="${ROOT}/data/pets.yaml"
doggo="$(get_yaml_item "doggo.name" "$pets")"
mood="$(get_yaml_item "mood" "$pets")"

main() {
    h1 "Doggo status ^^"
    p  "${doggo} is actually ${mood}."
}

But after a few time, he is finally tired...

update_yaml_item()

So, we need to update the mood. It's like adding an item but providing the key we want to update, the new value and the file holding it:

src/bin.sh
  pets="${ROOT}/data/pets.yaml"

  update_yaml_item "mood" "Tired" "$pets"
}

If we check again the 'Doggo status' it will reflect the change. But what if we finally don't care about tracking Max's mood, se the key and it's value is no longer needed?

remove_yaml_item()

Well, we just call the obvious function and provide the name of the no longer needed key. This is one of those use-cases for the confirm() function, btw.

src/bin.sh
  pets="${ROOT}/data/pets.yaml"

  confirm "Do you really want to remove the 'mood' key"

  remove_yaml_item "mood" "$pets"
}

env()

To store API keys, and other symilar things, we'll use a "gitignored" .yaml file in the SherpaCustomDir root. A simple helper will get the value from wherever needed.

Imagine a simple env.yaml file as:

$SCD/env.yaml
WEATHERAPI_KEY: fake5dsijfujp5key

Then in some script we check the weather

src/bin.sh
  use "std/fmt"
  api="https://api.weatherapi.com/v1"
  key="$(env "WEATHERAPI_KEY")"

  weatherNow="${api}/current.json?key=${key}&q=Berlin"
  temp="$(fetch $weatherNow | jq .current.temp_c)"


  main() {
    h1 "Berlin Weather Live"
    p  "Right now, we have ${temp}°C"
  }

package()

Just a little helper, in case we need to use data from the Sherpa.yaml manifest in the root of our BashBox.

Printing packageName v0.1.0 for the -v flag:

src/_options.sh
...
v)
  # -v short flag for version.
  printf "%s v%s\n" "$(package "name")" "$(package "version")"
  ;;
...

Data files CRUD

This time a serie of wrapers around our own verb_yaml_item commands, in case we are using yaml files inside ${ROOT}/data. Thanks to the predictable pattern, we can abstract the logic and have a nice'n'short syntax to manipulate data.

In this version, the first argument is the name of the data file (without extension), then the key and if needed the value. It's inspired from Eleventy's way to work with data files.

Remember Max? Our doggo? Let's re-write the examples using the data helpers ...

dataAddParent()

Knowing that we expect a .yaml file inside the ${ROOT}/data directory, all we need to do is calling the name of the file we want to use (without extension) then the key we want to add inside:

src/bin.sh
dataAddParent "pets" "doggo"

dataAdd()

It goes the same for addin regular keys, normal or nested. The pattern is: dataAdd "targetFile" "key" "value"

src/bin.sh
dataAdd "pets" "doggo.name" "Max"
dataAdd "pets" "mood" "Playful"

Let's check Max's name & mood:

dataGet()

It goes the same for addin regular keys, normal or nested. The pattern is: dataAdd "targetFile" "key" "value"

src/bin.sh
use "std/fmt"
doggo="$(dataGet "pets" "doggo.name")"
mood="$(dataGet "pets" "mood")"

main() {
    h1 "Doggo status ^^"
    p  "${doggo} is actually ${mood}."
}

So much cleaner. Time to update the mood...

dataUpdate()

It goes the same for addin regular keys, normal or nested. The pattern is: dataAdd "targetFile" "key" "value"

src/bin.sh
dataUpdate "pets" "mood" "Tired"

dataDelete()

Finally we'll get rid of a key

src/bin.sh
dataDelete "pets" "mood"

...and just like that, we have a nice little CRUD set.