Files
docker-finance/client/src/docker/lib/internal/lib_gen.bash

1151 lines
38 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"
#
# Runtime environment handler
source "${DOCKER_FINANCE_CLIENT_REPO}/client/src/docker/lib/internal/lib_env.bash" || exit 1
# Utilities (a container library (but container is never exposed to client code))
source "${DOCKER_FINANCE_CLIENT_REPO}/container/src/finance/lib/internal/lib_utils.bash" || exit 1
#
# Implementation
#
function lib_gen::__parse_args()
{
[ -z "$global_usage" ] && lib_utils::die_fatal
[ -z "$global_arg_delim_1" ] && lib_utils::die_fatal
[ -z "$global_arg_delim_2" ] && lib_utils::die_fatal
[ -z "$global_arg_delim_3" ] && lib_utils::die_fatal
# All available hledger-flow accounts
local _accounts
mapfile -t _accounts < <(find ${DOCKER_FINANCE_CLIENT_REPO}/container/src/hledger-flow/accounts -name template | sort | rev | cut -d/ -f2 | rev)
declare -r _accounts
local -r _usage="
\e[32mDescription:\e[0m
Generate environment and configurations
\e[32mUsage:\e[0m
$ $global_usage <<[all${global_arg_delim_2}<type1[{${global_arg_delim_2}type2${global_arg_delim_3}...}]>] [type${global_arg_delim_3}<type1[${global_arg_delim_3}type2...]>]> [<[profile${global_arg_delim_2}<profile>]> [config${global_arg_delim_2}<config1[${global_arg_delim_3}config2...]>] [account${global_arg_delim_2}<account1[${global_arg_delim_3}account2...]>]] [confirm${global_arg_delim_2}<{on|yes|true} | {off|no|false}>] [dev${global_arg_delim_2}<{on|yes|true} | {off|no|false}>]
\e[32mArguments:\e[0m
All categories and data:
all${global_arg_delim_2}<all | type>
Note: 'type' is currently the same as 'all'
Category type:
type${global_arg_delim_2}<env | build | flow | superscript>
Flow (only):
Full profile (w/ subprofile):
profile${global_arg_delim_2}<parent${global_arg_delim_1}child>
Configuration type:
config${global_arg_delim_2}<fetch | hledger | meta | subscript>
Accounts:
account${global_arg_delim_2}<$(echo "${_accounts[@]}" | sed 's: : | :g')>
Enable developer profile w/ mockups:
dev${global_arg_delim_2}<{on|yes|true} | {off|no|false}> # (default disabled)
Enable confirmations:
confirm${global_arg_delim_2}<{on|yes|true} | {off|no|false}> # (default enabled)
\e[32mExamples:\e[0m
\e[37;2m#\e[0m
\e[37;2m# All (client, container)\e[0m
\e[37;2m#\e[0m
\e[37;2m# Generate all client and container data\e[0m
$ $global_usage all${global_arg_delim_2}all
\e[37;2m# Generate all client and container data, skipping confirmations (using a default profile/subprofile name)\e[0m
$ $global_usage all${global_arg_delim_2}all confirm${global_arg_delim_2}no
\e[37;2m# Generate all client and container data for profile called 'parent/child', skipping confirmations\e[0m
$ $global_usage all${global_arg_delim_2}all profile${global_arg_delim_2}parent${global_arg_delim_1}child confirm${global_arg_delim_2}off
\e[37;2m#\e[0m
\e[37;2m# Client (joint container)\e[0m
\e[37;2m#\e[0m
\e[37;2m# Generate only the Docker environment\e[0m
$ $global_usage type${global_arg_delim_2}env
\e[37;2m# Generate custom Dockerfile and joint client/container superscript\e[0m
$ $global_usage type${global_arg_delim_2}build${global_arg_delim_3}superscript
\e[37;2m# Generate all client related data\e[0m
$ $global_usage type${global_arg_delim_2}env${global_arg_delim_3}build${global_arg_delim_3}superscript
\e[37;2m#\e[0m
\e[37;2m# Flow: Profile -> Configs / Accounts\e[0m
\e[37;2m#\e[0m
\e[37;2m# Generate all client related and all flow related for profile/subprofile called 'parent/child'\e[0m
$ $global_usage type${global_arg_delim_2}env${global_arg_delim_3}build${global_arg_delim_3}superscript${global_arg_delim_3}flow profile${global_arg_delim_2}parent${global_arg_delim_1}child
\e[37;2m# Same command as above but without confirmations and with developer mockups\e[0m
$ $global_usage type${global_arg_delim_2}env${global_arg_delim_3}build${global_arg_delim_3}superscript${global_arg_delim_3}flow profile${global_arg_delim_2}parent${global_arg_delim_1}child confirm${global_arg_delim_2}false dev${global_arg_delim_2}true
\e[37;2m# Generate only the given configurations for 'parent/child'\e[0m
$ $global_usage type${global_arg_delim_2}flow profile${global_arg_delim_2}parent${global_arg_delim_1}child config${global_arg_delim_2}fetch${global_arg_delim_3}hledger${global_arg_delim_3}meta${global_arg_delim_3}subscript
\e[37;2m# Generate only the given accounts for 'parent/child'\e[0m
$ $global_usage type${global_arg_delim_2}flow profile${global_arg_delim_2}parent${global_arg_delim_1}child account${global_arg_delim_2}capital-one${global_arg_delim_3}chase${global_arg_delim_3}coinbase
\e[37;2m# Generate the given configs and accounts for given 'parent/child'\e[0m
$ $global_usage type${global_arg_delim_2}flow profile${global_arg_delim_2}parent${global_arg_delim_1}child config${global_arg_delim_2}meta${global_arg_delim_3}subscript account${global_arg_delim_2}ethereum-based${global_arg_delim_3}metamask
\e[32mNotes:\e[0m
- The 'profile' argument is limited to 'type' flow
- The 'config' and 'account' arguments require 'profile'
"
#
# Ensure supported arguments
#
[ $# -eq 0 ] && lib_utils::die_usage "$_usage"
for _arg in "$@"; do
[[ ! "$_arg" =~ ^all${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^type${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^profile${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^config[s]?${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^account[s]?${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^confirm${global_arg_delim_2} ]] \
&& [[ ! "$_arg" =~ ^dev${global_arg_delim_2} ]] \
&& lib_utils::die_usage "$_usage"
done
#
# Parse arguments before testing
#
# Parse key for value
for _arg in "$@"; do
local _key="${_arg%${global_arg_delim_2}*}"
local _len="$((${#_key} + 1))"
if [[ "$_key" =~ ^all$ ]]; then
local _arg_all="${_arg:${_len}}"
[ -z "$_arg_all" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^type$ ]]; then
local _arg_type="${_arg:${_len}}"
[ -z "$_arg_type" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^profile$ ]]; then
local _arg_profile="${_arg:${_len}}"
[ -z "$_arg_profile" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^config[s]?$ ]]; then
local _arg_config="${_arg:${_len}}"
[ -z "$_arg_config" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^account[s]?$ ]]; then
local _arg_account="${_arg:${_len}}"
[ -z "$_arg_account" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^confirm$ ]]; then
local _arg_confirm="${_arg:${_len}}"
[ -z "$_arg_confirm" ] && lib_utils::die_usage "$_usage"
fi
if [[ "$_key" =~ ^dev$ ]]; then
local _arg_dev="${_arg:${_len}}"
[ -z "$_arg_dev" ] && lib_utils::die_usage "$_usage"
fi
done
#
# Test for valid ordering/functionality of argument values
#
[[ -z "$_arg_all" && -z "$_arg_type" ]] && lib_utils::die_usage "$_usage"
# Arg: all
if [ ! -z "$_arg_all" ]; then
# If all= then no need for type= or config= or account=
if [[ ! -z "$_arg_type" || ! -z "$_arg_config" || ! -z "$_arg_account" ]]; then
lib_utils::die_usage "$_usage"
fi
fi
# Arg: type
# Note: check not needed
# Arg: profile
if [ ! -z "$_arg_profile" ]; then
# Requires type to be set
if [[ ! "${_arg_all[*]}" =~ all|type && -z "$_arg_type" ]]; then
lib_utils::die_usage "$_usage"
fi
fi
# Arg: config
if [ ! -z "$_arg_config" ]; then
# Requires profile to be set
if [ -z "$_arg_profile" ]; then
lib_utils::die_usage "$_usage"
fi
fi
# Arg: account
if [ ! -z "$_arg_account" ]; then
# Requires profile to be set
if [ -z "$_arg_profile" ]; then
lib_utils::die_usage "$_usage"
fi
fi
# Arg: confirm
# Note: optional argument, check not needed
# Arg: dev
# Note: optional argument, check not needed
#
# Test argument values, set globals
#
IFS="$global_arg_delim_3"
# Arg: all
if [ ! -z "$_arg_all" ]; then
read -ra _read <<<"$_arg_all"
for _arg in "${_read[@]}"; do
# Supported values
[[ ! "$_arg" =~ ^all$|^type[s]?$ ]] \
&& lib_utils::die_usage "$_usage"
# If all=all then no need for all={type}
[[ "$_arg" == "all" && "${#_read[@]}" -gt 1 ]] \
&& lib_utils::die_usage "$_usage"
done
# TODO: currently, 'all' will be equivalent 'type'
declare -gr global_arg_all=("${_read[@]}")
fi
# Arg: type
if [ ! -z "$_arg_type" ]; then
read -ra _read <<<"$_arg_type"
for _arg in "${_read[@]}"; do
if [[ ! "$_arg" =~ ^env$|^build$|^flow$|^superscript$ ]]; then
lib_utils::die_usage "$_usage"
fi
done
declare -gr global_arg_type=("${_read[@]}")
fi
# Arg: profile
if [ ! -z "$_arg_profile" ]; then
# Requires flow type
if [[ ! "${global_arg_all[*]}" =~ all|type && ! "${global_arg_type[*]}" =~ flow ]]; then
lib_utils::die_usage "$_usage"
fi
if [[ ! "$_arg_profile" =~ $global_arg_delim_1 ]]; then
lib_utils::die_usage "$_usage"
fi
declare -gr global_arg_profile="${_arg_profile%${global_arg_delim_1}*}"
declare -gr global_arg_subprofile="${_arg_profile##*${global_arg_delim_1}}"
fi
# Arg: config
if [ ! -z "$_arg_config" ]; then
# Requires profile type
if [ -z "$global_arg_profile" ]; then
lib_utils::die_usage "$_usage"
fi
read -ra _read <<<"$_arg_config"
for _arg in "${_read[@]}"; do
if [[ ! "$_arg" =~ ^fetch$|^hledger$|^meta$|^subscript$ ]]; then
lib_utils::die_usage "$_usage"
fi
done
declare -gr global_arg_config=("${_read[@]}")
fi
# Arg: account
if [ ! -z "$_arg_account" ]; then
# Requires profile type
if [ -z "$global_arg_profile" ]; then
lib_utils::die_usage "$_usage"
fi
read -ra _read <<<"$_arg_account"
# Test if given account is supported
for _arg in "${_read[@]}"; do
_found=false
for _account in "${_accounts[@]}"; do
[[ "$_arg" == "$_account" ]] && _found=true
done
[ $_found == true ] && continue || lib_utils::die_usage "$_usage"
done
declare -gr global_arg_account=("${_read[@]}")
fi
# Arg: confirm
if [ ! -z "$_arg_confirm" ]; then
if [[ ! "$_arg_confirm" =~ (^on$|^yes$|^true$)|(^off$|^no$|^false$) ]]; then
lib_utils::die_usage "$_usage"
fi
if [[ "$_arg_confirm" =~ ^off$|^no$|^false$ ]]; then
declare -gr global_arg_confirm=""
else
declare -gr global_arg_confirm="true"
fi
else
declare -gr global_arg_confirm="true"
fi
# Arg: dev
if [ ! -z "$_arg_dev" ]; then
if [[ ! "$_arg_dev" =~ (^on$|^yes$|^true$)|(^off$|^no$|^false$) ]]; then
lib_utils::die_usage "$_usage"
fi
if [[ "$_arg_dev" =~ ^on$|^yes$|^true$ ]]; then
declare -gr global_arg_dev="true"
else
declare -gr global_arg_dev=""
fi
else
declare -gr global_arg_dev=""
fi
}
#
# Generate new configurations or layout
#
function lib_gen::gen()
{
lib_gen::__parse_args "$@"
lib_utils::print_custom "\n"
lib_utils::print_info "Generating client/container environment"
lib_gen::__gen_client
if [ $? -eq 0 ]; then
[ -z "$global_platform" ] && lib_utils::die_fatal
if [ "$global_platform" != "dev-tools" ]; then
lib_gen::__gen_container
fi
fi
if [ $? -eq 0 ]; then
lib_utils::print_custom "\n"
lib_utils::print_info "Congratulations, environment sucessfully generated!"
lib_utils::print_custom "\n"
else
lib_utils::print_custom "\n"
lib_utils::die_fatal "Environment not fully generated! Try again"
lib_utils::print_custom "\n"
fi
}
function lib_gen::__gen_client()
{
[ -z "$global_suffix" ] && lib_utils::die_fatal
lib_utils::print_debug "Generating client"
#
# Client-side environment file
#
if [[ -z "${global_arg_type[*]}" || "${global_arg_type[*]}" =~ env ]]; then
[ -z "$global_env_file" ] && lib_utils::die_fatal
[ -z "$global_repo_env_file" ] && lib_utils::die_fatal
# 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 environment file found, backup then generate new one? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
local _confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; 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_env::__read "$global_repo_env_file"
lib_utils::print_debug "Writing $global_env_file"
lib_env::__write "$global_env_file"
fi
fi
lib_utils::print_custom " \e[32m│ └─\e[34m Edit file now? [Y/n] \e[0m"
lib_gen::__gen_edit "$global_env_file"
# Get/Set new (edited) environment variables
lib_env::__read "$global_env_file"
lib_env::__set_client_globals
fi
#
# Custom (optional) Dockerfile
#
if [[ -z "${global_arg_type[*]}" || "${global_arg_type[*]}" =~ build ]]; then
[ -z "$global_custom_dockerfile" ] && lib_utils::die_fatal
[ -z "$global_repo_custom_dockerfile" ] && lib_utils::die_fatal
# 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"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
local _confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; 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 file now? [Y/n] \e[0m"
lib_gen::__gen_edit "$global_custom_dockerfile"
fi
}
function lib_gen::__gen_container()
{
lib_utils::print_debug "Generating container"
#
# Generate joint client/container superscript
#
if [[ -z "${global_arg_type[*]}" || "${global_arg_type[*]}" =~ superscript ]]; then
lib_utils::print_custom " \e[32m│\e[0m\n"
lib_utils::print_custom " \e[32m├─\e[34;1m Generate joint client/container superscript? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; then
lib_gen::__gen_superscript
fi
fi
#
# 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
#
if [[ "${global_arg_all[*]}" =~ all|type || "${global_arg_type[*]}" =~ flow ]]; then
lib_utils::print_custom " \e[32m│\e[0m\n"
lib_utils::print_custom " \e[32m├─\e[34;1m Generate container finance flow (layout and profiles)? [Y/n] \e[0m"
# Prompt if type not given
if [[ ! "${global_arg_all[*]}" =~ all|type || -z "${global_arg_type[*]}" ]]; then
if [ -z "$global_arg_confirm" ]; then
lib_utils::print_custom "\n"
else
read -p "" _read
local _confirm="${_read:-y}"
if [[ "$_confirm" != [yY] ]]; then
lib_utils::print_custom " \e[32m│\e[0m\n"
return 0
fi
fi
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
fi
}
#
# Generate joint client/container superscript
#
function lib_gen::__gen_superscript()
{
[ -z "$global_superscript" ] && lib_utils::die_fatal
if [ -f "$global_superscript" ]; then
lib_utils::print_custom " \e[32m│ └─\e[34m Backup existing file and generate a new one? [N/y] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
local _confirm="${_read:-n}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; then
# Backup
local -r _backup=("cp" "-a" "$global_superscript" "${global_superscript}_${global_suffix}")
lib_utils::print_debug "${_backup[@]}"
"${_backup[@]}" || lib_utils::die_fatal
# Write
lib_gen::__gen_superscript_write
fi
local _print_custom=" \e[32m│ └─\e[34m Edit file now? [Y/n] \e[0m"
else
# Generate new default file
lib_gen::__gen_superscript_write
local _print_custom=" \e[32m│ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
lib_utils::print_custom "$_print_custom"
lib_gen::__gen_edit "$global_superscript"
}
function lib_gen::__gen_superscript_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}"
[ -z "$global_superscript" ] && lib_utils::die_fatal
lib_utils::print_debug "global_superscript=${global_superscript}"
sed \
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
"${global_repo_conf_dir}/container/shell/superscript.bash.in" >"$global_superscript"
}
#
# Generate plugins (custom):
#
# - Provides:
# - A layout to drop-in custom plugins
# * Underlying impl expects this layout
#
function lib_gen::__gen_plugins()
{
[ -z "$DOCKER_FINANCE_CLIENT_PLUGINS" ] && lib_utils::die_fatal
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: construct full path to subprofile
#
[ -z "$global_user" ] && lib_utils::die_fatal
# Profile
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"
if [ ! -z "$global_arg_profile" ]; then
lib_utils::print_custom "\n"
_read="$global_arg_profile"
else
if [ -z "$global_arg_confirm" ]; then
lib_utils::print_custom "\n"
_read=""
else
read -p "" _read
fi
fi
local -r _profile="${_read:-default}"
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Using profile:\e[0m ${_profile}\n"
# Subprofile
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"
if [ ! -z "$global_arg_subprofile" ]; then
lib_utils::print_custom "\n"
_read="$global_arg_subprofile"
else
if [ -z "$global_arg_confirm" ]; then
lib_utils::print_custom "\n"
_read=""
else
read -p "" _read
fi
fi
local -r _subprofile="${_read:-${global_user}}"
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Using subprofile:\e[0m ${_subprofile}\n"
# Check if full profile exists
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"
if [ -z "$global_arg_confirm" ]; then
lib_utils::print_custom "\n"
_read=""
else
read -p "" _read
fi
_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: generate profile-specific
#
local -r _gen_path="${_profiles}/${_profile}/${_subprofile}"
lib_utils::print_debug "_gen_path=${_gen_path}"
local -r _gen_conf_path="${_gen_path}/docker-finance.d"
lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
lib_gen::__gen_subprofile_flow
}
#
# Subprofile: generate flow
#
function lib_gen::__gen_subprofile_flow()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" ]] && lib_utils::die_fatal
if lib_gen::__gen_subprofile_flow_args_config "subscript"; then
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate subprofile's subscript file? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; then
# Subprofile's shell script
lib_gen::__gen_subprofile_flow_subscript
# Append subprofile source to superscript
lib_gen::__gen_subprofile_flow_superscript
fi
fi
if lib_gen::__gen_subprofile_flow_args_config "fetch"; then
# Prompt for default generation
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate subprofile's fetch configuration file? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] && lib_gen::__gen_subprofile_flow_fetch
fi
if lib_gen::__gen_subprofile_flow_args_config "meta"; then
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate subprofile's financial metadata file? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] && lib_gen::__gen_subprofile_flow_meta
fi
if lib_gen::__gen_subprofile_flow_args_config "hledger"; then
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate subprofile's hledger configuration file? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] && lib_gen::__gen_subprofile_flow_hledger
fi
if lib_gen::__gen_subprofile_flow_args_account; then
lib_utils::print_custom " \e[32m│ │ │\e[0m\n"
lib_utils::print_custom " \e[32m│ │ ├─\e[34;1m Generate subprofile's hledger-flow accounts? [Y/n] \e[0m"
if [ ! -z "${global_arg_account[*]}" ]; then
lib_utils::print_custom "\n"
lib_gen::__gen_subprofile_flow_accounts
else
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] && lib_gen::__gen_subprofile_flow_accounts
fi
fi
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"
}
function lib_gen::__gen_subprofile_flow_args_config()
{
[ -z "$1" ] && lib_utils::die_fatal
if [[ -z "${global_arg_type[*]}" || ("${global_arg_type[*]}" =~ flow && -z "$global_arg_profile") || ("${global_arg_type[*]}" =~ flow && ! -z "$global_arg_profile" && -z "${global_arg_account[*]}" && -z "${global_arg_config[*]}") || "${global_arg_config[*]}" =~ $1 ]]; then
return 0
fi
return 1
}
function lib_gen::__gen_subprofile_flow_args_account()
{
if [[ -z "${global_arg_type[*]}" || ("${global_arg_type[*]}" =~ flow && -z "$global_arg_profile") || ("${global_arg_type[*]}" =~ flow && ! -z "$global_arg_profile" && -z "${global_arg_account[*]}" && -z "${global_arg_config[*]}") || ("${global_arg_type[*]}" =~ flow && ! -z "$global_arg_profile" && ! -z "${global_arg_account[*]}") ]]; then
return 0
fi
return 1
}
#
# Subprofile: flow: generate subprofile subscript
#
function lib_gen::__gen_subprofile_flow_subscript()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" ]] && lib_utils::die_fatal
local _dir="${_gen_conf_path}/shell"
[ ! -d "$_dir" ] && mkdir -p "$_dir"
local _file="${_dir}/subscript.bash"
if [ -f "$_file" ]; then
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m File found, backup then generate new one? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; then
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
lib_gen::__gen_subprofile_flow_subscript_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
else
lib_gen::__gen_subprofile_flow_subscript_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
lib_utils::print_custom "$_print_custom"
lib_gen::__gen_edit "$_file"
}
function lib_gen::__gen_subprofile_flow_subscript_write()
{
[[ -z "$DOCKER_FINANCE_CONTAINER_REPO" || -z "$DOCKER_FINANCE_CONTAINER_CMD" ]] && lib_utils::die_fatal
[[ -z "$_profile" || -z "$_subprofile" || -z "$_file" ]] && lib_utils::die_fatal
[ -z "$global_client_version" ] && lib_utils::die_fatal
[ -z "$global_repo_conf_dir" ] && 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/subscript.bash.in" >"$_file"
}
#
# Subprofile: flow: append subscript to superscript
#
function lib_gen::__gen_subprofile_flow_superscript()
{
[[ -z "$_profile" || -z "$_subprofile" ]] && lib_utils::die_fatal
[ -z "$global_superscript" ] && lib_utils::die_fatal
[ ! -f "$global_superscript" ] && lib_utils::die_fatal "Superscript does not exist!"
# Append subprofile source to superscript
local -r _source="source \"\${DOCKER_FINANCE_CONTAINER_FLOW}/profiles/${_profile}/${_subprofile}/docker-finance.d/shell/subscript.bash\""
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Appending subprofile to superscript\e[0m\n"
# If source subprofile does not exist, append
grep "$_source" "$global_superscript" >&/dev/null \
|| sed -i "$(wc -l <$global_superscript)i\\$_source\\" "$global_superscript"
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m Edit superscript now? [Y/n] \e[0m"
lib_gen::__gen_edit "$global_superscript"
}
#
# Subprofile: flow: generate fetch config
#
function lib_gen::__gen_subprofile_flow_fetch()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" ]] && lib_utils::die_fatal
local -r _dir="${_gen_conf_path}/fetch"
[ ! -d "$_dir" ] && mkdir -p "$_dir"
local -r _file="${_dir}/fetch.yaml"
if [ -f "$_file" ]; then
lib_utils::print_custom " \e[32m│ │ │ └─\e[34m File found, backup then generate new one? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; 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 file now? [Y/n] \e[0m"
fi
else
lib_gen::__gen_subprofile_flow_fetch_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
lib_utils::print_custom "$_print_custom"
lib_gen::__gen_edit "$_file"
}
function lib_gen::__gen_subprofile_flow_fetch_write()
{
[[ -z "$_profile" || -z "$_subprofile" || -z "$_file" ]] && lib_utils::die_fatal
[ -z "$global_client_version" ] && lib_utils::die_fatal
[ -z "$global_repo_conf_dir" ] && 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()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" ]] && lib_utils::die_fatal
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 File found, backup then generate new one? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; 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 file now? [Y/n] \e[0m"
fi
else
lib_gen::__gen_subprofile_flow_meta_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
lib_utils::print_custom "$_print_custom"
lib_gen::__gen_edit "$_file"
}
function lib_gen::__gen_subprofile_flow_meta_write()
{
[ -z "$_file" ] && lib_utils::die_fatal
[ -z "$global_client_version" ] && lib_utils::die_fatal
[ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
# 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 hledger configuration file
#
function lib_gen::__gen_subprofile_flow_hledger()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" ]] && lib_utils::die_fatal
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 File found, backup then generate new one? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]]; then
cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
lib_gen::__gen_subprofile_flow_hledger_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
else
lib_gen::__gen_subprofile_flow_hledger_write
local _print_custom=" \e[32m│ │ │ └─\e[34m Edit file now? [Y/n] \e[0m"
fi
lib_utils::print_custom "$_print_custom"
lib_gen::__gen_edit "$_file"
}
function lib_gen::__gen_subprofile_flow_hledger_write()
{
[ -z "$_file" ] && lib_utils::die_fatal
[ -z "$global_client_version" ] && lib_utils::die_fatal
lib_utils::print_debug "global_client_version=${global_client_version}"
[ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
lib_utils::print_debug "global_repo_conf_dir=${global_repo_conf_dir}"
sed \
-e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \
"${global_repo_conf_dir}/container/hledger/hledger.conf.in" >"$_file"
}
#
# Subprofile: flow: generate accounts
#
function lib_gen::__gen_subprofile_flow_accounts()
{
[[ -z "$_gen_path" || -z "$_gen_conf_path" || -z "$_subprofile" ]] && lib_utils::die_fatal
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"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] || return 0
else
mkdir -p "$_subprofile_path"
fi
# All available hledger-flow account paths
local _templates_paths
mapfile -t _templates_paths < <(find "${DOCKER_FINANCE_CLIENT_REPO}"/container/src/hledger-flow/accounts -name template | sort)
declare -r _templates_paths
# All available hledger-flow account names
local _accounts
if [ -z "${global_arg_account[*]}" ]; then
for _template in "${_templates_paths[@]}"; do
_accounts+=("$(echo "$_template" | rev | cut -d/ -f2 | rev)")
done
else
declare -r _accounts=("${global_arg_account[@]}")
fi
# Cycle through all available docker-finance templates and populate subprofile
for _account in "${_accounts[@]}"; do
for _template_path in "${_templates_paths[@]}"; do
if [[ "$_template_path" =~ \/$_account\/ ]]; then
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 $_account ? [Y/n] \e[0m"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] \
&& lib_gen::__gen_subprofile_flow_accounts_populate "$_subprofile_path" "$_template_path"
fi
done
done
}
function lib_gen::__gen_subprofile_flow_accounts_populate()
{
[[ -z "$1" || -z "$2" ]] && lib_utils::die_fatal
local _subprofile_path="$1"
local _template_path="$2"
# 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"
[ -z "$global_arg_confirm" ] && lib_utils::print_custom "\n" || read -p "" _read
_confirm="${_read:-y}"
[[ "$_confirm" == [yY] || -z "$global_arg_confirm" ]] || _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 [ ! -z "$global_arg_dev" ]; 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
}
function lib_gen::__gen_edit()
{
local _file="$1"
[[ -z "$_file" || ! -f "$_file" ]] && lib_utils::die_fatal
if [ -z "$global_arg_confirm" ]; then
lib_utils::print_custom "\n"
_read="n"
else
read -p "" _read
fi
local _confirm="${_read:-y}"
[ -z "$EDITOR" ] && lib_utils::die_fatal
[[ "$_confirm" == [yY] ]] && $EDITOR "$_file" || return 0
}
# vim: sw=2 sts=2 si ai et