- Generates new custom plugin directory layout upon `gen`
* Allows for drop-in client-side/container-side custom plugins
- Moved container plugin example into container's plugins
* Plugin is no longer generated upon `gen` (it's available in-repo)
- Updates plugin example
- Provides requisite lib_finance initialization
- Refactored, added more checks
- Expand example case
- Linter fixes
1002 lines
33 KiB
Bash
1002 lines
33 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# docker-finance | modern accounting for the power-user
|
|
#
|
|
# Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
[ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && exit 1
|
|
|
|
#
|
|
# "Libraries"
|
|
#
|
|
|
|
source "${DOCKER_FINANCE_CLIENT_REPO}/container/src/finance/lib/internal/lib_utils.bash" || exit 1
|
|
|
|
#
|
|
# Implementation
|
|
#
|
|
|
|
if [ $UID -lt 1000 ]; then
|
|
lib_utils::die_fatal "Do not run as root or system user!"
|
|
fi
|
|
|
|
# IMPORTANT: keep umask for security
|
|
umask o-rwx
|
|
|
|
if ! docker compose version 1>/dev/null; then
|
|
lib_utils::die_fatal "Docker compose plugin not installed"
|
|
fi
|
|
|
|
if [ -z "$EDITOR" ]; then
|
|
editors=("vim" "vi" "emacs" "nano")
|
|
for editor in "${editors[@]}"; do
|
|
hash "$editor" &>/dev/null && export EDITOR="$editor" && break
|
|
done
|
|
if [ $? -ne 0 ]; then
|
|
lib_utils::die_fatal "Shell EDITOR is not set, export EDITOR in your shell"
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# "Constructor" for environment generation
|
|
#
|
|
# 1. Sets client-side environment with defaults or use existing environment
|
|
# 2. If configured, resets to alternative environment configuration after bootstrap
|
|
#
|
|
# NOTE: some bootstrapped defaults are ignored by environment file (as seen below)
|
|
#
|
|
|
|
function lib_gen::gen()
|
|
{
|
|
[ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && lib_utils::die_fatal
|
|
|
|
# NOTE: global_* *MUST* be reset after sourcing new env file
|
|
|
|
#
|
|
# Generate `docker-finance` version
|
|
#
|
|
|
|
global_repo_manifest="${DOCKER_FINANCE_CLIENT_REPO}/client/docker-finance.yaml"
|
|
declare -g global_repo_manifest
|
|
|
|
global_client_version="$(grep '^version: ' $global_repo_manifest | sed -e 's/version: "//' -e 's/"//g')"
|
|
# shellcheck disable=SC2034 # used during env gen
|
|
declare -g global_client_version
|
|
|
|
#
|
|
# If empty environment:
|
|
#
|
|
# 1. Poke at possible (default) location of end-user environment file
|
|
# a. If found, point to new location and read/reset from there
|
|
#
|
|
# 2. If file not found, bootstrap with defaults
|
|
#
|
|
|
|
# Environment (end-user)
|
|
global_conf_filename="${USER}@$(uname -n)"
|
|
declare -g global_conf_filename
|
|
|
|
# Environment (end-user) tag dir (not full path)
|
|
[ -z "$global_tag" ] && lib_utils::die_fatal
|
|
global_tag_dir="client/$(uname -s)-$(uname -m)/${global_platform}/${global_tag}"
|
|
declare -g global_tag_dir
|
|
|
|
local _env_dir="/home/${USER}/.config/docker-finance.d/${global_tag_dir}/env" # NOTE: keep aligned with gen.bash
|
|
local _env_file="${_env_dir}/${global_conf_filename}"
|
|
|
|
# shellcheck source=/dev/null
|
|
[ -f "$_env_file" ] && source "$_env_file"
|
|
|
|
if [ -z "$DOCKER_FINANCE_CLIENT_CONF" ]; then
|
|
# shellcheck source=/dev/null
|
|
source "${DOCKER_FINANCE_CLIENT_REPO}/client/docker-finance.d/client/env/gen.bash"
|
|
fi
|
|
|
|
[ -z "$DOCKER_FINANCE_CLIENT_CONF" ] \
|
|
&& lib_utils::die_fatal "Defaults not generated! (${global_repo_env_file})"
|
|
|
|
lib_gen::__set_client_globals
|
|
|
|
#
|
|
# Reset environment with user-provided (user-defined) existing file (if available)
|
|
#
|
|
|
|
_env_dir="${DOCKER_FINANCE_CLIENT_CONF}/${global_tag_dir}/env"
|
|
_env_file="${_env_dir}/${global_conf_filename}"
|
|
|
|
if [ -f "$_env_file" ]; then
|
|
if [ -s "$_env_file" ]; then
|
|
# Re-bootstrap with (new) environment
|
|
lib_utils::print_debug "Environment found! Using '${_env_file}'"
|
|
lib_gen::__read_env "$_env_file"
|
|
lib_gen::__set_client_globals
|
|
else
|
|
lib_utils::print_warning \
|
|
"Client environment '${_env_file}' is empty! Writing defaults"
|
|
lib_gen::__write_env "$_env_file"
|
|
fi
|
|
else
|
|
[ -z "$global_command" ] && lib_utils::die_fatal
|
|
[ -z "$global_basename" ] && lib_utils::die_fatal
|
|
|
|
if [[ -z "$global_command" || "$global_command" != "gen" ]]; then
|
|
lib_utils::die_fatal \
|
|
"Client environment not found! Run gen command'"
|
|
fi
|
|
|
|
lib_utils::print_info \
|
|
"Client environment not found, writing defaults to '${_env_file}'"
|
|
lib_gen::__write_env "$_env_file"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Set client globals from environment
|
|
#
|
|
|
|
function lib_gen::__set_client_globals()
|
|
{
|
|
lib_utils::print_debug "Setting (or resetting) client globals"
|
|
|
|
#
|
|
# Repository env
|
|
#
|
|
|
|
[ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && lib_utils::die_fatal
|
|
|
|
# Generate `docker-finance` version
|
|
global_repo_manifest="${DOCKER_FINANCE_CLIENT_REPO}/client/docker-finance.yaml"
|
|
declare -g global_repo_manifest
|
|
lib_utils::print_debug "global_repo_manifest=${global_repo_manifest}"
|
|
|
|
global_client_version="$(grep '^version: ' $global_repo_manifest | sed -e 's/version: "//' -e 's/"//g')"
|
|
# shellcheck disable=SC2034 # used during env gen
|
|
declare -g global_client_version
|
|
lib_utils::print_debug "global_client_version=${global_client_version}"
|
|
|
|
# This does not reset when reading env; export again
|
|
export DOCKER_FINANCE_VERSION="$global_client_version"
|
|
lib_utils::print_debug "DOCKER_FINANCE_VERSION=${DOCKER_FINANCE_VERSION}"
|
|
|
|
# Repository-provided (not user-defined) default environment
|
|
global_repo_conf_dir="${DOCKER_FINANCE_CLIENT_REPO}/client/docker-finance.d"
|
|
declare -g global_repo_conf_dir
|
|
lib_utils::print_debug "global_repo_conf_dir=${global_repo_conf_dir}"
|
|
|
|
# Environment (repo)
|
|
declare -g global_repo_env_file="${global_repo_conf_dir}/client/env/gen.bash"
|
|
[ ! -f "$global_repo_env_file" ] \
|
|
&& lib_utils::die_fatal "Missing environment defaults! ($global_repo_env_file)"
|
|
lib_utils::print_debug "global_repo_env_file=${global_repo_env_file}"
|
|
|
|
#
|
|
# Repository env (Dockerfiles)
|
|
#
|
|
|
|
# Set image type
|
|
[ -z "$global_platform" ] && lib_utils::die_fatal
|
|
case "$global_platform" in
|
|
archlinux | ubuntu)
|
|
global_platform_image="finance"
|
|
;;
|
|
dev-tools)
|
|
global_platform_image="dev-tools"
|
|
;;
|
|
*)
|
|
lib_utils::die_fatal "unsupported platform"
|
|
;;
|
|
esac
|
|
declare -g global_platform_image
|
|
lib_utils::print_debug "global_platform_image=${global_platform_image}"
|
|
|
|
# Base location of Docker-related files (.in and final out files)
|
|
global_repo_dockerfiles="${DOCKER_FINANCE_CLIENT_REPO}/client/Dockerfiles/${global_platform_image}"
|
|
# shellcheck disable=SC2034 # used in lib_docker
|
|
declare -g global_repo_dockerfiles
|
|
lib_utils::print_debug "global_repo_dockerfiles=${global_repo_dockerfiles}"
|
|
|
|
# Base custom end-user .in Dockerfile (to be appended to final Dockerfile after installation)
|
|
declare -g global_repo_custom_dockerfile="${global_repo_conf_dir}/client/Dockerfiles/${global_platform_image}/Dockerfile.${global_platform}.in"
|
|
[ ! -f "$global_repo_custom_dockerfile" ] \
|
|
&& lib_utils::die_fatal "Missing default custom Dockerfile '${global_repo_custom_dockerfile}'"
|
|
lib_utils::print_debug "global_repo_custom_dockerfile=${global_repo_custom_dockerfile}"
|
|
|
|
#
|
|
# Client-side env
|
|
#
|
|
|
|
[ -z "$global_tag_dir" ] && lib_utils::die_fatal
|
|
|
|
# Environment (end-user) format
|
|
[ -z "$DOCKER_FINANCE_USER" ] && lib_utils::die_fatal
|
|
global_conf_filename="${DOCKER_FINANCE_USER}@$(uname -n)"
|
|
declare -g global_conf_filename
|
|
lib_utils::print_debug "global_conf_filename=${global_conf_filename}"
|
|
|
|
# Environment file (if available)
|
|
local _client_env_dir="${DOCKER_FINANCE_CLIENT_CONF}/${global_tag_dir}/env"
|
|
[ ! -d "$_client_env_dir" ] && mkdir -p "$_client_env_dir"
|
|
global_env_file="${_client_env_dir}/${global_conf_filename}"
|
|
lib_utils::print_debug "global_env_file=${global_env_file}"
|
|
|
|
# Custom Dockerfile (if available)
|
|
local _client_dockerfile_dir="${DOCKER_FINANCE_CLIENT_CONF}/${global_tag_dir}/Dockerfiles"
|
|
[ ! -d "$_client_dockerfile_dir" ] && mkdir -p "$_client_dockerfile_dir"
|
|
global_custom_dockerfile="${_client_dockerfile_dir}/${global_conf_filename}"
|
|
lib_utils::print_debug "global_custom_dockerfile=${global_custom_dockerfile}"
|
|
|
|
# NOTE:
|
|
#
|
|
# Client env tag format is avoided because:
|
|
#
|
|
# - We copy over static bash_aliases in Dockerfile,
|
|
# and superscript must be referenced by that static path
|
|
#
|
|
# - The needed dynamicness appears to not be satisfied via docker-compose
|
|
|
|
local _client_shell_dir="${DOCKER_FINANCE_CLIENT_CONF}/container/shell"
|
|
[ ! -d "$_client_shell_dir" ] && mkdir -p "$_client_shell_dir"
|
|
global_shell_file="${_client_shell_dir}/superscript.bash"
|
|
lib_utils::print_debug "global_shell_file=${global_shell_file}"
|
|
|
|
# Client view of client portion of repository
|
|
global_repo_client="${DOCKER_FINANCE_CLIENT_REPO}/client"
|
|
[ ! -d "$global_repo_client" ] && lib_utils::die_fatal "Repository '${global_repo_client}' not found!"
|
|
lib_utils::print_debug "global_repo_client=${global_repo_client}"
|
|
|
|
# Backup-file extension
|
|
# TODO: make configurable
|
|
global_suffix="$(date +%Y-%m-%d_%H:%M:%S)"
|
|
lib_utils::print_debug "global_suffix=${global_suffix}"
|
|
}
|
|
|
|
#
|
|
# Get/Set client-side environment with given file
|
|
#
|
|
|
|
function lib_gen::__read_env()
|
|
{
|
|
local _file="$1"
|
|
|
|
# Get environment
|
|
local _env=()
|
|
while read _line; do
|
|
_env+=("$_line")
|
|
done < <(printenv | grep -Eo "^DOCKER_FINANCE[^=]+")
|
|
|
|
# Reset environment
|
|
for _line in "${_env[@]}"; do
|
|
lib_utils::print_debug "Unsetting $_line"
|
|
unset "$_line"
|
|
done
|
|
|
|
# Set, if a script that generates env
|
|
if [[ "$(head -n1 $_file)" =~ (bin|env|sh|bash) ]]; then
|
|
# shellcheck source=/dev/null
|
|
source "$global_repo_env_file"
|
|
return $?
|
|
fi
|
|
|
|
# Set, if env file format (docker / bash)
|
|
while read _line; do
|
|
# Ignore comments
|
|
if [[ ! "$_line" =~ ^# ]]; then
|
|
# Don't allow manipulating version via file
|
|
if [[ "$_line" =~ ^DOCKER_FINANCE_VERSION ]]; then
|
|
continue
|
|
fi
|
|
# Export valid line
|
|
export "${_line?}" # SC2163
|
|
lib_utils::print_debug "$_line"
|
|
fi
|
|
done <"$_file"
|
|
}
|
|
|
|
#
|
|
# Write client-side environment to given file
|
|
#
|
|
|
|
function lib_gen::__write_env()
|
|
{
|
|
lib_utils::print_debug "Writing environment"
|
|
|
|
unset DOCKER_FINANCE_VERSION # Must be generated internally
|
|
printenv | grep -E "DOCKER_FINANCE" | sort >"$1"
|
|
}
|
|
|
|
#
|
|
# Generate new configurations
|
|
#
|
|
|
|
function lib_gen::generate()
|
|
{
|
|
lib_utils::print_custom "\n"
|
|
lib_utils::print_info "Generating client/container environment"
|
|
|
|
# WARNING: client generation MUST be done before container generation
|
|
lib_gen::__gen_client
|
|
|
|
if [ $? -eq 0 ]; then
|
|
if [ "$global_platform" != "dev-tools" ]; then
|
|
lib_gen::__gen_container
|
|
fi
|
|
fi
|
|
|
|
if [ $? -eq 0 ]; then
|
|
lib_utils::print_info "Congratulations, environment sucessfully generated!"
|
|
lib_utils::print_custom "\n"
|
|
else
|
|
lib_utils::die_fatal "Environment not fully generated! Try again"
|
|
fi
|
|
}
|
|
|
|
function lib_gen::__gen_client()
|
|
{
|
|
lib_utils::print_debug "Generating client"
|
|
|
|
#
|
|
# Client-side environment file
|
|
#
|
|
|
|
# Backup existing file
|
|
if [ -f "$global_env_file" ]; then
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_custom " \e[32m├─\e[34;1m Client-side environment found, backup then generate new one? [Y/n] \e[0m"
|
|
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$global_env_file" "${global_env_file}_${global_suffix}" \
|
|
|| lib_utils::die_fatal
|
|
|
|
# Get/Set with repository defaults
|
|
lib_utils::print_debug "Reading $global_repo_env_file"
|
|
lib_gen::__read_env "$global_repo_env_file"
|
|
|
|
lib_utils::print_debug "Writing $global_env_file"
|
|
lib_gen::__write_env "$global_env_file"
|
|
fi
|
|
fi
|
|
|
|
lib_utils::print_custom " \e[32m│ └─\e[34m Edit (new) environment now? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$global_env_file"
|
|
|
|
# Get/Set new (edited) environment variables
|
|
lib_gen::__read_env "$global_env_file"
|
|
lib_gen::__set_client_globals
|
|
|
|
#
|
|
# Custom (optional) Dockerfile
|
|
#
|
|
|
|
# Backup existing custom Dockerfile
|
|
if [ -f "$global_custom_dockerfile" ]; then
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_custom " \e[32m├─\e[34;1m Custom (optional) Dockerfile found, backup then generate new one? [Y/n] \e[0m"
|
|
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$global_custom_dockerfile" "${global_custom_dockerfile}_${global_suffix}" || lib_utils::die_fatal
|
|
cp -fa "$global_repo_custom_dockerfile" "$global_custom_dockerfile" || lib_utils::die_fatal
|
|
fi
|
|
else
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_custom " \e[32m├─\e[34;1m Generating new custom (optional) Dockerfile\e[0m\n"
|
|
|
|
lib_utils::print_debug "$global_repo_custom_dockerfile"
|
|
lib_utils::print_debug "$global_custom_dockerfile"
|
|
|
|
cp -a "$global_repo_custom_dockerfile" "$global_custom_dockerfile"
|
|
fi
|
|
|
|
lib_utils::print_custom " \e[32m│ └─\e[34m Edit (new) custom Dockerfile now? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$global_custom_dockerfile" || return 0
|
|
}
|
|
|
|
function lib_gen::__gen_container()
|
|
{
|
|
lib_utils::print_debug "Generating container"
|
|
|
|
#
|
|
# Generate plugins (custom)
|
|
#
|
|
|
|
[ -z "$DOCKER_FINANCE_CLIENT_PLUGINS" ] && lib_utils::die_fatal
|
|
if [ ! -d "$DOCKER_FINANCE_CLIENT_PLUGINS" ]; then
|
|
mkdir -p "$DOCKER_FINANCE_CLIENT_PLUGINS" || lib_utils::die_fatal
|
|
fi
|
|
|
|
lib_gen::__gen_plugins
|
|
|
|
#
|
|
# Generate flow
|
|
#
|
|
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_custom " \e[32m├─\e[34;1m Generate (or update) container flow (profiles, etc.)? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
if [[ "$_confirm" != [yY] ]]; then
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_normal ""
|
|
return 0
|
|
fi
|
|
|
|
[ -z "$DOCKER_FINANCE_CLIENT_FLOW" ] && lib_utils::die_fatal
|
|
if [ ! -d "$DOCKER_FINANCE_CLIENT_FLOW" ]; then
|
|
mkdir -p "$DOCKER_FINANCE_CLIENT_FLOW" || lib_utils::die_fatal
|
|
fi
|
|
|
|
lib_gen::__gen_times
|
|
lib_gen::__gen_profile
|
|
}
|
|
|
|
#
|
|
# Generate plugins (custom):
|
|
#
|
|
# - Provides:
|
|
# - A layout to drop-in custom plugins
|
|
# * Underlying impl expects this layout
|
|
#
|
|
|
|
function lib_gen::__gen_plugins()
|
|
{
|
|
lib_utils::print_debug "Generating custom plugins layout"
|
|
|
|
local -r _client="${DOCKER_FINANCE_CLIENT_PLUGINS}/client"
|
|
if [ ! -d "${_client}/docker" ]; then
|
|
mkdir -p "${_client}/docker" || lib_utils::die_fatal
|
|
fi
|
|
|
|
local -r _container="${DOCKER_FINANCE_CLIENT_PLUGINS}/container"
|
|
if [ ! -d "${_container}/finance" ]; then
|
|
mkdir -p "${_container}/finance" || lib_utils::die_fatal
|
|
fi
|
|
if [ ! -d "${_container}/root" ]; then
|
|
mkdir -p "${_container}/root" || lib_utils::die_fatal
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Generate flow: times
|
|
#
|
|
# - Provides a place for:
|
|
# * timew database
|
|
# * Anything time-related (among all profiles)
|
|
#
|
|
|
|
function lib_gen::__gen_times()
|
|
{
|
|
lib_utils::print_debug "Generating times"
|
|
|
|
local -r _times="${DOCKER_FINANCE_CLIENT_FLOW}/times"
|
|
if [ ! -d "$_times" ]; then
|
|
mkdir -p "$_times" || lib_utils::die_fatal
|
|
fi
|
|
|
|
# NOTE: timew database will be created upon first call
|
|
}
|
|
|
|
#
|
|
# Generate flow: profile/subprofile
|
|
#
|
|
|
|
function lib_gen::__gen_profile()
|
|
{
|
|
lib_utils::print_debug "Generating profiles"
|
|
|
|
local -r _profiles="${DOCKER_FINANCE_CLIENT_FLOW}/profiles"
|
|
if [ ! -d "$_profiles" ]; then
|
|
mkdir -p "$_profiles" || lib_utils::die_fatal
|
|
fi
|
|
|
|
#
|
|
# Profile: development setup
|
|
#
|
|
|
|
lib_utils::print_custom " \e[32m│ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ ├─\e[34;1m Will this profile be used for development and/or demonstration? [N/y] \e[0m"
|
|
read -p "" _read
|
|
local _is_testing="${_read:-false}"
|
|
if [[ "$_read" == [yY] ]]; then
|
|
_is_testing=true
|
|
fi
|
|
lib_utils::print_debug "_is_testing=${_is_testing}"
|
|
|
|
#
|
|
# Profile: construct full path to subprofile
|
|
#
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ ├─\e[34m Enter profile name (e.g., family in 'family/alice'): \e[0m"
|
|
read -p "" _read
|
|
local -r _profile="${_read:-default}"
|
|
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Using profile:\e[0m ${_profile}\n"
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ └─\e[34m Enter subprofile name (e.g., alice in 'family/alice'): \e[0m"
|
|
read -p "" _read
|
|
local -r _subprofile="${_read:-${global_user}}"
|
|
lib_utils::print_custom " \e[32m│ │ └─\e[34m Using subprofile:\e[0m ${_subprofile}\n"
|
|
|
|
if [ -d "${_profiles}/${_profile}/${_subprofile}" ]; then
|
|
lib_utils::print_custom " \e[32m│ │ └─\e[34m Subprofile exists! Continue with backup and generation? [Y/n] \e[0m"
|
|
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
if [[ "$_confirm" != [yY] ]]; then
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
lib_utils::print_normal ""
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# Profile: execute profile-specific
|
|
#
|
|
|
|
lib_utils::print_custom " \e[32m│ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) joint client/container shell script (superscript)? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
local _gen_path="\${DOCKER_FINANCE_CONTAINER_FLOW}/profiles/${_profile}/${_subprofile}" # \$ for container view
|
|
local _gen_conf_path="${_gen_path}/docker-finance.d"
|
|
lib_utils::print_debug "_gen_path=${_gen_path}"
|
|
lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
|
|
|
|
lib_gen::__gen_subprofile_shell "$_gen_path" "$_gen_conf_path"
|
|
fi
|
|
|
|
lib_utils::print_custom " \e[32m│ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) hledger configuration file? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
local _gen_path="${_profiles}/${_profile}/${_subprofile}" # client's view
|
|
local _gen_conf_path="${_gen_path}/docker-finance.d"
|
|
lib_utils::print_debug "_gen_path=${_gen_path}"
|
|
lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
|
|
|
|
lib_gen::__gen_subprofile_hledger "$_gen_path" "$_gen_conf_path"
|
|
fi
|
|
|
|
lib_utils::print_custom " \e[32m│ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) container flow configs and/or accounts? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
local _gen_path="${_profiles}/${_profile}/${_subprofile}"
|
|
local _gen_conf_path="${_gen_path}/docker-finance.d"
|
|
lib_utils::print_debug "_gen_path=${_gen_path}"
|
|
lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
|
|
|
|
lib_gen::__gen_subprofile_flow "$_gen_path" "$_gen_conf_path"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Subprofile: generate client-side container superscript
|
|
# TODO: separate superscript gen from subprofile append
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_shell()
|
|
{
|
|
local -r _gen_path="$1"
|
|
local -r _gen_conf_path="$2"
|
|
|
|
# Subprofile source added to superscript
|
|
local -r _source="source \"${_gen_conf_path}/shell/subprofile.bash\""
|
|
|
|
local _file="$global_shell_file"
|
|
if [ -f "$_file" ]; then
|
|
# Append subprofile source to existing file
|
|
lib_utils::print_custom " \e[32m│ │ └─\e[34m Shell superscript found, append new subprofile to existing file? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
grep "$_source" "$_file" >&/dev/null || sed -i "$(wc -l <$_file)i\\$_source\\" "$_file"
|
|
fi
|
|
# Or generate new file
|
|
lib_utils::print_custom " \e[32m│ │ └─\e[34m Backup existing superscript and generate a new one? [N/y] \e[0m"
|
|
read -p "" _read
|
|
local _confirm="${_read:-n}"
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
# Backup
|
|
local -r _backup=("cp" "-a" "$_file" "${_file}_${global_suffix}")
|
|
lib_utils::print_debug "${_backup[@]}"
|
|
"${_backup[@]}" || lib_utils::die_fatal
|
|
# Write
|
|
lib_gen::__gen_subprofile_shell_write
|
|
fi
|
|
local _print_custom=" \e[32m│ │ └─\e[34m Edit (new) superscript now? [Y/n] \e[0m"
|
|
else
|
|
# Generate new default file
|
|
lib_gen::__gen_subprofile_shell_write
|
|
local _print_custom=" \e[32m│ │ └─\e[34m Edit (new) superscript now? [Y/n] \e[0m"
|
|
fi
|
|
lib_utils::print_custom "$_print_custom"
|
|
read -p "" _read
|
|
local _confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_shell_write()
|
|
{
|
|
[ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
|
|
lib_utils::print_debug "global_repo_conf_dir=${global_repo_conf_dir}"
|
|
|
|
[ -z "$global_client_version" ] && lib_utils::die_fatal
|
|
lib_utils::print_debug "global_client_version=${global_client_version}"
|
|
|
|
sed \
|
|
-e "s:@DOCKER_FINANCE_SUBPROFILE_SOURCE@:${_source}:g" \
|
|
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
|
|
"${global_repo_conf_dir}/container/shell/superscript.bash.in" >"$_file"
|
|
}
|
|
|
|
#
|
|
# Subprofile: generate hledger configuration file
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_hledger()
|
|
{
|
|
local -r _gen_path="$1"
|
|
local -r _gen_conf_path="$2"
|
|
|
|
local _dir="${_gen_conf_path}/hledger"
|
|
[ ! -d "$_dir" ] && mkdir -p "$_dir"
|
|
|
|
local _file="${_dir}/hledger.conf"
|
|
if [ -f "$_file" ]; then
|
|
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m hledger configuration found, backup then generate new one? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
|
|
|
|
lib_gen::__gen_subprofile_hledger_write "$_file"
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) hledger configuration now? [Y/n] \e[0m"
|
|
fi
|
|
else
|
|
lib_gen::__gen_subprofile_hledger_write "$_file"
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) hledger configuration now? [Y/n] \e[0m"
|
|
fi
|
|
|
|
lib_utils::print_custom "$_print_custom"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_hledger_write()
|
|
{
|
|
local _file="$1"
|
|
[ -z "$_file" ] && lib_utils::die_fatal
|
|
|
|
[ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
|
|
lib_utils::print_debug "global_repo_conf_dir=${global_repo_conf_dir}"
|
|
|
|
[ -z "$global_client_version" ] && lib_utils::die_fatal
|
|
lib_utils::print_debug "global_client_version=${global_client_version}"
|
|
|
|
sed \
|
|
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
|
|
"${global_repo_conf_dir}/container/hledger/hledger.conf.in" >"$_file"
|
|
}
|
|
|
|
#
|
|
# Subprofile: generate flow
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_flow()
|
|
{
|
|
local -r _gen_path="$1"
|
|
local -r _gen_conf_path="$2"
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate (or update) subprofile's shell script? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && lib_gen::__gen_subprofile_flow_shell
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate (or update) subprofile's fetch configuration? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && lib_gen::__gen_subprofile_flow_fetch
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate (or update) subprofile's financial metadata? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && lib_gen::__gen_subprofile_flow_meta
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate (or update) subprofile's hledger-flow accounts? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && lib_gen::__gen_subprofile_flow_accounts
|
|
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
|
|
# Placeholder connector between hledger-flow code and end-user's hledger-flow profiles
|
|
# NOTE: entrypoint will (MUST) update this on every container run (yes, looks different than entrypoint)
|
|
lib_utils::print_custom " \e[32m├─\e[34;1m Connecting flow sources \e[0m\n"
|
|
|
|
[ -z "$DOCKER_FINANCE_CONTAINER_REPO" ] && lib_utils::die_fatal
|
|
ln -f -s "${DOCKER_FINANCE_CONTAINER_REPO}/src/hledger-flow" "${DOCKER_FINANCE_CLIENT_FLOW}/src"
|
|
|
|
lib_utils::print_custom " \e[32m│\e[0m\n"
|
|
}
|
|
|
|
#
|
|
# Subprofile: flow: generate subprofile script
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_flow_shell()
|
|
{
|
|
local _dir="${_gen_conf_path}/shell"
|
|
[ ! -d "$_dir" ] && mkdir -p "$_dir"
|
|
|
|
local _file="${_dir}/subprofile.bash"
|
|
if [ -f "$_file" ]; then
|
|
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Subprofile script found, backup then generate new one? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
|
|
|
|
lib_gen::__gen_subprofile_flow_shell_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile script now? [Y/n] \e[0m"
|
|
fi
|
|
else
|
|
lib_gen::__gen_subprofile_flow_shell_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile script now? [Y/n] \e[0m"
|
|
fi
|
|
|
|
lib_utils::print_custom "$_print_custom"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_flow_shell_write()
|
|
{
|
|
[ -z "$DOCKER_FINANCE_CONTAINER_CMD" ] && lib_utils::die_fatal
|
|
sed \
|
|
-e "s:@DOCKER_FINANCE_CONTAINER_CMD@:${DOCKER_FINANCE_CONTAINER_CMD}:g" \
|
|
-e "s:@DOCKER_FINANCE_CONTAINER_REPO@:${DOCKER_FINANCE_CONTAINER_REPO}:g" \
|
|
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
|
|
-e "s:@DOCKER_FINANCE_PROFILE@:${_profile}:g" \
|
|
-e "s:@DOCKER_FINANCE_SUBPROFILE@:${_subprofile}:g" \
|
|
"${global_repo_conf_dir}/container/shell/subprofile.bash.in" >"$_file"
|
|
}
|
|
|
|
#
|
|
# Subprofile: flow: generate fetch config
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_flow_fetch()
|
|
{
|
|
local _dir="${_gen_conf_path}/fetch"
|
|
[ ! -d "$_dir" ] && mkdir -p "$_dir"
|
|
|
|
local _file="${_dir}/fetch.yaml"
|
|
if [ -f "$_file" ]; then
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Subprofile's fetch configuration found, backup then generate new one? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
|
|
|
|
lib_gen::__gen_subprofile_flow_fetch_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile's fetch configuration now? [Y/n] \e[0m"
|
|
fi
|
|
else
|
|
lib_gen::__gen_subprofile_flow_fetch_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile's fetch configuration now? [Y/n] \e[0m"
|
|
fi
|
|
|
|
lib_utils::print_custom "$_print_custom"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_flow_fetch_write()
|
|
{
|
|
[ -z "$_profile" ] && lib_utils::die_fatal
|
|
[ -z "$_subprofile" ] && lib_utils::die_fatal
|
|
|
|
[ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
|
|
[ -z "$_file" ] && lib_utils::die_fatal
|
|
|
|
[ -z "$global_client_version" ] && lib_utils::die_fatal
|
|
|
|
sed \
|
|
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
|
|
-e "s:@DOCKER_FINANCE_PROFILE@:${_profile}:g" \
|
|
-e "s:@DOCKER_FINANCE_SUBPROFILE@:${_subprofile}:g" \
|
|
"${global_repo_conf_dir}/container/fetch/fetch.yaml.in" >"$_file"
|
|
}
|
|
|
|
#
|
|
# Subprofile: flow: generate financial metadata
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_flow_meta()
|
|
{
|
|
local _dir="${_gen_conf_path}/meta"
|
|
[ ! -d "$_dir" ] && mkdir -p "$_dir"
|
|
|
|
local _file="${_dir}/meta.csv"
|
|
if [ -f "$_file" ]; then
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Subprofile's financial metadata found, backup then generate new one? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
|
|
if [[ "$_confirm" == [yY] ]]; then
|
|
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
|
|
|
|
lib_gen::__gen_subprofile_flow_meta_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile's financial metadata now? [Y/n] \e[0m"
|
|
fi
|
|
else
|
|
lib_gen::__gen_subprofile_flow_meta_write
|
|
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) subprofile's financial metadata now? [Y/n] \e[0m"
|
|
fi
|
|
|
|
lib_utils::print_custom "$_print_custom"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_flow_meta_write()
|
|
{
|
|
# Deletes default comments or else ROOT meta sample won't work out-of-the-box
|
|
sed \
|
|
-e "/\/\/\\!/d" \
|
|
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
|
|
"${global_repo_conf_dir}/container/meta/meta.csv.in" >"$_file"
|
|
}
|
|
|
|
#
|
|
# Subprofile: flow: generate accounts
|
|
#
|
|
|
|
function lib_gen::__gen_subprofile_flow_accounts()
|
|
{
|
|
lib_utils::print_debug "Generating accounts"
|
|
|
|
local _subprofile_path="${_gen_path}/import/${_subprofile}"
|
|
lib_utils::print_debug "_subprofile_path=${_subprofile_path}"
|
|
|
|
if [ -d "$_subprofile_path" ]; then
|
|
lib_utils::print_custom " \e[32m│ │ │ ├─\e[34m Subprofile import path exists! Continue with account generation (files will be backed up)? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] || return 0
|
|
else
|
|
mkdir -p "$_subprofile_path"
|
|
fi
|
|
|
|
# docker-finance templates
|
|
local _templates_paths
|
|
mapfile -t _templates_paths < <(find ${DOCKER_FINANCE_CLIENT_REPO}/container/src/hledger-flow/accounts -name template | sort)
|
|
declare -r _templates_paths
|
|
lib_utils::print_debug "_templates_paths=${_templates_paths[*]}"
|
|
|
|
lib_utils::print_custom " \e[32m│ │ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ │ ├─\e[34;1m Generate individual subprofile accounts instead of generating them all at once? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
|
|
# Cycle through all available docker-finance templates and populate subprofile
|
|
if [[ $_confirm == [yY] ]]; then
|
|
for _template_path in "${_templates_paths[@]}"; do
|
|
lib_utils::print_debug "_template_path=${_template_path}"
|
|
lib_utils::print_custom " \e[32m│ │ │ │ │\e[0m\n"
|
|
lib_utils::print_custom " \e[32m│ │ │ │ ├─\e[34m\e[1m Generate $(echo $_template_path | rev | cut -d/ -f2 | rev) ? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] \
|
|
&& lib_gen::__gen_subprofile_flow_accounts_populate "$_subprofile_path" "$_template_path" "$_is_testing"
|
|
done
|
|
else
|
|
for _template_path in "${_templates_paths[@]}"; do
|
|
lib_utils::print_debug "$_subprofile_path $_template_path $_is_testing"
|
|
lib_gen::__gen_subprofile_flow_accounts_populate "$_subprofile_path" "$_template_path" "$_is_testing"
|
|
done
|
|
fi
|
|
}
|
|
|
|
function lib_gen::__gen_subprofile_flow_accounts_populate()
|
|
{
|
|
[ -z "$1" ] && lib_utils::die_fatal
|
|
[ -z "$2" ] && lib_utils::die_fatal
|
|
[ -z "$3" ] && lib_utils::die_fatal
|
|
local _subprofile_path="$1"
|
|
local _template_path="$2"
|
|
local _is_testing="$3"
|
|
|
|
# Continue if hledger-flow account exists
|
|
local _continue
|
|
_continue=true
|
|
|
|
local _account_path
|
|
_account_path="${_subprofile_path}/$(echo $_template_path | rev | cut -d/ -f2 | rev)"
|
|
lib_utils::print_debug "_account_path=${_account_path}"
|
|
|
|
# TODO: doesn't check for blockchain explorer shared rules/bash
|
|
if [ -d "$_account_path" ]; then
|
|
lib_utils::print_custom " \e[32m│ │ │ │ │ └─\e[34m Account exists! Continue with backup and generation? [Y/n] \e[0m"
|
|
read -p "" _read
|
|
_confirm="${_read:-y}"
|
|
[[ "$_confirm" == [yY] ]] || _continue=false
|
|
fi
|
|
|
|
# Populate with template
|
|
if [[ $_continue == true ]]; then
|
|
|
|
# Populates account and blockchain explorer's shared rules/preprocess
|
|
# (at top-level, along with all the account's shared rules/preprocess)
|
|
lib_utils::print_debug "Copying '${_template_path}/*' to '${_subprofile_path}/'"
|
|
cp -a -R --backup --suffix="_${global_suffix}" \
|
|
"${_template_path}"/* "$_subprofile_path/" || lib_utils::die_fatal
|
|
|
|
# If not blockchain explorers (since those exist as subaccounts)
|
|
if [[ ! "$_template_path" =~ "blockchain-explorers" ]]; then
|
|
|
|
# Add year to 1-in
|
|
while read _in_path; do
|
|
local _year_path
|
|
_year_path="${_in_path}/$(date +%Y)"
|
|
lib_utils::print_debug "Making year path '${_year_path}'"
|
|
mkdir -p "$_year_path"
|
|
|
|
# Appropriate test data if testing
|
|
local _mockup="${_in_path}/mockup"
|
|
lib_utils::print_debug "Getting mockup '${_mockup}'"
|
|
|
|
if [[ $_is_testing == true ]]; then
|
|
lib_utils::print_debug "Copying mockup to '${_in_path}'"
|
|
cp -a -R --backup --suffix="_${global_suffix}" "${_mockup}"/* "${_in_path}/" || lib_utils::die_fatal
|
|
fi
|
|
|
|
# Always remove original test data
|
|
rm -fr "$_mockup"
|
|
|
|
done < <(find "$_account_path" -name 1-in)
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
# vim: sw=2 sts=2 si ai et
|