123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- #!/bin/bash
- set -e
- export PATH=/usr/local/bin:/usr/bin:/bin
- CYAN='\033[33;36m';
- RED='\033[0;31m'
- YELLOW='\033[0;93m'
- GREEN='\033[0;32m'
- NC='\033[0m'
- throw()
- {
- echo -e "${RED}${1}${NC}"
- exit "${2:-1}"
- }
- scriptLocation=$(dirname -- "$(readlink -fn -- "$0"; echo x)")
- cd "${scriptLocation%x}"
- if [[ ! -f .env ]]; then
- throw "Error: .env does not exist" 2
- fi
- source .env
- docker="${DOCKER_COMMAND:-docker}"
- if [[ ${docker} != "docker" && ${docker} != "podman" ]]; then
- throw "Error: Invalid DOCKER_COMMAND"
- fi
- set +e
- ${docker} --version > /dev/null 2>&1
- dockerInstalled=$?
- dockerCompose="${docker} compose"
- ${dockerCompose} version > /dev/null 2>&1
- composeInstalled=$?
- if [[ ${composeInstalled} -gt 0 ]]; then
- dockerCompose="${docker}-compose"
- ${dockerCompose} --version > /dev/null 2>&1
- composeInstalled=$?
- fi
- set -e
- if [[ ${dockerInstalled} -gt 0 || ${composeInstalled} -gt 0 ]]; then
- if [[ ${dockerInstalled} -eq 0 && ${composeInstalled} -gt 0 ]]; then
- throw "Error: ${dockerCompose} not installed."
- fi
- if [[ ${dockerInstalled} -gt 0 && ${composeInstalled} -eq 0 ]]; then
- throw "Error: ${docker} not installed."
- fi
- throw "Error: ${docker} and ${dockerCompose} not installed."
- fi
- composeFiles="-f compose.yml"
- if [[ ${APP_ENV} == "development" ]]; then
- composeFiles="${composeFiles} -f compose.dev.yml"
- fi
- if [[ ${CONTAINER_MODE} == "local" ]]; then
- composeFiles="${composeFiles} -f compose.local.yml"
- fi
- if [[ -f compose.override.yml ]]; then
- composeFiles="${composeFiles} -f compose.override.yml"
- elif [[ -f docker-compose.override.yml ]]; then
- composeFiles="${composeFiles} -f docker-compose.override.yml"
- fi
- dockerCompose="${dockerCompose} ${composeFiles}"
- handleServices()
- {
-
- validServices=($1)
- servicesArray=()
- invalidServices=false
- for x in "${@:2}"; do
- if [[ ${validServices[*]} =~ (^|[[:space:]])"$x"($|[[:space:]]) ]]; then
- if ! [[ ${servicesArray[*]} =~ (^|[[:space:]])"$x"($|[[:space:]]) ]]; then
- servicesArray+=("${x}")
- fi
- else
- if [[ $invalidServices == false ]]; then
- invalidServices="${x}"
- else
- invalidServices="${invalidServices} ${x}"
- fi
- fi
- done
- if [[ $invalidServices == false && ${#servicesArray[@]} -gt 0 ]]; then
- echo "1|${servicesArray[*]}"
- elif [[ $invalidServices == false ]]; then
- echo "1|all"
- else
- echo "0|Invalid Service(s): ${invalidServices}"
- fi
- }
- runDockerCommand()
- {
- validCommands=(start stop restart pull build ps logs)
- if ! [[ ${validCommands[*]} =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; then
- throw "Error: Invalid runDockerCommand input"
- fi
- servicesString=$(handleServices "backend frontend mongo postgres redis" "${@:3}")
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend, mongo, postgres, redis]"
- fi
- if [[ ${servicesString:2:4} == "all" ]]; then
- servicesString=""
- pullServices="mongo postgres redis"
- buildServices="backend frontend"
- else
- servicesString=${servicesString:2}
- pullArray=()
- buildArray=()
- if [[ "${servicesString}" == *mongo* ]]; then
- pullArray+=("mongo")
- fi
- if [[ "${servicesString}" == *postgres* ]]; then
- pullArray+=("postgres")
- fi
- if [[ "${servicesString}" == *redis* ]]; then
- pullArray+=("redis")
- fi
- if [[ "${servicesString}" == *backend* ]]; then
- buildArray+=("backend")
- fi
- if [[ "${servicesString}" == *frontend* ]]; then
- buildArray+=("frontend")
- fi
- pullServices="${pullArray[*]}"
- buildServices="${buildArray[*]}"
- fi
- if [[ ${2} == "stop" || ${2} == "restart" ]]; then
-
- ${dockerCompose} stop ${servicesString}
- fi
- if [[ ${2} == "start" || ${2} == "restart" ]]; then
-
- ${dockerCompose} up -d ${servicesString}
- fi
- if [[ ${2} == "pull" && ${pullServices} != "" ]]; then
-
- ${dockerCompose} pull ${pullServices}
- fi
- if [[ ${2} == "build" && ${buildServices} != "" ]]; then
-
- ${dockerCompose} build ${buildServices}
- fi
- if [[ ${2} == "ps" || ${2} == "logs" ]]; then
-
- ${dockerCompose} "${2}" ${servicesString}
- fi
- }
- getContainerId()
- {
- if [[ $docker == "docker" ]]; then
- containerId=$(${dockerCompose} ps -q "${1}")
- else
- containerId=$(${dockerCompose} ps \
- | sed '0,/CONTAINER/d' \
- | awk "/${1}/ {print \$1;exit}")
- fi
- echo "${containerId}"
- }
- handleReset()
- {
- servicesString=$(handleServices "backend frontend mongo postgres redis" "${@:2}")
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend, mongo, postgres, redis]"
- fi
- confirmMessage="${GREEN}Are you sure you want to reset all data"
- if [[ ${servicesString:2:4} != "all" ]]; then
- confirmMessage="${confirmMessage} for $(echo "${servicesString:2}" | tr ' ' ',')"
- fi
- echo -e "${confirmMessage}? ${YELLOW}[y,n]: ${NC}"
- read -r confirm
- if [[ "${confirm}" != y* ]]; then
- throw "Cancelled reset"
- fi
- if [[ ${servicesString:2:4} == "all" ]]; then
- runDockerCommand "$(basename "$0") $1" stop
- ${dockerCompose} rm -v --force
- else
-
- runDockerCommand "$(basename "$0") $1" stop ${servicesString:2}
-
- ${dockerCompose} rm -v --force ${servicesString:2}
- fi
- }
- attachContainer()
- {
- containerId=$(getContainerId "${2}")
- if [[ -z $containerId ]]; then
- throw "Error: ${2} offline, please start to attach."
- fi
- case $2 in
- backend)
- echo -e "${YELLOW}Detach with CTRL+P+Q${NC}"
- ${docker} attach "$containerId"
- ;;
- mongo)
- MONGO_VERSION_INT=${MONGO_VERSION:0:1}
- echo -e "${YELLOW}Detach with CTRL+D${NC}"
- if [[ $MONGO_VERSION_INT -ge 5 ]]; then
- ${dockerCompose} exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry()" --shell
- else
- ${dockerCompose} exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}"
- fi
- ;;
- postgres)
- echo -e "${YELLOW}Detach with CTRL+D${NC}"
- PGPASSWORD="${POSTGRES_PASSWORD}" ${dockerCompose} exec postgres psql "${POSTGRES_USERNAME}" musare
- ;;
- redis)
- echo -e "${YELLOW}Detach with CTRL+C${NC}"
- ${dockerCompose} exec redis redis-cli -a "${REDIS_PASSWORD}"
- ;;
- *)
- throw "Invalid service ${2}\n${YELLOW}Usage: ${1} [backend, mongo, redis]"
- ;;
- esac
- }
- handleLinting()
- {
- set +e
-
- services=$(sed "s/\(\ \)\{0,1\}\(-\)\{0,2\}fix//g;t;q1" <<< "${@:2}")
- fixFound=$?
- if [[ $fixFound -eq 0 ]]; then
- fix="--fix"
- echo -e "${GREEN}Auto-fix enabled${NC}"
- fi
-
- services=$(sed "s/\(\ \)\{0,1\}\(-\)\{0,2\}no-cache//g;t;q1" <<< "${services}")
- noCacheFound=$?
- cache="--cache"
- if [[ $noCacheFound -eq 0 ]]; then
- cache=""
- echo -e "${YELLOW}ESlint cache disabled${NC}"
- fi
- set -e
-
- servicesString=$(handleServices "backend frontend docs shell" ${services[@]})
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend, docs, shell] [fix]"
- fi
- set +e
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *frontend* ]]; then
- echo -e "${CYAN}Running frontend lint...${NC}"
-
- ${dockerCompose} exec -T frontend npm run lint -- ${cache} ${fix}
- frontendExitValue=$?
- fi
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *backend* ]]; then
- echo -e "${CYAN}Running backend lint...${NC}"
-
- ${dockerCompose} exec -T backend npm run lint -- ${cache} ${fix}
- backendExitValue=$?
- fi
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *docs* ]]; then
- echo -e "${CYAN}Running docs lint...${NC}"
-
- ${docker} run --rm -v "${scriptLocation}":/workdir ghcr.io/igorshubovych/markdownlint-cli:latest ".wiki" "*.md" ${fix}
- docsExitValue=$?
- fi
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *shell* ]]; then
- echo -e "${CYAN}Running shell lint...${NC}"
- ${docker} run --rm -v "${scriptLocation}":/mnt koalaman/shellcheck:stable ./*.sh ./**/*.sh
- shellExitValue=$?
- fi
- set -e
- if [[ ${frontendExitValue} -gt 0 || ${backendExitValue} -gt 0 || ${docsExitValue} -gt 0 || ${shellExitValue} -gt 0 ]]; then
- exit 1
- fi
- }
- handleTypescript()
- {
- set +e
-
- services=$(sed "s/\(\ \)\{0,1\}\(-\)\{0,2\}strict//g;t;q1" <<< "${@:2}")
- strictFound=$?
- if [[ $strictFound -eq 0 ]]; then
- strict="--strict"
- echo -e "${GREEN}Strict mode enabled${NC}"
- fi
- set -e
-
- servicesString=$(handleServices "backend frontend" ${services[@]})
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend] [strict]"
- fi
- set +e
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *frontend* ]]; then
- echo -e "${CYAN}Running frontend typescript check...${NC}"
-
- ${dockerCompose} exec -T frontend npm run typescript -- ${strict}
- frontendExitValue=$?
- fi
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *backend* ]]; then
- echo -e "${CYAN}Running backend typescript check...${NC}"
-
- ${dockerCompose} exec -T backend npm run typescript -- ${strict}
- backendExitValue=$?
- fi
- set -e
- if [[ ${frontendExitValue} -gt 0 || ${backendExitValue} -gt 0 ]]; then
- exit 1
- fi
- }
- handleTests()
- {
- servicesString=$(handleServices "backend frontend" "${@:2}")
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend]"
- fi
- set +e
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *backend* ]]; then
- echo -e "${CYAN}Running backend tests...${NC}"
- ${dockerCompose} exec -T backend npm run test
- backendExitValue=$?
- fi
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *frontend* ]]; then
- echo -e "${CYAN}Running frontend tests...${NC}"
- ${dockerCompose} exec -T frontend npm run test -- --run
- frontendExitValue=$?
- fi
- set -e
- if [[ ${backendExitValue} -gt 0 || ${frontendExitValue} -gt 0 ]]; then
- exit 1
- fi
- }
- handleTestCoverage()
- {
- servicesString=$(handleServices "frontend" "${@:2}")
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [frontend]"
- fi
- set +e
- if [[ ${servicesString:2:4} == "all" || "${servicesString:2}" == *frontend* ]]; then
- echo -e "${CYAN}Running frontend test coverage report...${NC}"
- ${dockerCompose} exec -T frontend npm run coverage
- frontendExitValue=$?
- fi
- set -e
- if [[ ${frontendExitValue} -gt 0 ]]; then
- exit 1
- fi
- }
- handleUpdate()
- {
- musareshModified=$(git diff HEAD -- musare.sh)
- git fetch
- updated=$(git log --name-only --oneline HEAD..@\{u\})
- if [[ ${updated} == "" ]]; then
- echo -e "${GREEN}Already up to date${NC}"
- exit 0
- fi
- breakingConfigChange=$(git rev-list "$(git rev-parse HEAD)" | grep d8b73be1de231821db34c677110b7b97e413451f)
- if [[ -f backend/config/default.json && -z $breakingConfigChange ]]; then
- throw "Configuration has breaking changes. Please rename or remove 'backend/config/default.json' and run the update command again to continue."
- fi
- musareshChange=$(echo "${updated}" | grep "musare.sh")
- dbChange=$(echo "${updated}" | grep "backend/logic/db/schemas")
- bcChange=$(echo "${updated}" | grep "backend/config/default.json")
- if [[ ( $2 == "auto" && -z $dbChange && -z $bcChange && -z $musareshChange ) || -z $2 ]]; then
- if [[ -n $musareshChange && $(git diff @\{u\} -- musare.sh) != "" ]]; then
- if [[ $musareshModified != "" ]]; then
- throw "musare.sh has been modified, please reset these changes and run the update command again to continue."
- else
- git checkout @\{u\} -- musare.sh
- throw "${YELLOW}musare.sh has been updated, please run the update command again to continue."
- fi
- else
- git pull
- echo -e "${CYAN}Updating...${NC}"
- runDockerCommand "$(basename "$0") $1" pull
- runDockerCommand "$(basename "$0") $1" build
- runDockerCommand "$(basename "$0") $1" restart
- echo -e "${GREEN}Updated!${NC}"
- if [[ -n $dbChange ]]; then
- echo -e "${RED}Database schema has changed, please run migration!${NC}"
- fi
- if [[ -n $bcChange ]]; then
- echo -e "${RED}Backend config has changed, please update!${NC}"
- fi
- fi
- elif [[ $2 == "auto" ]]; then
- throw "Auto Update Failed! musare.sh, database and/or config has changed!"
- fi
- }
- handleBackup()
- {
- if [[ -z "${BACKUP_LOCATION}" ]]; then
- backupLocation="${scriptLocation%x}/backups"
- else
- backupLocation="${BACKUP_LOCATION%/}"
- fi
- if [[ ! -d "${backupLocation}" ]]; then
- echo -e "${YELLOW}Creating backup directory at ${backupLocation}${NC}"
- mkdir "${backupLocation}"
- fi
- if [[ -z "${BACKUP_NAME}" ]]; then
- backupLocation="${backupLocation}/musare-$(date +"%Y-%m-%d-%s").dump"
- else
- backupLocation="${backupLocation}/${BACKUP_NAME}"
- fi
- echo -e "${YELLOW}Creating backup at ${backupLocation}${NC}"
- ${dockerCompose} exec -T mongo sh -c "mongodump --authenticationDatabase musare -u ${MONGO_USER_USERNAME} -p ${MONGO_USER_PASSWORD} -d musare --archive" > "${backupLocation}"
- }
- handleRestore()
- {
- if [[ -z $2 ]]; then
- echo -e "${GREEN}Please enter the full path of the dump you wish to restore: ${NC}"
- read -r restoreFile
- else
- restoreFile=$2
- fi
- if [[ -z ${restoreFile} ]]; then
- throw "Error: no restore path given, cancelled restoration."
- elif [[ -d ${restoreFile} ]]; then
- throw "Error: restore path given is a directory, cancelled restoration."
- elif [[ ! -f ${restoreFile} ]]; then
- throw "Error: no file at restore path given, cancelled restoration."
- else
- ${dockerCompose} exec -T mongo sh -c "mongorestore --authenticationDatabase musare -u ${MONGO_USER_USERNAME} -p ${MONGO_USER_PASSWORD} --archive" < "${restoreFile}"
- fi
- }
- handleAdmin()
- {
- MONGO_VERSION_INT=${MONGO_VERSION:0:1}
- case $2 in
- add)
- if [[ -z $3 ]]; then
- echo -e "${GREEN}Please enter the username of the user you wish to make an admin: ${NC}"
- read -r adminUser
- else
- adminUser=$3
- fi
- if [[ -z $adminUser ]]; then
- throw "Error: Username for new admin not provided."
- fi
- if [[ $MONGO_VERSION_INT -ge 5 ]]; then
- ${dockerCompose} exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry(); db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'admin'}})"
- else
- ${dockerCompose} exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'admin'}})"
- fi
- ;;
- remove)
- if [[ -z $3 ]]; then
- echo -e "${GREEN}Please enter the username of the user you wish to remove as admin: ${NC}"
- read -r adminUser
- else
- adminUser=$3
- fi
- if [[ -z $adminUser ]]; then
- throw "Error: Username for new admin not provided."
- fi
- if [[ $MONGO_VERSION_INT -ge 5 ]]; then
- ${dockerCompose} exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry(); db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'default'}})"
- else
- ${dockerCompose} exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'default'}})"
- fi
- ;;
- *)
- throw "Invalid command $2\n${YELLOW}Usage: ${1} [add,remove] username"
- ;;
- esac
- }
- handleExecute()
- {
- servicesString=$(handleServices "backend frontend mongo postgres redis" "${2}")
- if [[ ${servicesString:0:1} != 1 ]]; then
- throw "${servicesString:2}\n${YELLOW}Usage: ${1} [backend, frontend, mongo, postgres, redis]"
- fi
-
- ${dockerCompose} exec "${2}" ${@:3}
- }
- availableCommands=$(cat << COMMANDS
- start - Start services
- stop - Stop services
- restart - Restart services
- status - Service status
- logs - View logs for services
- update - Update Musare
- attach [backend,mongo,postgres,redis] - Attach to backend service, mongo, postgres or redis shell
- build - Build services
- lint - Run lint on frontend, backend, docs and/or shell
- backup - Backup database data to file
- restore - Restore database data from backup file
- reset - Reset service data
- admin [add,remove] - Assign/unassign admin role to/from a user
- typescript - Run typescript checks on frontend and/or backend
- execute - Execute command in service docker container
- COMMANDS
- )
- case $1 in
- start)
- echo -e "${CYAN}Musare | Start Services${NC}"
-
- runDockerCommand "$(basename "$0") $1" start ${@:2}
- ;;
- stop)
- echo -e "${CYAN}Musare | Stop Services${NC}"
-
- runDockerCommand "$(basename "$0") $1" stop ${@:2}
- ;;
- restart)
- echo -e "${CYAN}Musare | Restart Services${NC}"
-
- runDockerCommand "$(basename "$0") $1" restart ${@:2}
- ;;
- build)
- echo -e "${CYAN}Musare | Build Services${NC}"
-
- runDockerCommand "$(basename "$0") $1" pull ${@:2}
-
- runDockerCommand "$(basename "$0") $1" build ${@:2}
- ;;
- status)
- echo -e "${CYAN}Musare | Service Status${NC}"
-
- runDockerCommand "$(basename "$0") $1" ps ${@:2}
- ;;
- reset)
- echo -e "${CYAN}Musare | Reset Services${NC}"
-
- handleReset "$(basename "$0") $1" ${@:2}
- ;;
- attach)
- echo -e "${CYAN}Musare | Attach${NC}"
- attachContainer "$(basename "$0") $1" "$2"
- ;;
- lint|eslint)
- echo -e "${CYAN}Musare | Lint${NC}"
-
- handleLinting "$(basename "$0") $1" ${@:2}
- ;;
- typescript|ts)
- echo -e "${CYAN}Musare | TypeScript Check${NC}"
-
- handleTypescript "$(basename "$0") $1" ${@:2}
- ;;
- test)
- echo -e "${CYAN}Musare | Test${NC}"
-
- handleTests "$(basename "$0") $1" ${@:2}
- ;;
- test:coverage)
- echo -e "${CYAN}Musare | Test Coverage${NC}"
-
- handleTestCoverage "$(basename "$0") $1" ${@:2}
- ;;
- update)
- echo -e "${CYAN}Musare | Update${NC}"
-
- handleUpdate "$(basename "$0") $1" ${@:2}
- ;;
- logs)
- echo -e "${CYAN}Musare | Logs${NC}"
-
- runDockerCommand "$(basename "$0") $1" logs ${@:2}
- ;;
- backup)
- echo -e "${CYAN}Musare | Backup${NC}"
-
- handleBackup "$(basename "$0") $1" ${@:2}
- ;;
- restore)
- echo -e "${CYAN}Musare | Restore${NC}"
-
- handleRestore "$(basename "$0") $1" ${@:2}
- ;;
- admin)
- echo -e "${CYAN}Musare | Add Admin${NC}"
-
- handleAdmin "$(basename "$0") $1" ${@:2}
- ;;
- execute|exec)
- echo -e "${CYAN}Musare | Execute${NC}"
-
- handleExecute "$(basename "$0") $1" ${@:2}
- ;;
- "")
- echo -e "${CYAN}Musare | Available Commands${NC}"
- echo -e "${YELLOW}${availableCommands}${NC}"
- ;;
- *)
- echo -e "${CYAN}Musare${NC}"
- echo -e "${RED}Error: Invalid Command $1${NC}"
- echo -e "${CYAN}Available Commands:${NC}"
- echo -e "${YELLOW}${availableCommands}${NC}"
- exit 1
- ;;
- esac
|