diff --git a/.secrets.baseline b/.secrets.baseline
index 9045f87..a30cb89 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -3,7 +3,7 @@
"files": "^.secrets.baseline$",
"lines": null
},
- "generated_at": "2025-11-23T00:19:45Z",
+ "generated_at": "2025-11-26T21:55:36Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
@@ -82,7 +82,7 @@
"hashed_secret": "1de54cf4c9c5e2b8b9aef885aebefb5c1be33332",
"is_secret": false,
"is_verified": false,
- "line_number": 136,
+ "line_number": 207,
"type": "Secret Keyword",
"verified_result": null
}
diff --git a/README.md b/README.md
index 10a9885..f02e131 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,77 @@ check_required_bins git jq
+
+ install_jq
+
+Installs jq binary.
+
+**Environment Variables:**
+- `VERBOSE`: If set to true, print verbose output (optional, defaults to false)
+
+**Arguments:**
+- `$1`: version of jq to install (optional, defaults to latest). Example format of valid version is "1.8.1"
+- `$2`: location to install jq to (optional, defaults to /usr/local/bin)
+- `$3`: if set to true, skips installation if jq is already detected (optional, defaults to true)
+- `$4`: the exact url to download jq from (optional, defaults to https://github.com/jqlang/jq/releases/latest/download/jq-${os}-${arch})
+
+**Returns:**
+- `0` - Success (jq installed successfully)
+- `1` - Failure (installation failed)
+- `2` - Failure (incorrect usage of function)
+
+**Usage:**
+```bash
+install_jq "latest" "/usr/local/bin" "true"
+```
+
+
+
+
+
+ is_boolean
+
+Determine if value is a boolean.
+
+**Arguments:**
+- `$1`: value to check (required)
+
+**Returns:**
+- `0` - Success (value is a boolean - true, True, false or False)
+- `1` - Failure (value is not a boolean)
+- `2` - Failure (incorrect usage of function)
+
+**Usage:**
+```bash
+is_boolean "true"
+```
+
+
+
+
+
+ return_mac_architecture
+
+Returns the architecture of the MacOS.
+
+**Arguments:** n/a
+
+**Returns:**
+- `0` - Success
+ - `"amd64"` - if the OS is macOS and the CPU is Intel
+ - `"arm64"` - if the OS is macOS and the CPU is Apple Silicon
+- `2` - Failure (Did not detect MacOS)
+
+**Usage:**
+```bash
+arch=$(return_mac_architecture)
+```
+
+
+
+
+
+
## [ibmcloud/iam](ibmcloud/iam.sh)
generate_iam_bearer_token
diff --git a/common/common.sh b/common/common.sh
index f928236..221cdd3 100755
--- a/common/common.sh
+++ b/common/common.sh
@@ -174,7 +174,9 @@ check_required_bins() {
done
if (( ${#missing[@]} )); then
- echo "Missing binaries: ${missing[*]}" >&2
+ if [ "${verbose}" = true ]; then
+ echo "Missing binaries: ${missing[*]}" >&2
+ fi
return ${RETURN_CODE_ERROR}
fi
@@ -184,11 +186,178 @@ check_required_bins() {
return ${RETURN_CODE_SUCCESS}
}
+#===============================================================
+# FUNCTION: is_boolean
+# DESCRIPTION: Determine is value is a boolean.
+#
+# ARGUMENTS:
+# - $1: value to check (required)
+#
+# RETURNS:
+# 0 - Success (value is a boolean - true, True, false or False)
+# 1 - Failure (value is not a boolean)
+# 2 - Failure (incorrect usage of function)
+#
+# USAGE: is_boolean
+#===============================================================
+is_boolean() {
+
+ # Validate an arg has been provided
+ if [ $# -ne 1 ]; then
+ echo "Error: is_boolean requires exactly 1 argument, but $# were provided" >&2
+ echo "Usage: is_boolean " >&2
+ exit ${RETURN_CODE_ERROR_INCORRECT_USAGE}
+ fi
+
+ if [[ $1 =~ ^([Tt]rue|[Ff]alse)$ ]]; then
+ return ${RETURN_CODE_SUCCESS}
+ else
+ return ${RETURN_CODE_ERROR}
+ fi
+}
+
+#===============================================================
+# FUNCTION: return_mac_architecture
+# DESCRIPTION: Returns the architecture of the MacOS
+#
+# RETURNS:
+# 0 - Success
+# - "amd64" - if the OS is macOS and the CPU is Intel
+# - "arm64" - if the OS is macOS and the CPU is Apple Silicon
+# 2 - Failure (Did not detect MacOS)
+#
+# USAGE: return_mac_architecture
+#===============================================================
+return_mac_architecture() {
+
+ local arch="arm64"
+ local cpu
+ if [[ ${OSTYPE} == 'darwin'* ]]; then
+ cpu="$(sysctl -a | grep machdep.cpu.brand_string)"
+ if [[ "${cpu}" == 'machdep.cpu.brand_string: Intel'* ]]; then
+ # macOS on Intel architecture
+ arch="amd64"
+ fi
+ else
+ echo "Unsupported OS: ${OSTYPE}" >&2
+ return ${RETURN_CODE_ERROR_INCORRECT_USAGE}
+ fi
+ echo ${arch}
+}
+
+#===============================================================
+# FUNCTION: install_jq
+# DESCRIPTION: Installs jq binary
+#
+# ENVIRONMENT VARIABLES:
+# - VERBOSE: If set to true, print verbose output (optional, defaults to false)
+#
+# ARGUMENTS:
+# - $1: version of jq to install (optional, defaults to latest). Example format of valid version is "1.8.1".
+# - $2: location to install jq to (optional, defaults to /usr/local/bin)
+# - $3: if set to true, skips installation if jq is already detected (optional, defaults to true)
+# - $4: the exact url to download jq from (optional, defaults to https://github.com/jqlang/jq/releases/latest/download/jq-${os}-${arch})
+#
+# RETURNS:
+# 0 - Success (jq installation successful)
+# 1 - Failure (jq installation failed)
+# 2 - Failure (incorrect usage of function)
+#
+# USAGE: install_jq "latest" "/usr/local/bin" "true"
+#===============================================================
+install_jq() {
+
+ local version=${1:-"latest"} # default to latest if not specified
+ local location=${2:-"/usr/local/bin"}
+ local skip_if_detected=${3:-"true"}
+ local link_to_binary=${4:-""}
+ local verbose=${VERBOSE:-false}
+
+ # Validate $3 arg is boolean
+ if ! is_boolean "${skip_if_detected}"; then
+ echo "Unsupported value detected for the 3rd argument. Only 'true' or 'false' is supported. Found: ${skip_if_detected}." >&2
+ return ${RETURN_CODE_ERROR_INCORRECT_USAGE}
+ fi
+
+ # return 0 if jq already installed and skip_if_detected is true
+ if [ "${skip_if_detected}" == "true" ]; then
+ if check_required_bins jq; then
+ if [ "${verbose}" = true ]; then
+ echo "Found jq already installed. Taking no action."
+ fi
+ return ${RETURN_CODE_SUCCESS}
+ fi
+ fi
+ # ensure curl is installed
+ check_required_bins curl || return $?
+
+ # strip "v" prefix if it exists in version
+ if [[ "${version}" == "v"* ]]; then
+ version="${version#v}"
+ fi
+
+ # if no link to binary passed, determine the download link based on detected os and arch
+ if [ -z "${link_to_binary}" ]; then
+ # determine the OS and architecture
+ local os="linux"
+ local arch="amd64"
+ if [[ ${OSTYPE} == 'darwin'* ]]; then
+ arch=$(return_mac_architecture)
+ fi
+ # determine download link to binary
+ link_to_binary="https://github.com/jqlang/jq/releases/download/jq-${version}/jq-${os}-${arch}"
+ if [ "${version}" = "latest" ]; then
+ link_to_binary="https://github.com/jqlang/jq/releases/latest/download/jq-${os}-${arch}"
+ fi
+ fi
+ if [ "${verbose}" = true ]; then
+ echo "Using download link: ${link_to_binary}"
+ fi
+
+ # use sudo if needed
+ local arg=""
+ if ! [ -w "${location}" ]; then
+ echo "No write permission to ${location}. Using sudo..."
+ arg=sudo
+ fi
+
+ # remove if already exists
+ ${arg} rm -f "${location}/jq"
+
+ # download binary
+ set +e
+ if ! ${arg} curl --silent \
+ --connect-timeout 5 \
+ --max-time 10 \
+ --retry 3 \
+ --retry-delay 2 \
+ --retry-connrefused \
+ --fail \
+ --show-error \
+ --location \
+ --output "${location}/jq" \
+ "${link_to_binary}"
+ then
+ echo "Failed to download ${link_to_binary}"
+ return ${RETURN_CODE_ERROR}
+ fi
+ set -e
+
+ # make executable
+ ${arg} chmod +x "${location}/jq"
+
+ if [ "${verbose}" = true ]; then
+ echo "Successfully completed installation to ${location}/jq"
+ fi
+ return ${RETURN_CODE_SUCCESS}
+}
+
#===============================================================
# UNIT TESTS
#===============================================================
_test() {
+ local make_api_calls="${MAKE_API_CALLS:-false}"
printf "%s\n\n" "Running tests.."
# check_env_vars
@@ -223,6 +392,55 @@ _test() {
assert_fail "${rc}"
printf "%s\n\n" "✅ PASS"
+ # is_boolean
+ # -----------------------------------
+ # - Test valid booleans
+ for i in "true" "false" "True" "False"; do
+ printf "%s\n" "Running 'is_boolean $i'"
+ rc=${RETURN_CODE_SUCCESS}
+ is_boolean $i >/dev/null 2>&1 || rc=$?
+ assert_pass "${rc}"
+ printf "%s\n\n" "✅ PASS"
+ done
+
+ # - Test non valid boolean
+ printf "%s\n" "Running 'is_boolean not_a_boolean'"
+ rc=${RETURN_CODE_SUCCESS}
+ is_boolean not_a_boolean >/dev/null 2>&1 || rc=$?
+ assert_fail "${rc}"
+ printf "%s\n\n" "✅ PASS"
+
+ # install_jq
+ # -----------------------------------
+ if [ "${make_api_calls}" = true ]; then
+ # Check if jq already exists on $PATH
+ local jq_installed=true
+ check_required_bins jq || jq_installed=false
+
+ # - Test installing jq using defaults (when it does not already exist)
+ if [ ${jq_installed} = false ]; then
+ printf "%s\n" "Running 'install_jq' (when jq does not already exists)"
+ rc=${RETURN_CODE_SUCCESS}
+ install_jq >/dev/null 2>&1 || rc=$?
+ assert_pass "${rc}"
+ printf "%s\n\n" "✅ PASS"
+ fi
+
+ # - Test installing it when jq already exists with default args (should be skipped)
+ printf "%s\n" "Running 'install_jq' (when jq already exists - install will be skipped)"
+ rc=${RETURN_CODE_SUCCESS}
+ install_jq >/dev/null 2>&1 || rc=$?
+ assert_pass "${rc}"
+ printf "%s\n\n" "✅ PASS"
+
+ # - Test installing exact version to /tmp even if jq already detected on $PATH
+ printf "%s\n" "Running 'install_jq 1.8.1 /tmp false'"
+ rc=${RETURN_CODE_SUCCESS}
+ install_jq "1.8.1" "/tmp" "false" >/dev/null 2>&1 || rc=$?
+ assert_pass "${rc}"
+ printf "%s\n\n" "✅ PASS"
+ fi
+
# -----------------------------------
echo "✅ All tests passed!"
}