port the make list functions and JWT, and expose the framework variables to hooks
lib/str.sh gains firstword, lastword, or, patsubst, filter, filter_out, addprefix, addsuffix and jwt: what the catalogue .mk files are written in. The make JWT macro split on the commas of its payload; this one does not. lib/context.sh registers COMPOSE_PROJECT_NAME, APP, DOCKER_NETWORK_* and the rest as lazy defaults, so a converted .mk keeps reading them as it did. Hooks also load a directory-level _stack.env and _stack.sh, for the .mk files of the catalogue that hold settings for several stacks at once.
This commit is contained in:
@@ -19,7 +19,7 @@ done
|
||||
MYOS_ROOT=$(cd "$(dirname "$_self")/.." && pwd -P)
|
||||
export MYOS_ROOT
|
||||
|
||||
for _m in core str var tags naming stack config compose hooks; do
|
||||
for _m in core str var tags naming stack config compose hooks context; do
|
||||
# shellcheck source=/dev/null
|
||||
. "$MYOS_ROOT/lib/$_m.sh"
|
||||
done
|
||||
@@ -225,6 +225,8 @@ fi
|
||||
# shellcheck disable=SC2086 # a list of references
|
||||
MYOS_STACKS=$(myos_group_expand $MYOS_REFS)
|
||||
|
||||
myos_context_defaults
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -46,35 +46,3 @@ myos_cmd_env() {
|
||||
done
|
||||
}
|
||||
|
||||
# myos_all_compose_files every compose file of every requested stack, in order
|
||||
myos_all_compose_files() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
myos_stack_compose_files "$_ref" 2>/dev/null
|
||||
done
|
||||
myos_framework_compose_files
|
||||
return 0
|
||||
}
|
||||
|
||||
# myos_first_app / myos_first_scope / myos_first_project
|
||||
# describe the first requested stack, which is what the introspection commands
|
||||
# report when several stacks are asked for at once.
|
||||
myos_first_app() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
case $_ref in
|
||||
.|./*|/*|../*) basename "$(myos_stack_resolve "$_ref" 2>/dev/null)" ;;
|
||||
*) myos_stack_name "$_ref" ;;
|
||||
esac
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
myos_first_scope() {
|
||||
for _ref in $MYOS_STACKS; do myos_scope "$_ref"; return 0; done
|
||||
}
|
||||
|
||||
myos_first_project() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
myos_project_name "$(myos_scope "$_ref")" "$USER" "$ENV" "$(myos_first_app)"
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#shellcheck shell=sh
|
||||
# shellcheck disable=SC3028 # HOSTNAME is a myos variable, set by bin/myos
|
||||
# context: what the requested stacks resolve to, and the framework variables a
|
||||
# stack hook may read.
|
||||
#
|
||||
# A hook is written the way a .mk was: it may mention COMPOSE_PROJECT_NAME,
|
||||
# USER or DOMAIN and expect the framework value. Those are registered as lazy
|
||||
# defaults, so each is computed when read and an explicit value still wins.
|
||||
|
||||
# myos_all_compose_files every compose file of every requested stack, in order
|
||||
myos_all_compose_files() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
myos_stack_compose_files "$_ref" 2>/dev/null
|
||||
done
|
||||
myos_framework_compose_files
|
||||
return 0
|
||||
}
|
||||
|
||||
# myos_first_app / myos_first_scope / myos_first_project
|
||||
# describe the first requested stack, which is what the introspection commands
|
||||
# report when several stacks are asked for at once.
|
||||
myos_first_app() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
case $_ref in
|
||||
.|./*|/*|../*) basename "$(myos_stack_resolve "$_ref" 2>/dev/null)" ;;
|
||||
*) myos_stack_name "$_ref" ;;
|
||||
esac
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
myos_first_scope() {
|
||||
for _ref in $MYOS_STACKS; do myos_scope "$_ref"; return 0; done
|
||||
}
|
||||
|
||||
myos_first_project() {
|
||||
for _ref in $MYOS_STACKS; do
|
||||
myos_project_name "$(myos_scope "$_ref")" "$USER" "$ENV" "$(myos_first_app)"
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
# myos_context_defaults register the framework variables as lazy defaults
|
||||
# shellcheck disable=SC2329 # these are reached through myos_var
|
||||
myos_context_defaults() {
|
||||
myos_default_APP() { myos_first_app; }
|
||||
myos_default_APP_NAME() { myos_name "$(myos_first_app)"; }
|
||||
myos_default_SCOPE() { myos_first_scope; }
|
||||
myos_default_COMPOSE_PROJECT_NAME() { myos_first_project; }
|
||||
myos_default_COMPOSE_SERVICE_NAME() { myos_service_name "$(myos_first_project)"; }
|
||||
myos_default_DOCKER_REPOSITORY() { printf '%s' "$(myos_first_project)" | tr '_-' '//'; }
|
||||
myos_default_DOCKER_NETWORK_DEFAULT() { myos_network_default "$(myos_first_project)"; }
|
||||
myos_default_DOCKER_NETWORK_PRIVATE() { myos_network_private "$USER" "$ENV"; }
|
||||
myos_default_DOCKER_NETWORK_PUBLIC() { myos_network_public "${HOSTNAME:-}"; }
|
||||
myos_default_DOCKER_NETWORK() { myos_network_private "$USER" "$ENV"; }
|
||||
myos_default_DOCKER_IMAGE_TAG() { printf 'latest'; }
|
||||
myos_default_GIT_USER() { printf '%s' "$USER"; }
|
||||
myos_default_HOST_COMPOSE_PROJECT_NAME() { printf '%s' "${HOSTNAME:-}"; }
|
||||
myos_default_HOST_DOCKER_VOLUME() { printf '%s' "${HOSTNAME:-}"; }
|
||||
myos_default_HOST_DOCKER_REPOSITORY() { printf '%s' "${HOSTNAME:-}" | tr '_-' '//'; }
|
||||
myos_default_USER_COMPOSE_PROJECT_NAME() { myos_resu "${MAIL:-}" | tr '.' '-'; }
|
||||
myos_default_RESU() { myos_resu "${MAIL:-}"; }
|
||||
}
|
||||
+4
-2
@@ -2,6 +2,8 @@
|
||||
# hooks: the per-stack settings that used to live in a .mk file.
|
||||
#
|
||||
# A stack may ship, next to its compose files:
|
||||
# _stack.env settings shared by every stack of the directory
|
||||
# _stack.sh the same, computed
|
||||
# <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)
|
||||
@@ -14,10 +16,10 @@
|
||||
# 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
|
||||
for _h in "$_hdir/_stack.env" "$_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
|
||||
for _h in "$_hdir/_stack.sh" "$_hdir/$_hname.sh" "$_hdir/$_hname.$ENV.sh"; do
|
||||
if [ -f "$_h" ]; then
|
||||
myos_debug "hook $_h"
|
||||
# shellcheck source=/dev/null
|
||||
|
||||
+74
@@ -31,3 +31,77 @@ myos_verlt() {
|
||||
[ "$1" = "$2" ] && return 1
|
||||
myos_verle "$1" "$2"
|
||||
}
|
||||
|
||||
# The make list functions the catalogue uses, on space separated words.
|
||||
|
||||
# myos_firstword LIST / myos_lastword LIST
|
||||
myos_firstword() { for _w in $1; do printf '%s' "$_w"; return 0; done; }
|
||||
myos_lastword() { _l=; for _w in $1; do _l=$_w; done; printf '%s' "$_l"; }
|
||||
|
||||
# myos_or A B... the first argument that is not empty
|
||||
myos_or() { for _a in "$@"; do [ -n "$_a" ] && { printf '%s' "$_a"; return 0; }; done; }
|
||||
|
||||
# myos_patsubst PATTERN REPLACEMENT LIST
|
||||
# The pattern holds one %, standing for any text; the replacement puts it back.
|
||||
myos_patsubst() {
|
||||
_pre=${1%%%*}; _suf=${1#*%}
|
||||
_rpre=${2%%%*}; _rsuf=${2#*%}
|
||||
_out=
|
||||
for _w in $3; do
|
||||
case $_w in
|
||||
"$_pre"*"$_suf")
|
||||
_stem=${_w#"$_pre"}; _stem=${_stem%"$_suf"}
|
||||
_out="${_out:+$_out }$_rpre$_stem$_rsuf" ;;
|
||||
*) _out="${_out:+$_out }$_w" ;;
|
||||
esac
|
||||
done
|
||||
printf '%s' "$_out"
|
||||
}
|
||||
|
||||
# myos_filter PATTERNS LIST / myos_filter_out PATTERNS LIST
|
||||
# A make pattern uses % where a shell pattern uses *.
|
||||
myos_filter() {
|
||||
_pats=$(printf '%s' "$1" | tr '%' '*')
|
||||
_out=
|
||||
for _w in $2; do
|
||||
for _p in $_pats; do
|
||||
# shellcheck disable=SC2254 # the pattern is meant to glob
|
||||
case $_w in $_p) _out="${_out:+$_out }$_w"; break ;; esac
|
||||
done
|
||||
done
|
||||
printf '%s' "$_out"
|
||||
}
|
||||
myos_filter_out() {
|
||||
_pats=$(printf '%s' "$1" | tr '%' '*')
|
||||
_out=
|
||||
for _w in $2; do
|
||||
_keep=yes
|
||||
for _p in $_pats; do
|
||||
# shellcheck disable=SC2254 # the pattern is meant to glob
|
||||
case $_w in $_p) _keep=no; break ;; esac
|
||||
done
|
||||
[ "$_keep" = yes ] && _out="${_out:+$_out }$_w"
|
||||
done
|
||||
printf '%s' "$_out"
|
||||
}
|
||||
|
||||
# myos_addprefix PREFIX LIST / myos_addsuffix SUFFIX LIST
|
||||
myos_addprefix() { _out=; for _w in $2; do _out="${_out:+$_out }$1$_w"; done; printf '%s' "$_out"; }
|
||||
myos_addsuffix() { _out=; for _w in $2; do _out="${_out:+$_out }$_w$1"; done; printf '%s' "$_out"; }
|
||||
|
||||
# myos_b64url read stdin, write url-safe base64 without padding
|
||||
myos_b64url() { openssl enc -A -base64 | tr '+/' '-_' | tr -d '='; }
|
||||
|
||||
# myos_jwt HEADER PAYLOAD SECRET a HS256 JSON web token
|
||||
# Ported from the JWT macro of make/def.mk, which supabase uses to derive its
|
||||
# anon and service keys from one secret. The make macro split on the commas of
|
||||
# the payload; this one does not.
|
||||
myos_jwt() {
|
||||
_h=${1:-'{"alg":"HS256","typ":"JWT"}'}
|
||||
_p=$2
|
||||
_s=$3
|
||||
_hb=$(printf '%s' "$_h" | myos_b64url)
|
||||
_pb=$(printf '%s' "$_p" | myos_b64url)
|
||||
_sig=$(printf '%s' "$_hb.$_pb" | openssl dgst -sha256 -binary -hmac "$_s" | myos_b64url)
|
||||
printf '%s.%s.%s' "$_hb" "$_pb" "$_sig"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
make -o docker-stack-build MAKE_OLDFILE=docker-stack-build ENV=local DOCKER_COMPOSE=docker --log-level=error compose docker-compose-build STACK=myos APP_NAME=myos
|
||||
docker --log-level=error compose --ansi=auto -f @MYOS@/share/compose/networks.yml -p tester-myos-local build --build-arg APP=myos --build-arg BRANCH=lightning --build-arg COMPOSE_VERSION=2.24.4 --build-arg DOCKER_MACHINE=x86_64 --build-arg DOCKER_REPOSITORY=tester/myos/local --build-arg DOCKER_SYSTEM=Linux --build-arg GIT_AUTHOR_EMAIL=tester@example.test --build-arg GIT_AUTHOR_NAME=tester --build-arg SSH_REMOTE_HOSTS=github.com gitlab.com --build-arg USER=tester --build-arg VERSION=legacy-1.0-beta-17-g55fae62 --build-arg SSH_AUTHORIZED_KEYS=https://github.com/tester.keys --build-arg SSH_BASTION_USERNAME=tester --build-arg SSH_PORT=22 --build-arg SSH_PUBLIC_HOSTS= github.com gitlab.com --build-arg SSH_USER=tester --build-arg GID=20 --build-arg UID=502
|
||||
docker --log-level=error compose --ansi=auto -f @MYOS@/share/compose/networks.yml -p tester-myos-local build --build-arg APP=myos --build-arg BRANCH=@BRANCH@ --build-arg COMPOSE_VERSION=2.24.4 --build-arg DOCKER_MACHINE=x86_64 --build-arg DOCKER_REPOSITORY=tester/myos/local --build-arg DOCKER_SYSTEM=Linux --build-arg GIT_AUTHOR_EMAIL=tester@example.test --build-arg GIT_AUTHOR_NAME=tester --build-arg SSH_REMOTE_HOSTS=github.com gitlab.com --build-arg USER=tester --build-arg VERSION=@VERSION@ --build-arg SSH_AUTHORIZED_KEYS=https://github.com/tester.keys --build-arg SSH_BASTION_USERNAME=tester --build-arg SSH_PORT=22 --build-arg SSH_PUBLIC_HOSTS= github.com gitlab.com --build-arg SSH_USER=tester --build-arg GID=20 --build-arg UID=502
|
||||
make -o docker-stack-up MAKE_OLDFILE=docker-stack-up ENV=local DOCKER_COMPOSE=docker --log-level=error compose docker-compose-up STACK=myos APP_NAME=myos
|
||||
sh -c docker network create tester-local >/dev/null 2>&1
|
||||
sh -c docker network create testhost >/dev/null 2>&1
|
||||
|
||||
@@ -39,6 +39,8 @@ myos_normalize() {
|
||||
-e 's/^APPS .*/APPS @APPS@/' \
|
||||
-e 's/^BRANCH .*/BRANCH @BRANCH@/' \
|
||||
-e 's/^VERSION .*/VERSION @VERSION@/' \
|
||||
-e 's/--build-arg VERSION=[^ ]*/--build-arg VERSION=@VERSION@/' \
|
||||
-e 's/--build-arg BRANCH=[^ ]*/--build-arg BRANCH=@BRANCH@/' \
|
||||
-e 's/[[:space:]][[:space:]]*/ /g' \
|
||||
-e 's/[[:space:]]*$//'
|
||||
}
|
||||
|
||||
@@ -68,3 +68,55 @@ Describe 'lib/str.sh'
|
||||
End
|
||||
End
|
||||
End
|
||||
|
||||
# The make list functions the catalogue relies on, ported so that a stack hook
|
||||
# reads like the .mk it replaces.
|
||||
Describe 'lib/str.sh list functions'
|
||||
It 'takes the first and last word'
|
||||
When call myos_firstword "a b c"
|
||||
The output should equal "a"
|
||||
End
|
||||
It 'takes the last word'
|
||||
When call myos_lastword "a b c"
|
||||
The output should equal "c"
|
||||
End
|
||||
It 'returns the first argument that is not empty'
|
||||
When call myos_or "" "" "third"
|
||||
The output should equal "third"
|
||||
End
|
||||
Describe 'myos_patsubst'
|
||||
Parameters
|
||||
"%" "pre%" "a b" "prea preb"
|
||||
"%.yml" "%.yaml" "a.yml b.md" "a.yaml b.md"
|
||||
"%" "%/" "x y" "x/ y/"
|
||||
End
|
||||
It "substitutes $1 -> $2 in $3"
|
||||
When call myos_patsubst "$1" "$2" "$3"
|
||||
The output should equal "$4"
|
||||
End
|
||||
End
|
||||
It 'filters a list'
|
||||
When call myos_filter "a%" "abc bcd axe"
|
||||
The output should equal "abc axe"
|
||||
End
|
||||
It 'filters a list out'
|
||||
When call myos_filter_out "local master main" "staging"
|
||||
The output should equal "staging"
|
||||
End
|
||||
It 'drops the words it is told to'
|
||||
When call myos_filter_out "local master main" "master"
|
||||
The output should equal ""
|
||||
End
|
||||
It 'adds a prefix and a suffix'
|
||||
When call myos_addprefix "x." "a b"
|
||||
The output should equal "x.a x.b"
|
||||
End
|
||||
End
|
||||
|
||||
Describe 'lib/str.sh myos_jwt'
|
||||
It 'signs a token whose payload decodes back'
|
||||
When call myos_jwt "" '{"role":"anon"}' secret
|
||||
The output should start with "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
|
||||
The output should include "eyJyb2xlIjoiYW5vbiJ9"
|
||||
End
|
||||
End
|
||||
|
||||
Reference in New Issue
Block a user