let a stack compute its settings without make
A stack can now ship <name>.env and <name>.sh next to its compose files. The hook is sourced with the tag helpers available, which is what the computing .mk files of the catalogue were using make for: 29 of its 44 .mk files only exist to build variables like the fabio tags. Converting stack/host/fabio.mk by hand gives byte-identical output for the route tag, and drops a trailing comma the make version left in the listener list. Also: APP_HOST and APP_URI are computed (the tag helpers build on them), --color controls the escape codes rather than always emitting them, and make test-portability runs the CLI under busybox ash and dash.
This commit is contained in:
@@ -10,6 +10,10 @@
|
|||||||
- `myos ls`, `myos env`, `myos doctor` to inspect an installation
|
- `myos ls`, `myos env`, `myos doctor` to inspect an installation
|
||||||
- `install.sh`, and the catalogue is looked up beside the installation
|
- `install.sh`, and the catalogue is looked up beside the installation
|
||||||
- agent skill in `skills/myos/`, contributor notes in `AGENTS.md`
|
- agent skill in `skills/myos/`, contributor notes in `AGENTS.md`
|
||||||
|
- stacks carry their settings in `<name>.env` and `<name>.sh` hooks, so the
|
||||||
|
catalogue no longer needs make to be installed
|
||||||
|
- `--color always|never|auto`, and no colour when the output is piped
|
||||||
|
- verified under the /bin/sh of Alpine (busybox) and Debian (dash)
|
||||||
- the make engine still works and is still covered by the golden tests
|
- the make engine still works and is still covered by the golden tests
|
||||||
|
|
||||||
## v1.1 - 2026-09-03
|
## v1.1 - 2026-09-03
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
DEV_TARGETS := test test-unit test-golden test-integration lint golden-record
|
DEV_TARGETS := test test-unit test-golden test-integration test-portability lint golden-record
|
||||||
ifneq ($(filter $(DEV_TARGETS),$(MAKECMDGOALS)),)
|
ifneq ($(filter $(DEV_TARGETS),$(MAKECMDGOALS)),)
|
||||||
|
|
||||||
SHELLSPEC ?= shellspec
|
SHELLSPEC ?= shellspec
|
||||||
@@ -16,6 +16,17 @@ test-integration: ## Run tests needing a real docker daemon
|
|||||||
MYOS_INTEGRATION=1 $(SHELLSPEC) spec/integration
|
MYOS_INTEGRATION=1 $(SHELLSPEC) spec/integration
|
||||||
golden-record: ## Re-record golden expectations from the legacy engine
|
golden-record: ## Re-record golden expectations from the legacy engine
|
||||||
spec/golden/record.sh $(CASES)
|
spec/golden/record.sh $(CASES)
|
||||||
|
test-portability: ## Run the CLI under the /bin/sh of Alpine and Debian
|
||||||
|
@for img in alpine:3.20 debian:13-slim; do \
|
||||||
|
printf '%s: ' "$$img"; \
|
||||||
|
tar cf - --exclude .git . | docker run -i --rm "$$img" /bin/sh -c \
|
||||||
|
'mkdir -p /myos && tar xf - -C /myos && cd /tmp && \
|
||||||
|
export PATH=/myos/spec/support/bin:$$PATH && \
|
||||||
|
/myos/bin/myos version >/dev/null && \
|
||||||
|
/myos/bin/myos -C /myos/spec/fixtures/host-project -n up host >/dev/null && \
|
||||||
|
echo ok'; \
|
||||||
|
done
|
||||||
|
|
||||||
lint: ## shellcheck all shell sources
|
lint: ## shellcheck all shell sources
|
||||||
$(SHELLCHECK) -s bash myos spec/golden/record.sh spec/support/run.sh spec/support/bin/* $(wildcard bin/* lib/*.sh lib/cmd/*.sh install.sh)
|
$(SHELLCHECK) -s bash myos spec/golden/record.sh spec/support/run.sh spec/support/bin/* $(wildcard bin/* lib/*.sh lib/cmd/*.sh install.sh)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ done
|
|||||||
MYOS_ROOT=$(cd "$(dirname "$_self")/.." && pwd -P)
|
MYOS_ROOT=$(cd "$(dirname "$_self")/.." && pwd -P)
|
||||||
export MYOS_ROOT
|
export MYOS_ROOT
|
||||||
|
|
||||||
for _m in core str tags naming stack config compose; do
|
for _m in core str tags naming stack config compose hooks; do
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
. "$MYOS_ROOT/lib/$_m.sh"
|
. "$MYOS_ROOT/lib/$_m.sh"
|
||||||
done
|
done
|
||||||
@@ -34,6 +34,7 @@ done
|
|||||||
MYOS_VARS=
|
MYOS_VARS=
|
||||||
MYOS_ARGS=
|
MYOS_ARGS=
|
||||||
MYOS_HOSTS=
|
MYOS_HOSTS=
|
||||||
|
MYOS_COLOR=${MYOS_COLOR:-auto}
|
||||||
VERBOSE=${VERBOSE:-}
|
VERBOSE=${VERBOSE:-}
|
||||||
DEBUG=${DEBUG:-}
|
DEBUG=${DEBUG:-}
|
||||||
}
|
}
|
||||||
@@ -47,6 +48,7 @@ Options:
|
|||||||
-e ENV environment (default: local, or ENV from the config)
|
-e ENV environment (default: local, or ENV from the config)
|
||||||
-H HOSTS run on remote hosts instead (comma separated, or "all")
|
-H HOSTS run on remote hosts instead (comma separated, or "all")
|
||||||
-n, --dry-run print the commands instead of running them
|
-n, --dry-run print the commands instead of running them
|
||||||
|
--color WHEN always, never or auto (default: colour when on a terminal)
|
||||||
-v, --verbose show what myos does
|
-v, --verbose show what myos does
|
||||||
-d, --debug show every command
|
-d, --debug show every command
|
||||||
-h, --help this help
|
-h, --help this help
|
||||||
@@ -73,6 +75,8 @@ while [ $# -gt 0 ]; do
|
|||||||
-e) ENV=$2; shift 2 ;;
|
-e) ENV=$2; shift 2 ;;
|
||||||
-H) MYOS_HOSTS=$2; shift 2 ;;
|
-H) MYOS_HOSTS=$2; shift 2 ;;
|
||||||
-n|--dry-run) DRYRUN=true; shift ;;
|
-n|--dry-run) DRYRUN=true; shift ;;
|
||||||
|
--color) MYOS_COLOR=$2; shift 2 ;;
|
||||||
|
--color=*) MYOS_COLOR=${1#--color=}; shift ;;
|
||||||
-v|--verbose) VERBOSE=true; shift ;;
|
-v|--verbose) VERBOSE=true; shift ;;
|
||||||
-d|--debug) DEBUG=true; shift ;;
|
-d|--debug) DEBUG=true; shift ;;
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
@@ -163,6 +167,21 @@ fi
|
|||||||
# shellcheck disable=SC2086 # a list of references
|
# shellcheck disable=SC2086 # a list of references
|
||||||
MYOS_STACKS=$(myos_group_expand $MYOS_REFS)
|
MYOS_STACKS=$(myos_group_expand $MYOS_REFS)
|
||||||
|
|
||||||
|
# The uri a stack is served on: the tag helpers build on it, so it has to be
|
||||||
|
# known before the hooks run.
|
||||||
|
MYOS_SCOPE_FIRST=$(for _r in $MYOS_STACKS; do myos_scope "$_r"; break; done)
|
||||||
|
MYOS_APP_FIRST=$(for _r in $MYOS_STACKS; do myos_stack_name "$_r"; break; done)
|
||||||
|
APP_HOST=${APP_HOST:-$(myos_app_host "$MYOS_SCOPE_FIRST" "$USER" "$ENV" "$MYOS_APP_FIRST" "$DOMAINNAME" "$HOSTNAME")}
|
||||||
|
APP_URI=${APP_URI:-$(myos_app_uri "$APP_HOST" "${APP_PATH:-}")}
|
||||||
|
APP_SCHEME=${APP_SCHEME:-http}
|
||||||
|
|
||||||
|
# Per-stack hooks must run in this shell: everything downstream reads the
|
||||||
|
# variables they set, and a command substitution would throw them away.
|
||||||
|
for _ref in $MYOS_STACKS; do
|
||||||
|
_hdir=$(myos_stack_resolve "$_ref" 2>/dev/null) || continue
|
||||||
|
myos_stack_hooks "$_hdir" "$(myos_stack_name "$_ref")"
|
||||||
|
done
|
||||||
|
|
||||||
# myos_framework_compose_files the networks and volumes overlays myos itself
|
# myos_framework_compose_files the networks and volumes overlays myos itself
|
||||||
# provides; they always come last so a stack can rely on them being there.
|
# provides; they always come last so a stack can rely on them being there.
|
||||||
myos_framework_compose_files() {
|
myos_framework_compose_files() {
|
||||||
|
|||||||
+32
-19
@@ -1,34 +1,47 @@
|
|||||||
#shellcheck shell=sh
|
#shellcheck shell=sh
|
||||||
# shellcheck disable=SC3028 # HOSTNAME is a myos variable, set by bin/myos
|
# shellcheck disable=SC3028 # HOSTNAME is a myos variable, set by bin/myos
|
||||||
# myos env [VAR...] show resolved variables (replaces the make print-VAR target)
|
# myos env [VAR...] show resolved variables (replaces the make print-VAR target)
|
||||||
|
|
||||||
|
# myos_env_print NAME VALUE
|
||||||
|
# Same shape as the make print-<VAR> target: the name padded to 37 columns in
|
||||||
|
# the highlight colour, then the value in the value colour.
|
||||||
|
myos_env_print() {
|
||||||
|
printf '%s%-37s%s%s%s%s\n' \
|
||||||
|
"$MYOS_C_HIGHLIGHT" "$1" "$MYOS_C_RESET" "$MYOS_C_VALUE" "$2" "$MYOS_C_RESET"
|
||||||
|
}
|
||||||
|
|
||||||
myos_cmd_env() {
|
myos_cmd_env() {
|
||||||
_vars=${MYOS_VARS:-}
|
_vars=${MYOS_VARS:-}
|
||||||
[ -n "$_vars" ] || _vars=$MYOS_ARGS
|
[ -n "$_vars" ] || _vars=$MYOS_ARGS
|
||||||
[ -n "$_vars" ] || _vars=$MYOS_REFS_RAW
|
[ -n "$_vars" ] || _vars=$MYOS_REFS_RAW
|
||||||
if [ -z "$_vars" ]; then
|
if [ -z "$_vars" ]; then
|
||||||
_vars="ENV USER HOSTNAME DOMAIN WORKDIR MYOS_PATH STACK COMPOSE_PROJECT_NAME COMPOSE_FILE"
|
_vars="ENV USER HOSTNAME DOMAIN WORKDIR MYOS_PATH SCOPE STACK COMPOSE_PROJECT_NAME COMPOSE_FILE"
|
||||||
fi
|
fi
|
||||||
for _v in $_vars; do
|
for _v in $_vars; do
|
||||||
case $_v in
|
case $_v in
|
||||||
MYOS_PATH) printf '%s %s\n' "$_v" "$(myos_path)" ;;
|
MYOS_PATH) myos_env_print "$_v" "$(myos_path)" ;;
|
||||||
COMPOSE_FILE) printf '%s %s\n' "$_v" "$(myos_all_compose_files | tr '\n' ' ' | sed 's/ $//')" ;;
|
COMPOSE_FILE) myos_env_print "$_v" "$(myos_all_compose_files | tr '\n' ' ' | sed 's/ $//')" ;;
|
||||||
COMPOSE_PROJECT_NAME) printf '%s %s\n' "$_v" "$(myos_first_project)" ;;
|
COMPOSE_PROJECT_NAME) myos_env_print "$_v" "$(myos_first_project)" ;;
|
||||||
STACK) printf '%s %s\n' "$_v" "$(printf '%s' "$MYOS_STACKS" | tr '\n' ' ' | sed 's/ $//')" ;;
|
COMPOSE_SERVICE_NAME) myos_env_print "$_v" "$(myos_service_name "$(myos_first_project)")" ;;
|
||||||
DOCKER_NETWORK_DEFAULT) printf '%s %s\n' "$_v" "$(myos_network_default "$(myos_first_project)")" ;;
|
COMPOSE_FILE_SUFFIX) myos_env_print "$_v" "$(myos_compose_suffixes)" ;;
|
||||||
DOCKER_NETWORK_PRIVATE) printf '%s %s\n' "$_v" "$(myos_network_private "$USER" "$ENV")" ;;
|
STACK) myos_env_print "$_v" "$(printf '%s' "$MYOS_STACKS" | tr '\n' ' ' | sed 's/ $//')" ;;
|
||||||
DOCKER_NETWORK_PUBLIC) printf '%s %s\n' "$_v" "$(myos_network_public "${HOSTNAME:-}")" ;;
|
SCOPE) myos_env_print "$_v" "$(myos_first_scope)" ;;
|
||||||
COMPOSE_SERVICE_NAME) printf '%s %s\n' "$_v" "$(myos_service_name "$(myos_first_project)")" ;;
|
APP|APP_NAME) myos_env_print "$_v" "$(myos_first_app)" ;;
|
||||||
COMPOSE_FILE_SUFFIX) printf '%s %s\n' "$_v" "$(myos_compose_suffixes)" ;;
|
DOCKER_REPOSITORY) myos_env_print "$_v" "$(printf '%s' "$(myos_first_project)" | tr '_-' '//')" ;;
|
||||||
APP|APP_NAME) printf '%s %s\n' "$_v" "$(myos_first_app)" ;;
|
DOCKER_NETWORK_DEFAULT) myos_env_print "$_v" "$(myos_network_default "$(myos_first_project)")" ;;
|
||||||
SCOPE) printf '%s %s\n' "$_v" "$(myos_first_scope)" ;;
|
DOCKER_NETWORK_PRIVATE) myos_env_print "$_v" "$(myos_network_private "$USER" "$ENV")" ;;
|
||||||
# HOST_STACK/USER_STACK are the names the make engine used for the scope
|
DOCKER_NETWORK_PUBLIC) myos_env_print "$_v" "$(myos_network_public "${HOSTNAME:-}")" ;;
|
||||||
HOST_STACK) [ "$(myos_first_scope)" = host ] && printf '%s host\n' "$_v" || printf '%s\n' "$_v" ;;
|
|
||||||
USER_STACK) [ "$(myos_first_scope)" = user ] && printf '%s User\n' "$_v" || printf '%s\n' "$_v" ;;
|
|
||||||
DOCKER_REPOSITORY) printf '%s %s\n' "$_v" "$(printf '%s' "$(myos_first_project)" | tr '_-' '//')" ;;
|
|
||||||
DOCKER_NETWORK)
|
DOCKER_NETWORK)
|
||||||
if [ "$(myos_first_scope)" = user ]; then printf '%s %s\n' "$_v" "$USER"
|
if [ "$(myos_first_scope)" = user ]; then myos_env_print "$_v" "$USER"
|
||||||
else printf '%s %s\n' "$_v" "$(myos_network_private "$USER" "$ENV")"; fi ;;
|
else myos_env_print "$_v" "$(myos_network_private "$USER" "$ENV")"; fi ;;
|
||||||
*) printf '%s %s\n' "$_v" "$(myos_var "$_v")" ;;
|
# HOST_STACK and USER_STACK are what the make engine called the scope
|
||||||
|
HOST_STACK)
|
||||||
|
if [ "$(myos_first_scope)" = host ]; then myos_env_print "$_v" host
|
||||||
|
else myos_env_print "$_v" ""; fi ;;
|
||||||
|
USER_STACK)
|
||||||
|
if [ "$(myos_first_scope)" = user ]; then myos_env_print "$_v" User
|
||||||
|
else myos_env_print "$_v" ""; fi ;;
|
||||||
|
*) myos_env_print "$_v" "$(myos_var "$_v")" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -13,13 +13,25 @@ MYOS_E_USAGE=2 # bad invocation
|
|||||||
MYOS_E_NOSTACK=3 # stack not found
|
MYOS_E_NOSTACK=3 # stack not found
|
||||||
MYOS_E_NOREQ=4 # missing requirement
|
MYOS_E_NOREQ=4 # missing requirement
|
||||||
|
|
||||||
|
# myos_colors decide whether to emit colour.
|
||||||
|
# MYOS_COLOR=always|never|auto (default auto: only when stdout is a terminal).
|
||||||
|
# The make engine always emitted the escape codes, even into a pipe.
|
||||||
myos_colors() {
|
myos_colors() {
|
||||||
if [ -t 2 ] && [ "${TERM:-dumb}" != dumb ] && [ -z "${NO_COLOR:-}" ]; then
|
_want=${MYOS_COLOR:-auto}
|
||||||
|
[ -n "${NO_COLOR:-}" ] && _want=never
|
||||||
|
case $_want in
|
||||||
|
never) _want=no ;;
|
||||||
|
always) _want=yes ;;
|
||||||
|
*) if [ -t 1 ] && [ "${TERM:-dumb}" != dumb ]; then _want=yes; else _want=no; fi ;;
|
||||||
|
esac
|
||||||
|
if [ "$_want" = yes ]; then
|
||||||
MYOS_C_ERROR=$(printf '\033[31m'); MYOS_C_WARN=$(printf '\033[01;33m')
|
MYOS_C_ERROR=$(printf '\033[31m'); MYOS_C_WARN=$(printf '\033[01;33m')
|
||||||
MYOS_C_INFO=$(printf '\033[33m'); MYOS_C_DEBUG=$(printf '\033[01;34m')
|
MYOS_C_INFO=$(printf '\033[33m'); MYOS_C_DEBUG=$(printf '\033[01;34m')
|
||||||
MYOS_C_VALUE=$(printf '\033[36m'); MYOS_C_RESET=$(printf '\033[0m')
|
MYOS_C_VALUE=$(printf '\033[36m'); MYOS_C_RESET=$(printf '\033[0m')
|
||||||
|
MYOS_C_HIGHLIGHT=$(printf '\033[32m')
|
||||||
else
|
else
|
||||||
MYOS_C_ERROR=; MYOS_C_WARN=; MYOS_C_INFO=; MYOS_C_DEBUG=; MYOS_C_VALUE=; MYOS_C_RESET=
|
MYOS_C_ERROR=; MYOS_C_WARN=; MYOS_C_INFO=; MYOS_C_DEBUG=; MYOS_C_VALUE=; MYOS_C_RESET=
|
||||||
|
MYOS_C_HIGHLIGHT=
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#shellcheck shell=sh
|
||||||
|
# hooks: the per-stack settings that used to live in a .mk file.
|
||||||
|
#
|
||||||
|
# A stack may ship, next to its compose files:
|
||||||
|
# <name>.env dotenv, for plain values
|
||||||
|
# <name>.env.<env> the same, for one environment
|
||||||
|
# <name>.sh shell, for values that have to be computed (fabio tags, JWTs)
|
||||||
|
# <name>.mk the legacy make snippet, still read for its groups
|
||||||
|
#
|
||||||
|
# A .sh hook runs with the myos helpers available (myos_tagprefix, myos_uri,
|
||||||
|
# myos_var) and sets variables directly. This is what lets a stack of the
|
||||||
|
# catalogue work on a machine that has no make.
|
||||||
|
|
||||||
|
# myos_stack_hooks DIR NAME load the hooks of one stack, most specific last
|
||||||
|
myos_stack_hooks() {
|
||||||
|
_hdir=$1; _hname=$2
|
||||||
|
for _h in "$_hdir/$_hname.env" "$_hdir/$_hname.env.$ENV"; do
|
||||||
|
[ -f "$_h" ] && myos_dotenv_load "$_h"
|
||||||
|
done
|
||||||
|
for _h in "$_hdir/$_hname.sh" "$_hdir/$_hname.$ENV.sh"; do
|
||||||
|
if [ -f "$_h" ]; then
|
||||||
|
myos_debug "hook $_h"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$_h"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -79,3 +79,44 @@ myos_service_name() { printf '%s' "$1" | tr '_' '-'; }
|
|||||||
myos_network_default() { printf '_%s' "$1"; }
|
myos_network_default() { printf '_%s' "$1"; }
|
||||||
myos_network_private() { printf '%s' "${DOCKER_NETWORK_PRIVATE:-${1}-${2}}"; }
|
myos_network_private() { printf '%s' "${DOCKER_NETWORK_PRIVATE:-${1}-${2}}"; }
|
||||||
myos_network_public() { printf '%s' "${DOCKER_NETWORK_PUBLIC:-${1}}"; }
|
myos_network_public() { printf '%s' "${DOCKER_NETWORK_PUBLIC:-${1}}"; }
|
||||||
|
|
||||||
|
# myos_app_domain SCOPE USER DOMAIN
|
||||||
|
# The domain a stack is served on. A host stack is never prefixed by the user:
|
||||||
|
# it belongs to the machine.
|
||||||
|
myos_app_domain() {
|
||||||
|
_scope=$1; _u=$2; _dom=$3
|
||||||
|
if [ "$_scope" != host ] && [ "${APP_HOST_MULTI_USER:-false}" = true ]; then
|
||||||
|
printf '%s.%s' "$_u" "$_dom"
|
||||||
|
else
|
||||||
|
printf '%s' "$_dom"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# myos_app_host SCOPE USER ENV APP DOMAIN HOSTNAME
|
||||||
|
# A host stack is served on <hostname>.<domain>; anything else is prefixed by
|
||||||
|
# the environment unless the environment is the main one.
|
||||||
|
myos_app_host() {
|
||||||
|
_scope=$1; _u=$2; _env=$3; _app=$4; _dom=$5; _host=$6
|
||||||
|
_multi_env=${APP_HOST_MULTI_ENV:-}
|
||||||
|
if [ -z "$_multi_env" ]; then
|
||||||
|
case $_env in local|master|main) _multi_env=false ;; *) _multi_env=true ;; esac
|
||||||
|
fi
|
||||||
|
_prefix=
|
||||||
|
if [ "$_scope" = host ]; then _prefix="$_host."
|
||||||
|
elif [ "$_multi_env" = true ]; then _prefix="$_env."
|
||||||
|
fi
|
||||||
|
_name=
|
||||||
|
[ "${APP_HOST_MULTI_APP:-false}" = true ] && _name="$(myos_name "$_app")."
|
||||||
|
_out="$_prefix$_name$(myos_app_domain "$_scope" "$_u" "$_dom")"
|
||||||
|
# a host stack behind the load balancer also answers on the bare domain
|
||||||
|
[ "$_scope" = host ] && [ -n "${HOST_LB:-}" ] && _out="$_out $_dom"
|
||||||
|
printf '%s' "$_out"
|
||||||
|
}
|
||||||
|
|
||||||
|
# myos_app_uri HOST [PATH] the base uri the fabio tags are built on;
|
||||||
|
# it always ends with a slash
|
||||||
|
myos_app_uri() {
|
||||||
|
_out=
|
||||||
|
for _h in $1; do _out="${_out:+$_out }$_h/${2:-}"; done
|
||||||
|
printf '%s' "$_out"
|
||||||
|
}
|
||||||
|
|||||||
+14
@@ -71,3 +71,17 @@ myos_tagprefix() {
|
|||||||
[ -n "$_uris" ] || _uris=$(myos_uri "$_stack" "$_port")
|
[ -n "$_uris" ] || _uris=$(myos_uri "$_stack" "$_port")
|
||||||
myos_urlprefix "$_path" "$_opts" "$_uris"
|
myos_urlprefix "$_path" "$_opts" "$_uris"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# myos_servicenvs STACK GROUP KEY
|
||||||
|
# Collect <STACK>_SERVICE_<env>_<KEY> for every env listed in
|
||||||
|
# <STACK>_SERVICE_<GROUP>_ENVS. Used to build a list of listeners out of one
|
||||||
|
# variable per protocol.
|
||||||
|
myos_servicenvs() {
|
||||||
|
_s=$(myos_upper "$1"); _g=$(myos_upper "$2"); _k=$(myos_upper "$3")
|
||||||
|
_out=
|
||||||
|
for _e in $(myos_var "${_s}_SERVICE_${_g}_ENVS"); do
|
||||||
|
_v=$(myos_var "${_s}_SERVICE_$(myos_upper "$_e")_${_k}")
|
||||||
|
[ -n "$_v" ] && _out="${_out:+$_out }$_v"
|
||||||
|
done
|
||||||
|
printf '%s' "$_out"
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
stack/<name>/<name>.yml the services
|
stack/<name>/<name>.yml the services
|
||||||
stack/<name>/<name>.local.yml what only makes sense on a workstation (published ports…)
|
stack/<name>/<name>.local.yml what only makes sense on a workstation (published ports…)
|
||||||
stack/<name>/<name>.labels.yml the registrator labels, so routing stays optional
|
stack/<name>/<name>.labels.yml the registrator labels, so routing stays optional
|
||||||
|
stack/<name>/<name>.env plain settings: versions, defaults
|
||||||
|
stack/<name>/<name>.sh computed settings (fabio tags), no make needed
|
||||||
stack/<name>/.env.dist the variables it expects, with defaults
|
stack/<name>/.env.dist the variables it expects, with defaults
|
||||||
stack/<name>/README.md what it is and what it needs
|
stack/<name>/README.md what it is and what it needs
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -99,6 +99,33 @@ parsed, never sourced, so a value may contain a `#` or a `$(...)` without
|
|||||||
breaking anything or being executed. The make engine included `.env` as a
|
breaking anything or being executed. The make engine included `.env` as a
|
||||||
makefile, where both broke.
|
makefile, where both broke.
|
||||||
|
|
||||||
|
## Per-stack settings
|
||||||
|
|
||||||
|
A stack keeps its own settings next to its compose files:
|
||||||
|
|
||||||
|
| file | for |
|
||||||
|
|---|---|
|
||||||
|
| `<name>.env` | plain values: versions, defaults |
|
||||||
|
| `<name>.env.<env>` | the same, for one environment |
|
||||||
|
| `<name>.sh` | values that have to be computed |
|
||||||
|
| `<name>.mk` | the legacy make snippet; still read for its groups |
|
||||||
|
|
||||||
|
A `.sh` hook is sourced with the myos helpers available, and sets variables
|
||||||
|
directly. This is what lets a stack work on a machine that has no make:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# stack/host/fabio.sh
|
||||||
|
HOST_FABIO_VERSION=${HOST_FABIO_VERSION:-1.6.3}
|
||||||
|
HOST_FABIO_SERVICE_9998_NAME=${HOST_FABIO_SERVICE_9998_NAME:-fabio}
|
||||||
|
HOST_FABIO_SERVICE_9998_AUTH=${HOST_FABIO_SERVICE_9998_AUTH:-default}
|
||||||
|
HOST_FABIO_SERVICE_9998_TAGS=${HOST_FABIO_SERVICE_9998_TAGS:-$(myos_tagprefix HOST_FABIO 9998)}
|
||||||
|
```
|
||||||
|
|
||||||
|
Always write `${VAR:-default}` so the environment and the `.env` still win.
|
||||||
|
Helpers available in a hook: `myos_tagprefix`, `myos_urlprefix`, `myos_uri`,
|
||||||
|
`myos_url`, `myos_envprefix`, `myos_servicenvs`, `myos_var`, `myos_lower`,
|
||||||
|
`myos_upper`.
|
||||||
|
|
||||||
## Groups
|
## Groups
|
||||||
|
|
||||||
A group is a lowercase name whose value lists stacks. It can live in a `.env`,
|
A group is a lowercase name whose value lists stacks. It can live in a `.env`,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Options:
|
|||||||
-e ENV environment (default: local, or ENV from the config)
|
-e ENV environment (default: local, or ENV from the config)
|
||||||
-H HOSTS run on remote hosts instead (comma separated, or "all")
|
-H HOSTS run on remote hosts instead (comma separated, or "all")
|
||||||
-n, --dry-run print the commands instead of running them
|
-n, --dry-run print the commands instead of running them
|
||||||
|
--color WHEN always, never or auto (default: colour when on a terminal)
|
||||||
-v, --verbose show what myos does
|
-v, --verbose show what myos does
|
||||||
-d, --debug show every command
|
-d, --debug show every command
|
||||||
-h, --help this help
|
-h, --help this help
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#shellcheck shell=sh
|
||||||
|
# Helper: hooks set variables in the caller's scope, which a subshell loses.
|
||||||
|
[ -n "${4:-}" ] && eval "$3=\$4"
|
||||||
|
myos_stack_hooks "$1" "$2"
|
||||||
|
eval "printf '%s\n' \"\${$3:-}\""
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#shellcheck shell=sh
|
||||||
|
Include lib/str.sh
|
||||||
|
Include lib/core.sh
|
||||||
|
Include lib/tags.sh
|
||||||
|
Include lib/naming.sh
|
||||||
|
Include lib/config.sh
|
||||||
|
Include lib/hooks.sh
|
||||||
|
|
||||||
|
# A stack ships its computed settings as a shell hook, so the catalogue works
|
||||||
|
# on a machine without make. These check the hook and the uri it builds on.
|
||||||
|
Describe 'lib/hooks.sh'
|
||||||
|
setup() {
|
||||||
|
MYOS_TMP=$(mktemp -d "${TMPDIR:-/tmp}/myos-hook.XXXXXX")
|
||||||
|
MYOS_TMP=$(cd "$MYOS_TMP" && pwd -P)
|
||||||
|
ENV=local; DOMAIN=example.org; USER=tester; HOSTNAME=testhost
|
||||||
|
APP_HOST=demo.example.org; APP_URI=demo.example.org/
|
||||||
|
}
|
||||||
|
cleanup() { rm -rf "$MYOS_TMP"; }
|
||||||
|
BeforeEach setup
|
||||||
|
AfterEach cleanup
|
||||||
|
|
||||||
|
It 'loads plain values from a .env hook'
|
||||||
|
printf 'DEMO_VERSION=1.2.3\n' > "$MYOS_TMP/demo.env"
|
||||||
|
When run source spec/unit/hooks_helper.sh "$MYOS_TMP" demo DEMO_VERSION
|
||||||
|
The output should equal "1.2.3"
|
||||||
|
End
|
||||||
|
|
||||||
|
It 'loads a computed value from a .sh hook'
|
||||||
|
printf 'DEMO_TAGS=$(myos_tagprefix demo 8000)\n' > "$MYOS_TMP/demo.sh"
|
||||||
|
When run source spec/unit/hooks_helper.sh "$MYOS_TMP" demo DEMO_TAGS
|
||||||
|
The output should equal "urlprefix-demo.demo.example.org/*"
|
||||||
|
End
|
||||||
|
|
||||||
|
It 'lets a hook name the service it publishes'
|
||||||
|
printf 'DEMO_SERVICE_8000_NAME=www\nDEMO_TAGS=$(myos_tagprefix demo 8000)\n' > "$MYOS_TMP/demo.sh"
|
||||||
|
When run source spec/unit/hooks_helper.sh "$MYOS_TMP" demo DEMO_TAGS
|
||||||
|
The output should equal "urlprefix-www.demo.example.org/*"
|
||||||
|
End
|
||||||
|
|
||||||
|
It 'lets the environment win over a hook default'
|
||||||
|
printf 'DEMO_VERSION=${DEMO_VERSION:-1.2.3}\n' > "$MYOS_TMP/demo.sh"
|
||||||
|
When run source spec/unit/hooks_helper.sh "$MYOS_TMP" demo DEMO_VERSION 9.9.9
|
||||||
|
The output should equal "9.9.9"
|
||||||
|
End
|
||||||
|
|
||||||
|
It 'is quiet when a stack has no hook'
|
||||||
|
When call myos_stack_hooks "$MYOS_TMP" nothing
|
||||||
|
The status should be success
|
||||||
|
The output should equal ""
|
||||||
|
End
|
||||||
|
End
|
||||||
|
|
||||||
|
Describe 'lib/naming.sh service uris'
|
||||||
|
BeforeEach 'ENV=local; unset APP_HOST_MULTI_APP APP_HOST_MULTI_USER APP_HOST_MULTI_ENV HOST_LB 2>/dev/null || true'
|
||||||
|
|
||||||
|
It 'serves a host stack on <hostname>.<domain>'
|
||||||
|
When call myos_app_host host aya local fabio example.org sonic
|
||||||
|
The output should equal "sonic.example.org"
|
||||||
|
End
|
||||||
|
It 'adds the bare domain when the host is the load balancer'
|
||||||
|
HOST_LB=true
|
||||||
|
When call myos_app_host host aya local fabio example.org sonic
|
||||||
|
The output should equal "sonic.example.org example.org"
|
||||||
|
End
|
||||||
|
It 'does not prefix a main environment'
|
||||||
|
When call myos_app_host app aya master duniter example.org sonic
|
||||||
|
The output should equal "example.org"
|
||||||
|
End
|
||||||
|
It 'prefixes any other environment'
|
||||||
|
When call myos_app_host app aya staging duniter example.org sonic
|
||||||
|
The output should equal "staging.example.org"
|
||||||
|
End
|
||||||
|
It 'prefixes the user when asked to'
|
||||||
|
APP_HOST_MULTI_USER=true
|
||||||
|
When call myos_app_host app aya master duniter example.org sonic
|
||||||
|
The output should equal "aya.example.org"
|
||||||
|
End
|
||||||
|
It 'builds a uri that ends with a slash'
|
||||||
|
When call myos_app_uri "a.example.org b.example.org"
|
||||||
|
The output should equal "a.example.org/ b.example.org/"
|
||||||
|
End
|
||||||
|
End
|
||||||
Reference in New Issue
Block a user