From 7111df14d36b9cf825ac3f4891ee929e0fca63ec Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Mon, 2 Mar 2026 15:16:19 -0800 Subject: [PATCH 1/2] client: add custom compose merge file functionality Like custom build functionality (custom Dockerfile), adds support for custom docker-compose.yml functionality: * Adds default custom compose file * Adds to run-time environment * Adds to `gen` and `edit` --- .../dev-tools/docker-compose.yml.dev-tools.in | 32 ++++++++++++ .../finance/docker-compose.yml.archlinux.in | 32 ++++++++++++ .../finance/docker-compose.yml.ubuntu.in | 32 ++++++++++++ .../src/docker/lib/internal/lib_docker.bash | 25 ++++++--- client/src/docker/lib/internal/lib_env.bash | 14 ++++- client/src/docker/lib/internal/lib_gen.bash | 52 ++++++++++++++++--- 6 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 client/conf.d/client/Dockerfiles/dev-tools/docker-compose.yml.dev-tools.in create mode 100644 client/conf.d/client/Dockerfiles/finance/docker-compose.yml.archlinux.in create mode 100644 client/conf.d/client/Dockerfiles/finance/docker-compose.yml.ubuntu.in diff --git a/client/conf.d/client/Dockerfiles/dev-tools/docker-compose.yml.dev-tools.in b/client/conf.d/client/Dockerfiles/dev-tools/docker-compose.yml.dev-tools.in new file mode 100644 index 0000000..408901c --- /dev/null +++ b/client/conf.d/client/Dockerfiles/dev-tools/docker-compose.yml.dev-tools.in @@ -0,0 +1,32 @@ +# docker-finance | modern accounting for the power-user +# +# Copyright (C) 2021-2024,2026 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 . + +# docker-finance @DOCKER_FINANCE_VERSION@ + +## This is a custom compose file that will be *merged* with the base compose file +## See https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/ for details + +## An example of adding a custom volume and environment variables +## NOTE: *MUST* also add these variables to the env file (`edit type=env`) +#services: +# docker-finance: +# volumes: +# - ${DOCKER_FINANCE_CLIENT_TESTING}:${DOCKER_FINANCE_CONTAINER_TESTING} +# environment: +# - DOCKER_FINANCE_CONTAINER_TESTING=${DOCKER_FINANCE_CONTAINER_TESTING} + +# vim: sw=2 sts=2 si ai et diff --git a/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.archlinux.in b/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.archlinux.in new file mode 100644 index 0000000..408901c --- /dev/null +++ b/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.archlinux.in @@ -0,0 +1,32 @@ +# docker-finance | modern accounting for the power-user +# +# Copyright (C) 2021-2024,2026 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 . + +# docker-finance @DOCKER_FINANCE_VERSION@ + +## This is a custom compose file that will be *merged* with the base compose file +## See https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/ for details + +## An example of adding a custom volume and environment variables +## NOTE: *MUST* also add these variables to the env file (`edit type=env`) +#services: +# docker-finance: +# volumes: +# - ${DOCKER_FINANCE_CLIENT_TESTING}:${DOCKER_FINANCE_CONTAINER_TESTING} +# environment: +# - DOCKER_FINANCE_CONTAINER_TESTING=${DOCKER_FINANCE_CONTAINER_TESTING} + +# vim: sw=2 sts=2 si ai et diff --git a/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.ubuntu.in b/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.ubuntu.in new file mode 100644 index 0000000..408901c --- /dev/null +++ b/client/conf.d/client/Dockerfiles/finance/docker-compose.yml.ubuntu.in @@ -0,0 +1,32 @@ +# docker-finance | modern accounting for the power-user +# +# Copyright (C) 2021-2024,2026 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 . + +# docker-finance @DOCKER_FINANCE_VERSION@ + +## This is a custom compose file that will be *merged* with the base compose file +## See https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/ for details + +## An example of adding a custom volume and environment variables +## NOTE: *MUST* also add these variables to the env file (`edit type=env`) +#services: +# docker-finance: +# volumes: +# - ${DOCKER_FINANCE_CLIENT_TESTING}:${DOCKER_FINANCE_CONTAINER_TESTING} +# environment: +# - DOCKER_FINANCE_CONTAINER_TESTING=${DOCKER_FINANCE_CONTAINER_TESTING} + +# vim: sw=2 sts=2 si ai et diff --git a/client/src/docker/lib/internal/lib_docker.bash b/client/src/docker/lib/internal/lib_docker.bash index 581962c..caf92a3 100644 --- a/client/src/docker/lib/internal/lib_docker.bash +++ b/client/src/docker/lib/internal/lib_docker.bash @@ -66,6 +66,8 @@ function lib_docker::__docker() # Generate docker-compose.yml # + # NOTE: custom compose file merging happens at runtime + local _path="${global_repo_dockerfiles}/docker-compose.yml" lib_utils::print_debug "Generating '${_path}'" @@ -83,12 +85,16 @@ function lib_docker::__docker_compose() { [ -z "$global_env_file" ] && lib_utils::die_fatal [ -z "$global_repo_dockerfiles" ] && lib_utils::die_fatal + [ -z "$global_custom_composefile" ] && lib_utils::die_fatal [ ! -f "$global_env_file" ] \ - && lib_utils::die_fatal "$global_env_file not found! Run the gen command!" + && lib_utils::die_fatal "$global_env_file not found! Run the \`gen\` command!" + + [ ! -f "$global_custom_composefile" ] \ + && lib_utils::die_fatal "$global_custom_composefile not found! Run \`gen type${global_arg_delim_2}compose\`" pushd "$global_repo_dockerfiles" 1>/dev/null || return $? - docker compose -f docker-compose.yml --env-file "$global_env_file" "$@" || return $? + docker compose -f docker-compose.yml -f "$global_custom_composefile" --env-file "$global_env_file" "$@" || return $? popd 1>/dev/null || return $? } @@ -546,7 +552,7 @@ function lib_docker::__parse_args_edit() Configuration type: - type${global_arg_delim_2} + type${global_arg_delim_2} \e[32mExamples:\e[0m @@ -556,11 +562,11 @@ function lib_docker::__parse_args_edit() \e[37;2m# Edit client/container shell (superscript) \e[0m $ $global_usage type${global_arg_delim_2}shell - \e[37;2m# Edit client's custom Dockerfile (appended to final Dockerfile) \e[0m - $ $global_usage type${global_arg_delim_2}build + \e[37;2m# Edit client's custom Dockerfile (appended to final Dockerfile) and custom docker-compose.yml \e[0m + $ $global_usage type${global_arg_delim_2}build${global_arg_delim_3}compose \e[37;2m# Previous commands with alternate wordings\e[0m - $ $global_usage type${global_arg_delim_2}superscript${global_arg_delim_3}dockerfile${global_arg_delim_3}env + $ $global_usage type${global_arg_delim_2}superscript${global_arg_delim_3}dockerfile${global_arg_delim_3}env${global_arg_delim_3}compose " if [ $# -ne 1 ]; then @@ -593,7 +599,7 @@ function lib_docker::__parse_args_edit() read -ra _read <<<"$_arg_type" for _type in "${_read[@]}"; do - if [[ ! "$_type" =~ ^env$|^shell$|^superscript$|^build$|^dockerfile$ ]]; then + if [[ ! "$_type" =~ ^env$|^shell$|^superscript$|^build$|^dockerfile$|^compose$ ]]; then lib_utils::die_usage "$_usage" fi done @@ -638,6 +644,11 @@ function lib_docker::__edit() _paths+=("$global_custom_dockerfile") ;; + compose) + [ -z "$global_custom_composefile" ] && lib_utils::die_fatal + + _paths+=("$global_custom_composefile") + ;; esac done diff --git a/client/src/docker/lib/internal/lib_env.bash b/client/src/docker/lib/internal/lib_env.bash index 2f5ec38..8f69ca5 100644 --- a/client/src/docker/lib/internal/lib_env.bash +++ b/client/src/docker/lib/internal/lib_env.bash @@ -208,6 +208,12 @@ function lib_env::__set_client_globals() && lib_utils::die_fatal "Missing default custom Dockerfile '${global_repo_custom_dockerfile}'" lib_utils::print_debug "global_repo_custom_dockerfile=${global_repo_custom_dockerfile}" + # Base custom end-user .in docker-compose.yml + declare -g global_repo_custom_composefile="${global_repo_conf_dir}/client/Dockerfiles/${global_platform_image}/docker-compose.yml.${global_platform}.in" + [ ! -f "$global_repo_custom_composefile" ] \ + && lib_utils::die_fatal "Missing default custom docker-compose.yml '${global_repo_custom_composefile}'" + lib_utils::print_debug "global_repo_custom_composefile=${global_repo_custom_composefile}" + # # Client-side env # @@ -226,12 +232,18 @@ function lib_env::__set_client_globals() global_env_file="${_client_env_dir}/${global_conf_filename}" lib_utils::print_debug "global_env_file=${global_env_file}" - # Custom Dockerfile (if available) + # Custom docker files (docker related) location local _client_dockerfile_dir="${DOCKER_FINANCE_CLIENT_CONF}/${global_tag_dir}/Dockerfiles" [ ! -d "$_client_dockerfile_dir" ] && mkdir -p "$_client_dockerfile_dir" + + # Custom Dockerfile global_custom_dockerfile="${_client_dockerfile_dir}/${global_conf_filename}" lib_utils::print_debug "global_custom_dockerfile=${global_custom_dockerfile}" + # Custom docker-compose.yml + global_custom_composefile="${_client_dockerfile_dir}/${global_conf_filename}.yml" + lib_utils::print_debug "global_custom_composefile=${global_custom_composefile}" + # NOTE: # # Client env tag format is avoided because: diff --git a/client/src/docker/lib/internal/lib_gen.bash b/client/src/docker/lib/internal/lib_gen.bash index 09091b8..213b4e7 100644 --- a/client/src/docker/lib/internal/lib_gen.bash +++ b/client/src/docker/lib/internal/lib_gen.bash @@ -64,7 +64,7 @@ function lib_gen::__parse_args() Category type: - type${global_arg_delim_2} + type${global_arg_delim_2} Flow (only): @@ -111,11 +111,11 @@ function lib_gen::__parse_args() \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 custom Dockerfile, docker-compose.yml and joint client/container superscript\e[0m + $ $global_usage type${global_arg_delim_2}build${global_arg_delim_3}compose${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 + $ $global_usage type${global_arg_delim_2}env${global_arg_delim_3}build${global_arg_delim_3}compose${global_arg_delim_3}superscript \e[37;2m#\e[0m \e[37;2m# Flow: Profile -> Configs / Accounts\e[0m @@ -279,7 +279,7 @@ function lib_gen::__parse_args() if [ ! -z "$_arg_type" ]; then read -ra _read <<<"$_arg_type" for _arg in "${_read[@]}"; do - if [[ ! "$_arg" =~ ^env$|^build$|^flow$|^superscript$ ]]; then + if [[ ! "$_arg" =~ ^env$|^build$|^compose$|^flow$|^superscript$ ]]; then lib_utils::die_usage "$_usage" fi done @@ -440,6 +440,9 @@ function lib_gen::__gen_client() fi + # Filter to de-clutter output file (license cleanup) + local -r _filter="1,17d" + # # Custom (optional) Dockerfile # @@ -467,9 +470,6 @@ function lib_gen::__gen_client() lib_utils::print_debug "$global_custom_dockerfile" fi - # Filter to de-clutter output file (license cleanup) - local -r _filter="1,17d" - sed \ -e "$_filter" \ -e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \ @@ -479,6 +479,42 @@ function lib_gen::__gen_client() lib_gen::__gen_edit "$global_custom_dockerfile" fi + # + # Custom (optional) docker-compose.yml + # + + if [[ -z "${global_arg_type[*]}" || "${global_arg_type[*]}" =~ compose ]]; then + + [ -z "$global_custom_composefile" ] && lib_utils::die_fatal + [ -z "$global_repo_custom_composefile" ] && lib_utils::die_fatal + + # Backup existing custom compose file + if [ -f "$global_custom_composefile" ]; then + lib_utils::print_custom " \e[32m│\e[0m\n" + lib_utils::print_custom " \e[32m├─\e[34;1m Custom docker-compose.yml 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_composefile" "${global_custom_composefile}_${global_suffix}" || 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 docker-compose.yml\e[0m\n" + + lib_utils::print_debug "$global_repo_custom_composefile" + lib_utils::print_debug "$global_custom_composefile" + fi + + sed \ + -e "$_filter" \ + -e "s:@DOCKER_FINANCE_VERSION@:${global_client_version}:g" \ + "$global_repo_custom_composefile" >"$global_custom_composefile" || lib_utils::die_fatal + + lib_utils::print_custom " \e[32m│ └─\e[34m Edit file now? [Y/n] \e[0m" + lib_gen::__gen_edit "$global_custom_composefile" + fi + # # Generate client-side `plugins` layout (custom) # From ef6dac4f4755da1bdc3d38a4861752d622a4c89d Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Tue, 3 Mar 2026 10:06:36 -0800 Subject: [PATCH 2/2] repo: gitea: workflows: dfi: add tests for custom compose merge file --- .gitea/workflows/dfi.bash | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/dfi.bash b/.gitea/workflows/dfi.bash index e73b40c..940de22 100755 --- a/.gitea/workflows/dfi.bash +++ b/.gitea/workflows/dfi.bash @@ -85,7 +85,7 @@ function client::install() function client::finance::gen() { local -r _tags=("micro" "tiny" "slim" "default") - local -r _types=("env" "build" "superscript" "env,build,superscript") + local -r _types=("env" "build" "compose" "superscript" "env,build,compose,superscript") for _tag in "${_tags[@]}"; do for _type in "${_types[@]}"; do @@ -112,10 +112,16 @@ function client::finance::edit() types+=("env") types+=("shell" "superscript") types+=("build" "dockerfile") + types+=("compose") types+=("env,shell,build") - types+=("env,superscript,dockerfile") + types+=("env,superscript,dockerfile,compose") declare -r types + # The expected default path for default finance image + local _path + _path="${HOME:?}/docker-finance/conf.d/client/$(uname -s)-$(uname -m)/archlinux/default" + declare -r _path + for _tag in "${tags[@]}"; do for _type in "${types[@]}"; do local _edit="dfi archlinux/${USER}:${_tag} edit" @@ -127,12 +133,25 @@ function client::finance::edit() # Valid "${ci_shell[@]}" "$_edit type=${_type} & wait ; kill -9 %1" done - # Build: uncomment all optional packages and plugin dependencies + if [[ "$_tag" == "default" ]]; then local _file - _file="${HOME:?}/docker-finance/conf.d/client/$(uname -s)-$(uname -m)/archlinux/default/Dockerfiles/${USER:?}@$(uname -n)" + + # Build: uncomment all optional packages and plugin dependencies from custom build file + _file="${_path}/Dockerfiles/${USER:?}@$(uname -n)" [ ! -f "$_file" ] && exit 1 sed -i '18,56s/#//' "$_file" + + # Build: uncomment custom compose merge file + _file+=".yml" + [ ! -f "$_file" ] && exit 1 + sed -i '8,13s/#//' "$_file" + + # env: add variables used in custom compose merge file + _file="${_path}/env/${USER:?}@$(uname -n)" + [ ! -f "$_file" ] && exit 1 + echo "DOCKER_FINANCE_CLIENT_TESTING=/tmp/testing" >>$_file + echo "DOCKER_FINANCE_CONTAINER_TESTING=/tmp/testing" >>$_file fi done } @@ -722,7 +741,7 @@ function container::finance::root() function client::dev-tools::gen() { local -r _tags=("micro" "tiny" "slim" "default") - local -r _types=("env" "build" "superscript" "env,build,superscript") + local -r _types=("env" "build" "compose" "superscript" "env,build,compose,superscript") # TODO: 'default' should only be supported for _tag in "${_tags[@]}"; do @@ -747,7 +766,8 @@ function client::dev-tools::edit() _types+=("env") _types+=("shell" "superscript") _types+=("build" "dockerfile") - _types+=("env,shell,build") + _types+=("compose") + _types+=("env,shell,build,compose") _types+=("env,superscript,dockerfile") declare -r _types