forked from michael.heier/citadel-core
Compare commits
3 Commits
release/0.
...
stable
Author | SHA1 | Date | |
---|---|---|---|
|
61f5f9f1e0 | ||
|
07aa73b59e | ||
|
a8d224f597 |
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -22,7 +22,7 @@ statuses/*
|
|||
app-data
|
||||
apps
|
||||
electrs/*
|
||||
fulcrumx/*
|
||||
fulcrum/*
|
||||
nginx/*
|
||||
redis/*
|
||||
docker-compose.override.yml
|
||||
|
@ -43,7 +43,7 @@ db/citadel-seed/*
|
|||
!db/.gitkeep
|
||||
!nginx/.gitkeep
|
||||
!redis/.gitkeep
|
||||
!fulcrumx/.gitkeep
|
||||
!fulcrum/.gitkeep
|
||||
|
||||
!**/*.license
|
||||
services/installed.json
|
||||
|
|
|
@ -118,26 +118,6 @@ def getUserData():
|
|||
userData = json.load(f)
|
||||
return userData
|
||||
|
||||
def checkUpdateAvailable(name: str) -> bool:
|
||||
latestAppYml = yaml.safe_load(getAppYml(name))
|
||||
with open(os.path.join(appsDir, name, "app.yml"), "r") as f:
|
||||
originalAppYml = yaml.safe_load(f)
|
||||
if not "metadata" in latestAppYml or not "version" in latestAppYml["metadata"] or not "metadata" in originalAppYml or not "version" in originalAppYml["metadata"]:
|
||||
print("App {} is not valid".format(name))
|
||||
return False
|
||||
return semver.compare(latestAppYml["metadata"]["version"], originalAppYml["metadata"]["version"]) > 0
|
||||
|
||||
def getAvailableUpdates():
|
||||
availableUpdates = []
|
||||
apps = findAndValidateApps(appsDir)
|
||||
for app in apps:
|
||||
try:
|
||||
if checkUpdateAvailable(app):
|
||||
availableUpdates.append(app)
|
||||
except Exception:
|
||||
print("Warning: Can't check app {} yet".format(app), file=sys.stderr)
|
||||
return availableUpdates
|
||||
|
||||
def startInstalled():
|
||||
# If userfile doesn't exist, just do nothing
|
||||
userData = {}
|
||||
|
@ -364,3 +344,59 @@ def updateRepos():
|
|||
shutil.rmtree(tempDir)
|
||||
with open(os.path.join(appsDir, "sourceMap.json"), "w") as f:
|
||||
json.dump(sourceMap, f)
|
||||
|
||||
|
||||
def getAvailableUpdates():
|
||||
availableUpdates = {}
|
||||
repos = []
|
||||
ignoreApps = []
|
||||
with open(sourcesList) as f:
|
||||
repos = f.readlines()
|
||||
try:
|
||||
with open(updateIgnore) as f:
|
||||
ignoreApps = f.readlines()
|
||||
except: pass
|
||||
# For each repo, clone the repo to a temporary dir, checkout the branch,
|
||||
# and overwrite the current app dir with the contents of the temporary dir/apps/app
|
||||
# Set this to ignoreApps. Normally, it keeps track of apps already installed from repos higher in the list,
|
||||
# but apps specified in updateignore have the highest priority
|
||||
alreadyDefined = [s.strip() for s in ignoreApps]
|
||||
for repo in repos:
|
||||
repo = repo.strip()
|
||||
if repo == "":
|
||||
continue
|
||||
# Also ignore comments
|
||||
if repo.startswith("#"):
|
||||
continue
|
||||
# Split the repo into the git url and the branch
|
||||
repo = repo.split(" ")
|
||||
if len(repo) != 2:
|
||||
print("Error: Invalid repo format in " + sourcesList, file=sys.stderr)
|
||||
exit(1)
|
||||
gitUrl = repo[0]
|
||||
branch = repo[1]
|
||||
# Clone the repo to a temporary dir
|
||||
tempDir = tempfile.mkdtemp()
|
||||
# Git clone with a depth of 1 to avoid cloning the entire repo
|
||||
# Don't print anything to stdout, as we don't want to see the git clone output
|
||||
subprocess.run("git clone --depth 1 --branch {} {} {}".format(branch, gitUrl, tempDir), shell=True, stdout=subprocess.DEVNULL)
|
||||
# Overwrite the current app dir with the contents of the temporary dir/apps/app
|
||||
for app in os.listdir(os.path.join(tempDir, "apps")):
|
||||
try:
|
||||
# if the app is already installed (or a simple file instead of a valid app), skip it
|
||||
if app in alreadyDefined or not os.path.isdir(os.path.join(tempDir, "apps", app)):
|
||||
continue
|
||||
with open(os.path.join(appsDir, app, "app.yml"), "r") as f:
|
||||
originalAppYml = yaml.safe_load(f)
|
||||
with open(os.path.join(tempDir, "apps", app, "app.yml"), "r") as f:
|
||||
latestAppYml = yaml.safe_load(f)
|
||||
if semver.compare(latestAppYml["metadata"]["version"], originalAppYml["metadata"]["version"]) > 0:
|
||||
availableUpdates[app] = {
|
||||
"updateFrom": originalAppYml["metadata"]["version"],
|
||||
"updateTo": latestAppYml["metadata"]["version"]
|
||||
}
|
||||
except Exception:
|
||||
print("Warning: Can't check app {} (yet)".format(app), file=sys.stderr)
|
||||
# Remove the temporary dir
|
||||
shutil.rmtree(tempDir)
|
||||
return availableUpdates
|
||||
|
|
1
bin/citadel
Symbolic link
1
bin/citadel
Symbolic link
|
@ -0,0 +1 @@
|
|||
../cli/citadel
|
487
cli/citadel
Executable file
487
cli/citadel
Executable file
|
@ -0,0 +1,487 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 Citadel and contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
|
||||
CLI_NAME="$(basename $0)"
|
||||
CLI_VERSION="0.0.1"
|
||||
CLI_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
SERVICE_NAME="citadel-startup"
|
||||
EDITOR="${EDITOR:-micro}"
|
||||
|
||||
source $CLI_DIR/utils/functions.sh
|
||||
source $CLI_DIR/utils/multiselect.sh
|
||||
source $CLI_DIR/utils/spinner.sh
|
||||
source $CLI_DIR/utils/helpers.sh
|
||||
|
||||
if [ -z ${1+x} ]; then
|
||||
command=""
|
||||
else
|
||||
command="$1"
|
||||
fi
|
||||
|
||||
# Check Citadel Status
|
||||
if [[ "$command" = "status" ]]; then
|
||||
POSITIONAL_ARGS=()
|
||||
|
||||
long=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-l | --long)
|
||||
long=true
|
||||
shift
|
||||
;;
|
||||
-* | --*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- "${POSITIONAL_ARGS[@]}"
|
||||
|
||||
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
|
||||
df -h | awk '$NF=="/"{printf "Disk Usage: %d/%dGB (%s)\n", $3,$2,$5}'
|
||||
top -bn1 | grep load | awk '{printf "CPU Load: %.2f\n", $(NF-2)}'
|
||||
|
||||
echo
|
||||
|
||||
if $long; then
|
||||
docker container ls --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
|
||||
else
|
||||
docker container ls --format "table {{.Names}}\t{{.Status}}"
|
||||
fi
|
||||
|
||||
if [[ $(pgrep -f karen) ]]; then
|
||||
printf "\nKaren is listening.\n"
|
||||
else
|
||||
printf "\nERROR: Karen is not listening.\n"
|
||||
fi
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
# Update Citadel
|
||||
if [[ "$command" = "update" ]]; then
|
||||
POSITIONAL_ARGS=()
|
||||
|
||||
branch=$(get_update_channel)
|
||||
force=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-b | --branch)
|
||||
branch="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--force)
|
||||
force=true
|
||||
shift # past argument
|
||||
;;
|
||||
-* | --*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL_ARGS+=("$1") # save positional arg
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
||||
|
||||
if $force; then
|
||||
sudo rm -f $CITADEL_ROOT/statuses/update-in-progress
|
||||
fi
|
||||
|
||||
sudo $CITADEL_ROOT/scripts/update/update --repo runcitadel/core#$branch
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
# Start Citadel
|
||||
if [[ "$command" = "start" ]]; then
|
||||
if $(is_managed_by_systemd); then
|
||||
if $(is_service_active); then
|
||||
echo 'Citadel is already running.'
|
||||
else
|
||||
sudo systemctl start citadel-startup
|
||||
fi
|
||||
else
|
||||
sudo $CITADEL_ROOT/scripts/start
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
# Stop Citadel
|
||||
if [[ "$command" = "stop" ]]; then
|
||||
active=$(is_service_active)
|
||||
|
||||
if $(is_managed_by_systemd) && $active; then
|
||||
if $(is_service_active); then
|
||||
sudo systemctl stop citadel-startup
|
||||
else
|
||||
echo 'Citadel is not running.'
|
||||
fi
|
||||
else
|
||||
sudo $CITADEL_ROOT/scripts/stop
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
# Restart Citadel
|
||||
if [[ "$command" = "restart" ]]; then
|
||||
shift
|
||||
|
||||
# TODO: enable restarting services
|
||||
|
||||
if [ ! -z ${1+x} ]; then
|
||||
echo "Too many arguments."
|
||||
echo "Usage: \`$CLI_NAME $command\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if $(is_managed_by_systemd); then
|
||||
sudo systemctl restart $SERVICE_NAME
|
||||
else
|
||||
sudo $CITADEL_ROOT/scripts/stop
|
||||
sudo $CITADEL_ROOT/scripts/start
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
# Reboot the system
|
||||
if [[ "$command" = "reboot" ]]; then
|
||||
$CLI_NAME stop || true
|
||||
sudo reboot
|
||||
exit
|
||||
fi
|
||||
|
||||
# Shutdown the system
|
||||
if [[ "$command" = "shutdown" ]]; then
|
||||
$CLI_NAME stop || true
|
||||
sudo shutdown now
|
||||
exit
|
||||
fi
|
||||
|
||||
# List all installed services apps
|
||||
if [[ "$command" = "list" ]]; then
|
||||
echo 'karen'
|
||||
docker ps --format "{{.Names}}"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Run a command inside a container
|
||||
if [[ "$command" = "run" ]]; then
|
||||
shift
|
||||
|
||||
if [ -z ${1+x} ]; then
|
||||
echo "Specify an app or service."
|
||||
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z ${2+x} ]; then
|
||||
echo "Specify a command to run."
|
||||
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker exec -t $1 sh -c "$2" || {
|
||||
echo "To see all installed services & apps use \`$CLI_NAME list\`"
|
||||
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
# Configure Citadel
|
||||
if [[ "$command" = "set" ]]; then
|
||||
shift
|
||||
|
||||
if [ -z ${1+x} ]; then
|
||||
echo "Missing subcommand."
|
||||
echo "Usage: \`$CLI_NAME $command <subcommand>\`"
|
||||
exit 1
|
||||
else
|
||||
subcommand="$1"
|
||||
fi
|
||||
|
||||
# Switch update channel
|
||||
if [[ "$subcommand" = "update-channel" ]]; then
|
||||
if [ -z ${2+x} ]; then
|
||||
echo "Specify an update channel to switch to."
|
||||
echo "Usage: \`$CLI_NAME $subcommand <stable|beta|c-lightning>\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $2 in "stable" | "beta" | "c-lightning")
|
||||
# continue
|
||||
;;
|
||||
*)
|
||||
echo "Not a valid update channel: \"$2\""
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
sudo $CITADEL_ROOT/scripts/set-update-channel $2
|
||||
$CLI_NAME update
|
||||
exit
|
||||
fi
|
||||
|
||||
# Switch Bitcoin/Electrum implementation
|
||||
if [[ "$subcommand" = "implementation" ]] || [[ "$subcommand" = "impl" ]]; then
|
||||
shift
|
||||
sudo $CITADEL_ROOT/services/manage.py set $@
|
||||
$CLI_NAME restart
|
||||
exit
|
||||
fi
|
||||
|
||||
# Switch Bitcoin network
|
||||
if [[ "$subcommand" = "network" ]]; then
|
||||
shift
|
||||
|
||||
if [ -z ${1+x} ]; then
|
||||
echo "Specify a network to switch to."
|
||||
echo "Usage: \`$CLI_NAME $subcommand <mainnet|signet|testnet|regtest>\`"
|
||||
else
|
||||
case $1 in
|
||||
"mainnet" | "testnet" | "signet" | "regtest")
|
||||
sudo $CITADEL_ROOT/scripts/stop
|
||||
sudo OVERWRITE_NETWORK=$1 $CITADEL_ROOT/scripts/configure
|
||||
sudo $CITADEL_ROOT/scripts/start
|
||||
;;
|
||||
*)
|
||||
echo "Not a valid value for network"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "\"$subcommand\" is not a valid subcommand."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# App commands
|
||||
if [[ "$command" = "app" ]]; then
|
||||
shift
|
||||
sudo $CITADEL_ROOT/scripts/app $@
|
||||
exit
|
||||
fi
|
||||
|
||||
# Edit common app configuration files
|
||||
if [[ "$command" = "configure" ]]; then
|
||||
if [ -z ${2+x} ]; then
|
||||
echo "Specify an app or service to configure."
|
||||
echo "Usage: \`$CLI_NAME $command <service>\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POSITIONAL_ARGS=()
|
||||
|
||||
persist=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--persist)
|
||||
persist=true
|
||||
shift # past argument
|
||||
;;
|
||||
-* | --*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL_ARGS+=("$1") # save positional arg
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
||||
|
||||
# These service and app configs are already persisted
|
||||
# TODO: add more apps
|
||||
|
||||
if [[ "$2" = "nextcloud" ]]; then
|
||||
edit_file --priviledged $CITADEL_ROOT/app-data/nextcloud/data/nextcloud/config/config.php
|
||||
prompt_apply_config nextcloud-web-1 false
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$2" = "nginx" ]]; then
|
||||
edit_file $CITADEL_ROOT/nginx/nginx.conf
|
||||
prompt_apply_config nginx false
|
||||
exit
|
||||
fi
|
||||
|
||||
if $persist; then
|
||||
echo "NOTE: As of now persisted config changes will not be kept when updating Citadel."
|
||||
else
|
||||
echo "NOTE: Some changes to this configuration file may be overwritten the next time you start Citadel."
|
||||
echo "To persist the changes run the command again with \`$CLI_NAME configure $2 --persist\`"
|
||||
fi
|
||||
|
||||
read -p "Continue? [Y/n] " should_continue
|
||||
echo
|
||||
if [[ $should_continue =~ [Nn]$ ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
# Service and app configs below may be overwritten
|
||||
# TODO: check which implementation is running
|
||||
# and do "bitcoin" / "lightning" / "electrum"
|
||||
|
||||
if [[ "$2" = "bitcoin" ]]; then
|
||||
if $persist; then
|
||||
edit_file $CITADEL_ROOT/templates/bitcoin-sample.conf
|
||||
prompt_apply_config bitcoin true
|
||||
else
|
||||
edit_file $CITADEL_ROOT/bitcoin/bitcoin.conf
|
||||
prompt_apply_config bitcoin false
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$2" = "lnd" ]]; then
|
||||
if $persist; then
|
||||
edit_file $CITADEL_ROOT/templates/lnd-sample.conf
|
||||
prompt_apply_config lightning true
|
||||
else
|
||||
edit_file $CITADEL_ROOT/lnd/lnd.conf
|
||||
prompt_apply_config lightning false
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$2" = "electrs" ]]; then
|
||||
edit_file $CITADEL_ROOT/templates/electrs-sample.toml
|
||||
prompt_apply_config electrum true
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$2" = "fulcrum" ]]; then
|
||||
if $persist; then
|
||||
edit_file $CITADEL_ROOT/templates/fulcrum-sample.conf
|
||||
prompt_apply_config electrum true
|
||||
else
|
||||
edit_file $CITADEL_ROOT/fulcrum/fulcrum.conf
|
||||
prompt_apply_config electrum false
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "No service or app \"$2\" not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Show logs for apps & services
|
||||
if [[ "$command" = "logs" ]]; then
|
||||
shift
|
||||
|
||||
POSITIONAL_ARGS=()
|
||||
|
||||
follow=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-f | --follow)
|
||||
follow=true
|
||||
shift # past argument
|
||||
;;
|
||||
-n | --tail)
|
||||
number_of_lines="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
-* | --*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL_ARGS+=("$1") # save positional arg
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
||||
|
||||
# Set default number_of_lines if not set by user
|
||||
if [ -z ${number_of_lines+x} ]; then
|
||||
if [[ ${#POSITIONAL_ARGS[@]} == 0 ]] || [[ ${#POSITIONAL_ARGS[@]} == 1 ]]; then
|
||||
number_of_lines=40
|
||||
else
|
||||
number_of_lines=10
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z ${1+x} ] || [[ "$1" = "karen" ]]; then
|
||||
if [[ ${#POSITIONAL_ARGS[@]} == 2 ]]; then
|
||||
echo "Karen logs cannot be viewed together with other services."
|
||||
echo "Usage: \`$CLI_NAME $command karen\`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tail $($follow && echo "-f") -n $number_of_lines $CITADEL_ROOT/logs/karen.log
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ${#POSITIONAL_ARGS[@]} == 1 ]]; then
|
||||
docker logs $($follow && echo "-f") --tail $number_of_lines $@ || {
|
||||
echo "To see all installed services & apps use \`$CLI_NAME list\`"
|
||||
echo "Usage: \`$CLI_NAME $command <service>\`"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
# TODO: can only show logs for services in docker-compose.yml
|
||||
docker compose logs $($follow && echo "-f") --tail $number_of_lines $@ || {
|
||||
echo "To see all installed services & apps use \`$CLI_NAME list\`"
|
||||
echo "Usage: \`$CLI_NAME $command <service>\`"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
# Debug Citadel
|
||||
if [[ "$command" = "debug" ]]; then
|
||||
shift
|
||||
sudo $CITADEL_ROOT/scripts/debug $@
|
||||
exit
|
||||
fi
|
||||
|
||||
# Show version information for this CLI
|
||||
if [[ "$command" = "--version" ]] || [[ "$command" = "-v" ]]; then
|
||||
citadel_version=$(jq -r '.version' $CITADEL_ROOT/info.json)
|
||||
echo "Citadel v$citadel_version"
|
||||
echo "citadel-cli v$CLI_VERSION"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Show usage information for this CLI
|
||||
if [[ "$command" = "--help" ]] || [[ "$command" = "-h" ]]; then
|
||||
show_help
|
||||
exit
|
||||
fi
|
||||
|
||||
# If we get here it means no valid command was supplied
|
||||
# Show help and exit
|
||||
show_help
|
||||
exit
|
111
cli/utils/functions.sh
Normal file
111
cli/utils/functions.sh
Normal file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
show_help() {
|
||||
cat <<EOF
|
||||
${CLI_NAME}-cli v${CLI_VERSION}
|
||||
Manage your Citadel.
|
||||
|
||||
Usage: ${CLI_NAME} <command> [options]
|
||||
|
||||
Flags:
|
||||
-h, --help Show this help message
|
||||
-v, --version Show version information for this CLI
|
||||
|
||||
Commands:
|
||||
status Check the status of all services
|
||||
start Start the Citadel service
|
||||
stop Stop the Citadel service safely
|
||||
restart Restart the Citadel service
|
||||
reboot Reboot the system
|
||||
shutdown Shutdown the system
|
||||
update Update Citadel
|
||||
list List all installed services apps
|
||||
run <service> "<command>" Run a command inside a container
|
||||
set <command> Switch between Bitcoin & Lightning implementations
|
||||
app <command> Install, update or restart apps
|
||||
configure <service> Edit service & app configuration files
|
||||
logs <service> Show logs for an app or service
|
||||
debug View logs for troubleshooting
|
||||
EOF
|
||||
}
|
||||
|
||||
is_managed_by_systemd() {
|
||||
if systemctl --all --type service | grep -q "$SERVICE_NAME"; then
|
||||
echo true
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
}
|
||||
|
||||
is_service_active() {
|
||||
service_status=$(systemctl is-active $SERVICE_NAME)
|
||||
if [[ "$service_status" = "active" ]]; then
|
||||
echo true
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
}
|
||||
|
||||
edit_file() {
|
||||
if [[ $1 = "--priviledged" ]]; then
|
||||
echo "Editing this file requires elevated priviledges."
|
||||
|
||||
if ! sudo test -f $2; then
|
||||
echo "File not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if sudo test -w $2; then
|
||||
sudo $EDITOR $2
|
||||
else
|
||||
echo "File not writable."
|
||||
fi
|
||||
else
|
||||
if ! test -f $1; then
|
||||
echo "File not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -w $1; then
|
||||
$EDITOR $1
|
||||
else
|
||||
echo "File not writable."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_update_channel() {
|
||||
update_channel_line=$(cat $CITADEL_ROOT/.env | grep UPDATE_CHANNEL)
|
||||
update_channel=(${update_channel_line//=/ })
|
||||
|
||||
if [ -z ${update_channel[1]+x} ]; then
|
||||
# fall back to stable
|
||||
echo "stable"
|
||||
else
|
||||
echo ${update_channel[1]}
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_apply_config() {
|
||||
service=$1
|
||||
persisted=$2
|
||||
|
||||
read -p "Do you want to apply the changes now? [y/N] " should_restart
|
||||
echo
|
||||
if [[ $should_restart =~ [Yy]$ ]]; then
|
||||
if $persisted; then
|
||||
sudo $CITADEL_ROOT/scripts/configure
|
||||
fi
|
||||
|
||||
printf "\nRestarting service \"$service\"...\n"
|
||||
docker restart $service
|
||||
echo "Done."
|
||||
else
|
||||
if $persisted; then
|
||||
echo "To apply the changes, restart Citadel by running \`$CLI_NAME restart\`."
|
||||
else
|
||||
echo "To apply the changes, restart service \"$service\" by running \`docker restart $service\`."
|
||||
fi
|
||||
fi
|
||||
}
|
11
cli/utils/helpers.sh
Normal file
11
cli/utils/helpers.sh
Normal file
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
trim() {
|
||||
local var="$*"
|
||||
# remove leading whitespace characters
|
||||
var="${var#"${var%%[![:space:]]*}"}"
|
||||
# remove trailing whitespace characters
|
||||
var="${var%"${var##*[![:space:]]}"}"
|
||||
printf '%s' "$var"
|
||||
}
|
123
cli/utils/multiselect.sh
Normal file
123
cli/utils/multiselect.sh
Normal file
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
function multiselect() {
|
||||
# little helpers for terminal print control and key input
|
||||
ESC=$(printf "\033")
|
||||
cursor_blink_on() { printf "$ESC[?25h"; }
|
||||
cursor_blink_off() { printf "$ESC[?25l"; }
|
||||
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
|
||||
print_inactive() { printf "$2 $1 "; }
|
||||
print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; }
|
||||
get_cursor_row() {
|
||||
IFS=';' read -sdR -p $'\E[6n' ROW COL
|
||||
echo ${ROW#*[}
|
||||
}
|
||||
|
||||
local return_value=$1
|
||||
local -n options=$2
|
||||
local -n defaults=$3
|
||||
|
||||
local selected=()
|
||||
for ((i = 0; i < ${#options[@]}; i++)); do
|
||||
if [[ ${defaults[i]} = "true" ]]; then
|
||||
selected+=("true")
|
||||
else
|
||||
selected+=("false")
|
||||
fi
|
||||
printf "\n"
|
||||
done
|
||||
|
||||
# determine current screen position for overwriting the options
|
||||
local lastrow=$(get_cursor_row)
|
||||
local startrow=$(($lastrow - ${#options[@]}))
|
||||
|
||||
# ensure cursor and input echoing back on upon a ctrl+c during read -s
|
||||
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
|
||||
cursor_blink_off
|
||||
|
||||
key_input() {
|
||||
local key
|
||||
IFS= read -rsn1 key 2>/dev/null >&2
|
||||
if [[ $key = "" ]]; then echo enter; fi
|
||||
if [[ $key = $'\x20' ]]; then echo space; fi
|
||||
if [[ $key = "k" ]]; then echo up; fi
|
||||
if [[ $key = "j" ]]; then echo down; fi
|
||||
if [[ $key = $'\x1b' ]]; then
|
||||
read -rsn2 key
|
||||
if [[ $key = [A || $key = k ]]; then echo up; fi
|
||||
if [[ $key = [B || $key = j ]]; then echo down; fi
|
||||
fi
|
||||
}
|
||||
|
||||
toggle_option() {
|
||||
local option=$1
|
||||
if [[ ${selected[option]} == true ]]; then
|
||||
selected[option]=false
|
||||
else
|
||||
selected[option]=true
|
||||
fi
|
||||
}
|
||||
|
||||
print_options() {
|
||||
# print options by overwriting the last lines
|
||||
local idx=0
|
||||
|
||||
for option in "${options[@]}"; do
|
||||
local prefix="[ ]"
|
||||
|
||||
if [[ ${selected[idx]} == true ]]; then
|
||||
prefix="[\e[38;5;46m✔\e[0m]"
|
||||
fi
|
||||
|
||||
cursor_to $(($startrow + $idx))
|
||||
if [ $idx -eq $1 ]; then
|
||||
print_active "$option" "$prefix"
|
||||
else
|
||||
print_inactive "$option" "$prefix"
|
||||
fi
|
||||
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
}
|
||||
|
||||
get_result() {
|
||||
local result=()
|
||||
for ((i = 0; i < ${#options[@]}; i++)); do
|
||||
if ${selected[i]}; then
|
||||
result+=(${options[i]})
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${result[@]}"
|
||||
}
|
||||
|
||||
local active=0
|
||||
while true; do
|
||||
print_options $active
|
||||
|
||||
# user key control
|
||||
case $(key_input) in
|
||||
space) toggle_option $active ;;
|
||||
enter)
|
||||
print_options -1
|
||||
break
|
||||
;;
|
||||
up)
|
||||
active=$((active - 1))
|
||||
if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi
|
||||
;;
|
||||
down)
|
||||
active=$((active + 1))
|
||||
if [ $active -ge ${#options[@]} ]; then active=0; fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# cursor position back to normal
|
||||
cursor_to $lastrow
|
||||
printf "\n"
|
||||
cursor_blink_on
|
||||
|
||||
eval $return_value='("$(get_result)")'
|
||||
}
|
87
cli/utils/spinner.sh
Normal file
87
cli/utils/spinner.sh
Normal file
|
@ -0,0 +1,87 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Author: Tasos Latsas
|
||||
|
||||
# spinner.sh
|
||||
#
|
||||
# Display an awesome 'spinner' while running your long shell commands
|
||||
#
|
||||
# Do *NOT* call _spinner function directly.
|
||||
# Use {start,stop}_spinner wrapper functions
|
||||
|
||||
# usage:
|
||||
# 1. source this script in your's
|
||||
# 2. start the spinner:
|
||||
# start_spinner [display-message-here]
|
||||
# 3. run your command
|
||||
# 4. stop the spinner:
|
||||
# stop_spinner [your command's exit status]
|
||||
#
|
||||
# Also see: test.sh
|
||||
|
||||
function _spinner() {
|
||||
# $1 start/stop
|
||||
#
|
||||
# on start: $2 display message
|
||||
# on stop : $2 process exit status
|
||||
# $3 spinner function pid (supplied from stop_spinner)
|
||||
|
||||
local on_success="DONE"
|
||||
local on_fail="FAIL"
|
||||
local white="\e[1;37m"
|
||||
local green="\e[1;32m"
|
||||
local red="\e[1;31m"
|
||||
local nc="\e[0m"
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
# display message with some space
|
||||
echo -ne "${2} "
|
||||
|
||||
# start spinner
|
||||
i=1
|
||||
sp='\|/-'
|
||||
delay=${SPINNER_DELAY:-0.15}
|
||||
|
||||
while :; do
|
||||
printf "\b${sp:i++%${#sp}:1}"
|
||||
sleep $delay
|
||||
done
|
||||
;;
|
||||
stop)
|
||||
if [[ -z ${3} ]]; then
|
||||
echo "spinner is not running.."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kill $3 >/dev/null 2>&1
|
||||
|
||||
# inform the user upon success or failure
|
||||
echo -en "\b["
|
||||
if [[ $2 -eq 0 ]]; then
|
||||
echo -en "${green}${on_success}${nc}"
|
||||
else
|
||||
echo -en "${red}${on_fail}${nc}"
|
||||
fi
|
||||
echo -e "]"
|
||||
;;
|
||||
*)
|
||||
echo "invalid argument, try {start/stop}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function start_spinner() {
|
||||
# $1 : msg to display
|
||||
_spinner "start" "${1}" &
|
||||
# set global spinner pid
|
||||
_sp_pid=$!
|
||||
disown
|
||||
}
|
||||
|
||||
function stop_spinner() {
|
||||
# $1 : command exit status
|
||||
_spinner "stop" $1 $_sp_pid
|
||||
unset _sp_pid
|
||||
}
|
|
@ -2,7 +2,7 @@ version: '3.8'
|
|||
services:
|
||||
tor:
|
||||
container_name: tor
|
||||
image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
|
||||
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b
|
||||
user: toruser
|
||||
restart: on-failure
|
||||
volumes:
|
||||
|
@ -15,7 +15,7 @@ services:
|
|||
ipv4_address: $TOR_PROXY_IP
|
||||
app-tor:
|
||||
container_name: app-tor
|
||||
image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
|
||||
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b
|
||||
user: toruser
|
||||
restart: on-failure
|
||||
volumes:
|
||||
|
@ -26,7 +26,7 @@ services:
|
|||
ipv4_address: $APPS_TOR_IP
|
||||
app-2-tor:
|
||||
container_name: app-2-tor
|
||||
image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
|
||||
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b
|
||||
user: toruser
|
||||
restart: on-failure
|
||||
volumes:
|
||||
|
@ -37,7 +37,7 @@ services:
|
|||
ipv4_address: $APPS_2_TOR_IP
|
||||
app-3-tor:
|
||||
container_name: app-3-tor
|
||||
image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
|
||||
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b
|
||||
user: toruser
|
||||
restart: on-failure
|
||||
volumes:
|
||||
|
@ -65,7 +65,7 @@ services:
|
|||
ipv4_address: $NGINX_IP
|
||||
bitcoin:
|
||||
container_name: bitcoin
|
||||
image: nolim1t/bitcoinknots:v22.0.knots20211108@sha256:a475da2b2ecda55fcc65ea23e1a36c58b2c10549f1c3d3bb3c31c7dda1127354
|
||||
image: ghcr.io/runcitadel/bitcoinknots:main@sha256:5fbee0f6f0d09d42aacc11c373ffe6162210c42ce21e6eba294e547e3ad80219
|
||||
depends_on:
|
||||
- tor
|
||||
volumes:
|
||||
|
@ -79,7 +79,7 @@ services:
|
|||
ipv4_address: $BITCOIN_IP
|
||||
lightning:
|
||||
container_name: lightning
|
||||
image: lightninglabs/lnd:v0.14.3-beta@sha256:6a2234b0aad4caed3d993736816b198d6228f32c59b27ba2218d5ebf516ae905
|
||||
image: lightninglabs/lnd:v0.15.0-beta@sha256:d227a9db0727ff56020c8d6604c8c369757123d238ab6ce679579c2dd0d0d259
|
||||
user: 1000:1000
|
||||
depends_on:
|
||||
- tor
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"version": "0.0.5",
|
||||
"name": "Citadel 0.0.5",
|
||||
"version": "0.0.7",
|
||||
"name": "Citadel 0.0.7",
|
||||
"requires": ">=0.0.1",
|
||||
"notes": "This update fixes a few bugs in the 0.0.4 release that were preventing some apps from working correctly."
|
||||
"notes": "While we are busy with the next huge update, you may need to wait longer for updates. This update updates Bitcoin Knots and LND to their latest versions to ensure apps can utilize their latest features. In addition, this update includes the Citadel CLI. More information on that will be published soon."
|
||||
}
|
||||
|
|
2
scripts/configure
vendored
2
scripts/configure
vendored
|
@ -334,7 +334,7 @@ templates_to_build = {
|
|||
"./templates/bitcoin-sample.conf": "./bitcoin/bitcoin.conf",
|
||||
"./templates/.env-sample": "./.env",
|
||||
"./templates/electrs-sample.toml": "./electrs/electrs.toml",
|
||||
"./templates/fulcrumx-sample.conf": "./fulcrumx/fulcrumx.conf",
|
||||
"./templates/fulcrum-sample.conf": "./fulcrum/fulcrum.conf",
|
||||
"./templates/nginx-sample.conf": "./nginx/nginx.conf"
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ pkill -f ./scripts/status-monitor || true
|
|||
./scripts/status-monitor storage 60 &>> "${CITADEL_LOGS}/status-monitor.log" &
|
||||
./scripts/status-monitor temperature 15 &>> "${CITADEL_LOGS}/status-monitor.log" &
|
||||
./scripts/status-monitor uptime 15 &>> "${CITADEL_LOGS}/status-monitor.log" &
|
||||
./scripts/status-monitor app-updates 300 &>> "${CITADEL_LOGS}/status-monitor.log" &
|
||||
./scripts/status-monitor app-updates 600 &>> "${CITADEL_LOGS}/status-monitor.log" &
|
||||
|
||||
echo "Starting backup monitor..."
|
||||
echo
|
||||
|
|
|
@ -12,4 +12,4 @@ nginx/*
|
|||
services/installed.yml
|
||||
apps/sourceMap.json
|
||||
apps/.updateignore
|
||||
fulcrumx/*
|
||||
fulcrum/*
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
bitcoin:
|
||||
container_name: bitcoin
|
||||
image: ghcr.io/runcitadel/bitcoin-custom:main@sha256:d0af506f8dc92a434e845305ac4252d0601b699c4b3bc4443073a0a2e237f3a0
|
||||
depends_on:
|
||||
- tor
|
||||
volumes:
|
||||
- ${PWD}/bitcoin:/data/.bitcoin
|
||||
restart: on-failure
|
||||
stop_grace_period: 1m
|
||||
ports:
|
||||
- $BITCOIN_P2P_PORT:$BITCOIN_P2P_PORT
|
||||
networks:
|
||||
default:
|
||||
ipv4_address: $BITCOIN_IP
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
bitcoin:
|
||||
container_name: bitcoin
|
||||
image: lncm/bitcoind:v22.0@sha256:37a1adb29b3abc9f972f0d981f45e41e5fca2e22816a023faa9fdc0084aa4507
|
||||
image: lncm/bitcoind:v23.0@sha256:57317c90d89156a30327fe1b8e51b836e0fd1a8ba13721eb2e75e6b35a570e26
|
||||
depends_on:
|
||||
- tor
|
||||
volumes:
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
bitcoin:
|
||||
container_name: bitcoin
|
||||
image: nolim1t/bitcoinknots:v22.0.knots20211108@sha256:a475da2b2ecda55fcc65ea23e1a36c58b2c10549f1c3d3bb3c31c7dda1127354
|
||||
image: ghcr.io/runcitadel/bitcoinknots:main@sha256:5fbee0f6f0d09d42aacc11c373ffe6162210c42ce21e6eba294e547e3ad80219
|
||||
depends_on:
|
||||
- tor
|
||||
volumes:
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
electrum:
|
||||
container_name: electrum
|
||||
image: ghcr.io/runcitadel/fulcrumx:latest@sha256:a74abdfe8397f02482faed6bd828477c452df071129f66ad6596d0ab8d29cf39
|
||||
image: cculianu/fulcrum:latest@sha256:c0543f8b8a5bf6b0c447d8525d6b4360a6c07532f7741f19cc2c179968e71848
|
||||
working_dir: /data
|
||||
volumes:
|
||||
- ${PWD}/bitcoin:/bitcoin:ro
|
||||
- ${PWD}/fulcrumx:/data
|
||||
command: /usr/bin/FulcrumX /data/fulcrumx.conf
|
||||
- ${PWD}/fulcrum:/data
|
||||
command: /usr/bin/Fulcrum /data/fulcrum.conf
|
||||
restart: on-failure
|
||||
stop_grace_period: 5m
|
||||
ports:
|
|
@ -1,19 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
electrum:
|
||||
container_name: electrum
|
||||
image: ghcr.io/runcitadel/fulcrumx:latest@sha256:557a54a5652b475b01c59eb6cffee3568f18b74d875c4cc125e5ac190c4b0706
|
||||
working_dir: /data
|
||||
volumes:
|
||||
- ${PWD}/bitcoin:/bitcoin:ro
|
||||
- ${PWD}/fulcrumx:/data
|
||||
command: /usr/bin/FulcrumX /data/fulcrumx.conf
|
||||
restart: on-failure
|
||||
stop_grace_period: 5m
|
||||
ports:
|
||||
- "$ELECTRUM_PORT:$ELECTRUM_PORT"
|
||||
networks:
|
||||
default:
|
||||
ipv4_address: $ELECTRUM_IP
|
|
@ -1,6 +1,6 @@
|
|||
lightning:
|
||||
container_name: lightning
|
||||
image: lightninglabs/lnd:v0.14.3-beta@sha256:6a2234b0aad4caed3d993736816b198d6228f32c59b27ba2218d5ebf516ae905
|
||||
image: lightninglabs/lnd:v0.15.0-beta@sha256:d227a9db0727ff56020c8d6604c8c369757123d238ab6ce679579c2dd0d0d259
|
||||
user: 1000:1000
|
||||
depends_on:
|
||||
- tor
|
||||
|
|
Loading…
Reference in New Issue
Block a user