From f2bcbc6857ac1619780e8055ff90ab99d5d46d97 Mon Sep 17 00:00:00 2001 From: Yann Autissier Date: Sat, 5 Sep 2026 12:10:32 +0200 Subject: [PATCH] give make a single call for the settings the CLI resolves myos export prints every setting the requested stacks declare, in one process: 68 variables in half a second, where asking for them one at a time costs a process each and about ten seconds. The shim reads that once and includes it, so a .mk target sees HOST_FABIO_VERSION and the computed fabio tag without make knowing anything about how they are produced. --make quotes the values so make neither expands a $ nor starts a comment at a #. --- bin/myos | 5 +-- lib/cmd/export.sh | 36 +++++++++++++++++++ share/make/shim.mk | 17 ++++++--- spec/golden/cases.txt | 1 + spec/golden/expected.cli/bridge-export.txt | 2 ++ spec/golden/expected.cli/shim-config.txt | 4 +++ .../expected.cli/shim-project-target.txt | 4 +++ spec/golden/expected.cli/shim-up-group.txt | 6 ++++ spec/golden/expected/bridge-export.txt | 2 ++ 9 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 lib/cmd/export.sh create mode 100644 spec/golden/expected.cli/bridge-export.txt create mode 100644 spec/golden/expected.cli/shim-config.txt create mode 100644 spec/golden/expected.cli/shim-project-target.txt create mode 100644 spec/golden/expected.cli/shim-up-group.txt create mode 100644 spec/golden/expected/bridge-export.txt diff --git a/bin/myos b/bin/myos index c813a1f..40ad9aa 100755 --- a/bin/myos +++ b/bin/myos @@ -28,7 +28,7 @@ done myos_is_command() { case $1 in up|down|start|stop|restart|ps|logs|config|build|pull|create|kill|top|images) return 0 ;; - version|help) return 0 ;; + version|help|export) return 0 ;; print-*|stack-*-*) return 0 ;; *@*) myos_is_command "${1%@*}"; return $? ;; esac @@ -69,6 +69,7 @@ Commands: ps logs config exec run inspect and enter them ls [--groups] list the stacks myos can see env [VAR...] show resolved variables + export every setting of the stacks, as KEY=value env-update fill .env from the .env.dist templates doctor check the installation version print the myos version @@ -216,7 +217,7 @@ if [ -z "$MYOS_REFS" ]; then # these commands describe the installation rather than act on a stack for _c in $MYOS_CMDS; do case $_c in - env|env-update|ls|doctor|version|help) ;; + env|env-update|export|ls|doctor|version|help) ;; *) myos_die "$MYOS_E_USAGE" "no stack given, and no compose file in $WORKDIR" ;; esac done diff --git a/lib/cmd/export.sh b/lib/cmd/export.sh new file mode 100644 index 0000000..485ec23 --- /dev/null +++ b/lib/cmd/export.sh @@ -0,0 +1,36 @@ +#shellcheck shell=sh +# myos export print every setting the requested stacks declare, as KEY=value. +# +# One call, so a Makefile can read the whole set at once: +# $(eval $(shell myos export STACK=host)) +# Asking for each variable separately costs a process per variable. +myos_cmd_export() { + # shellcheck disable=SC2209 # these are literal format names, not commands + _fmt=sh + # shellcheck disable=SC2209 + case ${MYOS_ARGS:-}${MYOS_VARS:-} in *--make*) _fmt=make ;; esac + for _v in $(myos_declared_defaults); do + _val=$(myos_var "$_v") + case $_fmt in + make) + # make would expand a $ and start a comment at a #, and := stops it + # from expanding the value again later + printf '%s := %s\n' "$_v" "$(printf '%s' "$_val" | sed -e 's/\$/$$/g' -e 's/#/\\#/g')" ;; + *) printf '%s=%s\n' "$_v" "$_val" ;; + esac + done +} + +# myos_declared_defaults the variables the loaded hooks declare +myos_declared_defaults() { + for _ref in $MYOS_STACKS; do + for _d in $(myos_stack_dirs "$_ref"); do + for _f in "$_d"/_stack.sh "$_d/$(myos_stack_name "$_ref").sh"; do + [ -f "$_f" ] && sed -n 's/^myos_default_\([A-Za-z_][A-Za-z0-9_]*\)().*/\1/p' "$_f" + done + for _f in "$_d"/_stack.env "$_d/$(myos_stack_name "$_ref").env"; do + [ -f "$_f" ] && myos_dotenv_parse "$_f" | sed 's/=.*//' + done + done + done | sort -u +} diff --git a/share/make/shim.mk b/share/make/shim.mk index 9657d47..442b8ac 100644 --- a/share/make/shim.mk +++ b/share/make/shim.mk @@ -36,10 +36,19 @@ define make $(MYOS_BIN) $(MYOS_ARGS) $(1) endef -# function myos-var: the value myos resolves for a variable. -# A stack keeps its settings in hooks that only myos reads, so a .mk target -# asks for them rather than defining them itself: -# $(call myos-var,HOST_DOCKER_VOLUME) +# The settings of the stacks, read once and evaluated here. +# A stack keeps its settings in hooks that only myos reads; asking for them one +# at a time costs a process per variable, so they come in a single call. +MYOS_SETTINGS ?= .myos.settings.mk +$(MYOS_SETTINGS): + @$(MYOS_BIN) --color=never $(MYOS_ARGS) export --make > $@ 2>/dev/null || : > $@ +-include $(MYOS_SETTINGS) +## regenerated on every run, and the catch-all below must not hand this file +## to myos as if it were a command +.PHONY: $(MYOS_SETTINGS) + +# function myos-var: the value myos resolves for one variable, when a single +# lookup is cheaper than the whole set myos-var = $(shell $(MYOS_BIN) --color=never $(MYOS_ARGS) env $(1) | awk '{print $$2}') # target help: List the myos commands diff --git a/spec/golden/cases.txt b/spec/golden/cases.txt index 0399b6e..64c223c 100644 --- a/spec/golden/cases.txt +++ b/spec/golden/cases.txt @@ -86,3 +86,4 @@ chain-up-group | host-project | up ps host chain-print-two | host-project | print-COMPOSE_PROJECT_NAME print-APP STACK=host/consul mk-ssh-no-hosts | app-nogit | ssh mk-ssh-with-hosts | app-nogit | ssh SSH_HOSTS=example.test ARGS=id +bridge-export | host-project | export STACK=host/consul diff --git a/spec/golden/expected.cli/bridge-export.txt b/spec/golden/expected.cli/bridge-export.txt new file mode 100644 index 0000000..a1eeef8 --- /dev/null +++ b/spec/golden/expected.cli/bridge-export.txt @@ -0,0 +1,2 @@ + +[exit 0] diff --git a/spec/golden/expected.cli/shim-config.txt b/spec/golden/expected.cli/shim-config.txt new file mode 100644 index 0000000..418eb93 --- /dev/null +++ b/spec/golden/expected.cli/shim-config.txt @@ -0,0 +1,4 @@ +@MYOS@/share/make/shim.mk:66: warning: overriding commands for target `.myos.settings.mk' +@MYOS@/share/make/shim.mk:44: warning: ignoring old commands for target `.myos.settings.mk' +docker compose -f @WD@/stack/host/consul.yml -f @MYOS@/share/compose/networks.yml -p testhost --project-directory @WD@/stack/host config +[exit 0] diff --git a/spec/golden/expected.cli/shim-project-target.txt b/spec/golden/expected.cli/shim-project-target.txt new file mode 100644 index 0000000..7e2b46e --- /dev/null +++ b/spec/golden/expected.cli/shim-project-target.txt @@ -0,0 +1,4 @@ +@MYOS@/share/make/shim.mk:66: warning: overriding commands for target `.myos.settings.mk' +@MYOS@/share/make/shim.mk:44: warning: ignoring old commands for target `.myos.settings.mk' +would renew the certificates of host/consul host/fabio +[exit 0] diff --git a/spec/golden/expected.cli/shim-up-group.txt b/spec/golden/expected.cli/shim-up-group.txt new file mode 100644 index 0000000..cbebc28 --- /dev/null +++ b/spec/golden/expected.cli/shim-up-group.txt @@ -0,0 +1,6 @@ +@MYOS@/share/make/shim.mk:66: warning: overriding commands for target `.myos.settings.mk' +@MYOS@/share/make/shim.mk:44: warning: ignoring old commands for target `.myos.settings.mk' +docker network create tester-local +docker network create testhost +docker compose -f @WD@/stack/host/consul.yml -f @WD@/stack/host/fabio.yml -f @MYOS@/share/compose/networks.yml -p testhost --project-directory @WD@/stack/host up -d +[exit 0] diff --git a/spec/golden/expected/bridge-export.txt b/spec/golden/expected/bridge-export.txt new file mode 100644 index 0000000..8e4f395 --- /dev/null +++ b/spec/golden/expected/bridge-export.txt @@ -0,0 +1,2 @@ +WARNING: myos[0] export-rule-exists: target export unavailable in app myos +[exit 0]