Compare commits

..
5 Commits
5 changed files with 120 additions and 15 deletions
+17 -3
View File
@@ -2,7 +2,7 @@
# file rc.sh: Call user defined functions # file rc.sh: Call user defined functions
## author: Yann "aya" Autissier ## author: Yann "aya" Autissier
## license: GPL ## license: GPL
## version: 20220630 ## version: 20260730
case $- in case $- in
# if this is an interactive shell # if this is an interactive shell
@@ -20,7 +20,14 @@ case $- in
func_name="${func_name#?}" func_name="${func_name#?}"
done done
# call user function with args passed from the content of the file # call user function with args passed from the content of the file
command -v "${func_name}" >/dev/null 2>&1 && "${func_name}" "${func_args}" # an empty file means no args at all, do not pass an empty string
if command -v "${func_name}" >/dev/null 2>&1; then
if [ -n "${func_args}" ]; then
"${func_name}" "${func_args}"
else
"${func_name}"
fi
fi
fi fi
done done
# load user stuff from RC_* env vars # load user stuff from RC_* env vars
@@ -36,7 +43,14 @@ case $- in
func_name="${func_name#?}" func_name="${func_name#?}"
done done
# call user function with args passed from the value of the env var # call user function with args passed from the value of the env var
command -v "${func_name}" >/dev/null 2>&1 && "${func_name}" "${func_args}" # RC_FOO=true means no args at all, do not pass an empty string
if command -v "${func_name}" >/dev/null 2>&1; then
if [ -n "${func_args:-}" ]; then
"${func_name}" "${func_args}"
else
"${func_name}"
fi
fi
done done
unset IFS unset IFS
;; ;;
+89 -7
View File
@@ -2,7 +2,7 @@
# file rc_functions.sh: Define shell functions # file rc_functions.sh: Define shell functions
## author: Yann "aya" Autissier ## author: Yann "aya" Autissier
## license: GPL ## license: GPL
## version: 20221229 ## version: 20260730
# function force: Run a command sine die # function force: Run a command sine die
force() { force() {
@@ -58,6 +58,25 @@ process_count() {
# function prompt_set: Export custom PROMPT_COMMAND # function prompt_set: Export custom PROMPT_COMMAND
prompt_set() { prompt_set() {
# zsh: PROMPT_COMMAND is bash-only, set the terminal title from a precmd hook
if [ -n "${ZSH_VERSION:-}" ]; then
_prompt_precmd() {
local __ret=$? dcs st host
case "${TERM}" in
screen*) dcs=$'\ek'; st=$'\e\\';;
*) dcs=$'\e]0;'; st=$'\a';;
esac
host="${HOSTNAME:-${HOST}}"
if [ -n "${STY}" ]; then
printf '%s%s%s' "${dcs}" "${PWD##*/}" "${st}"
else
printf '%s%s@%s:%s%s' "${dcs}" "${USER}" "${host%%.*}" "${PWD##*/}" "${st}"
fi
return ${__ret}
}
autoload -Uz add-zsh-hook 2>/dev/null && add-zsh-hook precmd _prompt_precmd
return
fi
case "${TERM}" in case "${TERM}" in
screen*) screen*)
ESCAPE_CODE_DCS="\033k" ESCAPE_CODE_DCS="\033k"
@@ -87,6 +106,58 @@ prompt_set() {
# function ps1_set: Export custom PS1 # function ps1_set: Export custom PS1
ps1_set() { ps1_set() {
# zsh: bash prompt escapes (\[ \]) and "$0" shell detection do not apply;
# rebuild PROMPT from a precmd hook using zsh's %{ %} non-printing markers
if [ -n "${ZSH_VERSION:-}" ]; then
_ps1_precmd() {
local __ret=$?
local dgray=$'%{\e[1;30m%}' red=$'%{\e[01;31m%}' green=$'%{\e[01;32m%}' \
brown=$'%{\e[0;33m%}' yellow=$'%{\e[01;33m%}' blue=$'%{\e[01;34m%}' \
cyan=$'%{\e[0;36m%}' gray=$'%{\e[0;37m%}' reset=$'%{\e[0m%}'
local scol ucol hcol count user host wd git end sym uid h br
uid="$(id -u)"
# exit status: blue on success, yellow on 1, red otherwise
case "${__ret}" in
0) scol="${blue}";;
1) scol="${yellow}";;
*) scol="${red}";;
esac
count="${dgray}[${scol}${__ret}"
type process_count >/dev/null 2>&1 \
&& count="${count}${dgray}|${blue}$(process_count 2>/dev/null)"
type user_count >/dev/null 2>&1 \
&& count="${count}${dgray}|${blue}$(user_count 2>/dev/null)"
type load_average >/dev/null 2>&1 \
&& count="${count}${dgray}|${blue}$(load_average 2>/dev/null)"
count="${count}${dgray}]${reset}"
# user: red for root, brown otherwise
[ "${uid}" = 0 ] && ucol="${red}" || ucol="${brown}"
user="${ucol}$(id -nu):${uid}${reset}"
# hostname: red on prod, yellow if ENV set, green otherwise
h="$(hostname |sed 's/\..*//')"
case "${ENV}${h}" in
*[Pp][Rr][Oo][Dd]*|*[Pp][Rr][Dd]*) hcol="${red}";;
*) [ -n "${ENV}" ] && hcol="${yellow}" || hcol="${green}";;
esac
host="${hcol}${h}${reset}"
# workdir with ~ for $HOME
wd="${gray}$(pwd |sed 's|^'"${HOME}"'\(/.*\)*$|~\1|')${reset}"
# git branch
if type __git_ps1 >/dev/null 2>&1; then
git="$(__git_ps1 ' (%s)' 2>/dev/null)"
else
br="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
[ -n "${br}" ] && git=" (${br})"
fi
git="${cyan}${git}${reset}"
# tail: # for root, $ otherwise
[ "${uid}" = 0 ] && sym='#' || sym='$'
end="${dgray}${sym}${reset}"
PROMPT="${count}${user}${dgray}@${host}${dgray}:${wd}${git}${end} "
}
autoload -Uz add-zsh-hook 2>/dev/null && add-zsh-hook precmd _ps1_precmd
return
fi
case "$0" in case "$0" in
*sh) *sh)
COLOR_DGRAY="\[\033[1;30m\]" COLOR_DGRAY="\[\033[1;30m\]"
@@ -194,6 +265,8 @@ screen_detach() {
# function ssh_add: Load all private keys in ~/.ssh/ to ssh agent # function ssh_add: Load all private keys in ~/.ssh/ to ssh agent
ssh_add() { ssh_add() {
command -v ssh-agent >/dev/null 2>&1 && command -v ssh-add >/dev/null 2>&1 || return command -v ssh-agent >/dev/null 2>&1 && command -v ssh-add >/dev/null 2>&1 || return
# zsh: emulate bash handling of unmatched globs (scoped to this function via localoptions)
[ -n "${ZSH_VERSION:-}" ] && setopt localoptions nonomatch
SSH_AGENT_DIR="/tmp/ssh-$(id -u)" SSH_AGENT_DIR="/tmp/ssh-$(id -u)"
SSH_AGENT_SOCK="${SSH_AGENT_DIR}/agent@$(hostname |sed 's/\..*//')" SSH_AGENT_SOCK="${SSH_AGENT_DIR}/agent@$(hostname |sed 's/\..*//')"
# launch a new agent # launch a new agent
@@ -219,11 +292,15 @@ ssh_add() {
fi fi
SSH_PRIVATE_KEYS="${SSH_PRIVATE_KEYS:-} ${dir}/id_ed25519 ${dir}/id_rsa $(grep -l${GREP_RECURSIVE_FLAG:-} 'PRIVATE KEY' "${dir}/"${GREP_RECURSIVE_CHAR:-} 2>/dev/null |grep -vwE "${dir}/id_(rsa|ed25519)")" SSH_PRIVATE_KEYS="${SSH_PRIVATE_KEYS:-} ${dir}/id_ed25519 ${dir}/id_rsa $(grep -l${GREP_RECURSIVE_FLAG:-} 'PRIVATE KEY' "${dir}/"${GREP_RECURSIVE_CHAR:-} 2>/dev/null |grep -vwE "${dir}/id_(rsa|ed25519)")"
done done
# shellcheck disable=SC2086 # split on spaces/newlines via tr, portable across bash (IFS split) and zsh (no word split)
printf '%s\n' ${SSH_PRIVATE_KEYS} |while read -r file; do printf '%s' "${SSH_PRIVATE_KEYS}" |tr ' ' '\n' |while read -r file; do
[ -r "${file}" ] || continue [ -r "${file}" ] || continue
# fingerprint is empty when ssh-keygen cannot read the key (legacy PEM without .pub):
# in that case add it instead of matching the empty pattern against every agent line
fingerprint="$(ssh-keygen -lf "${file}" 2>/dev/null |awk '{print $2}')"
[ -n "${fingerprint}" ] && ssh-add -l 2>/dev/null |grep -qF "${fingerprint}" && continue
# add private key to agent # add private key to agent
ssh-add -l |grep -q "$(ssh-keygen -lf "${file}" 2>/dev/null |awk '{print $2}')" 2>/dev/null || ssh-add "${file}" ssh-add "${file}"
done done
unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_AGENT_DIR SSH_AGENT_SOCK SSH_PRIVATE_KEYS unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_AGENT_DIR SSH_AGENT_SOCK SSH_PRIVATE_KEYS
} }
@@ -231,6 +308,8 @@ ssh_add() {
# function ssh_del: removes all private keys in ~/.ssh/ from ssh agent # function ssh_del: removes all private keys in ~/.ssh/ from ssh agent
ssh_del() { ssh_del() {
command -v ssh-add >/dev/null 2>&1 || return command -v ssh-add >/dev/null 2>&1 || return
# zsh: emulate bash handling of unmatched globs (scoped to this function via localoptions)
[ -n "${ZSH_VERSION:-}" ] && setopt localoptions nonomatch
# attach to agent # attach to agent
if [ -z "${SSH_AUTH_SOCK}" ]; then if [ -z "${SSH_AUTH_SOCK}" ]; then
return return
@@ -245,11 +324,14 @@ ssh_del() {
fi fi
SSH_PRIVATE_KEYS="${SSH_PRIVATE_KEYS:-} ${dir}/id_ed25519 ${dir}/id_rsa $(grep -l${GREP_RECURSIVE_FLAG:-} 'PRIVATE KEY' "${dir}/"${GREP_RECURSIVE_CHAR:-} 2>/dev/null |grep -vwE "${dir}/id_(rsa|ed25519)")" SSH_PRIVATE_KEYS="${SSH_PRIVATE_KEYS:-} ${dir}/id_ed25519 ${dir}/id_rsa $(grep -l${GREP_RECURSIVE_FLAG:-} 'PRIVATE KEY' "${dir}/"${GREP_RECURSIVE_CHAR:-} 2>/dev/null |grep -vwE "${dir}/id_(rsa|ed25519)")"
done done
# shellcheck disable=SC2086 # split on spaces/newlines via tr, portable across bash (IFS split) and zsh (no word split)
printf '%s\n' ${SSH_PRIVATE_KEYS} |while read -r file; do printf '%s' "${SSH_PRIVATE_KEYS}" |tr ' ' '\n' |while read -r file; do
[ -r "${file}" ] || continue [ -r "${file}" ] || continue
# skip keys we cannot fingerprint, an empty pattern would match any agent line
fingerprint="$(ssh-keygen -lf "${file}" 2>/dev/null |awk '{print $2}')"
[ -n "${fingerprint}" ] || continue
# remove private key from agent # remove private key from agent
ssh-add -l |grep -q "$(ssh-keygen -lf "${file}" 2>/dev/null |awk '{print $2}')" 2>/dev/null && ssh-add -d "${file}" ssh-add -l 2>/dev/null |grep -qF "${fingerprint}" && ssh-add -d "${file}"
done done
unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_PRIVATE_KEYS unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_PRIVATE_KEYS
} }
+1 -1
View File
@@ -156,7 +156,7 @@ define docker-stack-update
$(eval stack_update := $(patsubst %.yml,%,$(notdir $(1)))) $(eval stack_update := $(patsubst %.yml,%,$(notdir $(1))))
$(eval stack_name := $(firstword $(subst :, ,$(stack_update)))) $(eval stack_name := $(firstword $(subst :, ,$(stack_update))))
$(eval stack_version := $(or $(2),$(if $(findstring :,$(stack_update)),$(lastword $(subst :, ,$(stack_update))),latest))) $(eval stack_version := $(or $(2),$(if $(findstring :,$(stack_update)),$(lastword $(subst :, ,$(stack_update))),latest)))
$(eval stack_path := $(patsubst %/,%,$(or $(3),$(foreach stack_dir,$(STACK_DIR),$(if $(findstring /,$(1)),$(if $(wildcard $(stack_dir)/$(1) $(stack_dir)/$(1).yml),$(stack_dir)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard $(stack_dir)/$(1).yml),$(dir $(1)),$(1))),$(if $(wildcard $(stack_dir)/$(stackz)/$(1) $(stack_dir)/$(stackz)/$(1).yml),$(stack_dir)/$(stackz)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard $(stack_dir)/$(stackz)/$(1).yml),$(dir $(1)),$(1))),$(dir $(1)))))),$(foreach stack_dir,$(STACK_DIR),$(firstword $(wildcard $(stack_dir)/$(stackz)/$(stack_name) $(stack_dir)/$(stackz) $(stack_dir)/$(stack_name))))))) $(eval stack_path := $(patsubst %/,%,$(or $(3),$(realpath $(foreach stack_dir,$(STACK_DIR),$(if $(findstring /,$(1)),$(if $(wildcard $(stack_dir)/$(1) $(stack_dir)/$(1).yml),$(stack_dir)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard $(stack_dir)/$(1).yml),$(dir $(1)),$(1))),$(if $(wildcard $(stack_dir)/$(stackz)/$(1) $(stack_dir)/$(stackz)/$(1).yml),$(stack_dir)/$(stackz)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard $(stack_dir)/$(stackz)/$(1).yml),$(dir $(1)),$(1))),$(dir $(1)))))),$(foreach stack_dir,$(STACK_DIR),$(firstword $(wildcard $(stack_dir)/$(stackz)/$(stack_name) $(stack_dir)/$(stackz) $(stack_dir)/$(stack_name))))))))
$(call debug,stack_path) $(call debug,stack_path)
$(call compose-file,$(stack_path),docker-compose $(stack_name),$(COMPOSE_FILE_SUFFIX) $(stack_version)) $(call compose-file,$(stack_path),docker-compose $(stack_name),$(COMPOSE_FILE_SUFFIX) $(stack_version))
$(if $(wildcard $(stack_path)/.env.dist),$(call .env,,$(stack_path)/.env.dist,$(wildcard $(CONFIG)/$(ENV)/$(APP)/.env $(stack_path)/.env.$(ENV) .env))) $(if $(wildcard $(stack_path)/.env.dist),$(call .env,,$(stack_path)/.env.dist,$(wildcard $(CONFIG)/$(ENV)/$(APP)/.env $(stack_path)/.env.$(ENV) .env)))
+4
View File
@@ -14,6 +14,10 @@ DOCKER_NETWORK ?= $(if $(USER_STACK),$(USER),$(DOCKER_NETWORK_P
DOCKER_NETWORK_DEFAULT ?= _$(COMPOSE_PROJECT_NAME) DOCKER_NETWORK_DEFAULT ?= _$(COMPOSE_PROJECT_NAME)
DOCKER_NETWORK_PRIVATE ?= $(USER)-$(ENV) DOCKER_NETWORK_PRIVATE ?= $(USER)-$(ENV)
DOCKER_NETWORK_PUBLIC ?= $(HOSTNAME) DOCKER_NETWORK_PUBLIC ?= $(HOSTNAME)
# The myos networks.yml is appended to COMPOSE_FILE after the per-stack
# env-vars scan, so these infra vars must always reach compose or the
# external network names fall back to their placeholders.
ENV_VARS += DOCKER_NETWORK_DEFAULT DOCKER_NETWORK_PRIVATE DOCKER_NETWORK_PUBLIC
# DOCKER_RUN: if empty, run system command, else run it in a docker # DOCKER_RUN: if empty, run system command, else run it in a docker
DOCKER_RUN ?= $(if $(filter-out false False FALSE,$(DOCKER)),$(DOCKER)) DOCKER_RUN ?= $(if $(filter-out false False FALSE,$(DOCKER)),$(DOCKER))
DOCKER_RUN_ENTRYPOINT ?= $(patsubst %,--entrypoint=%,$(DOCKER_ENTRYPOINT)) DOCKER_RUN_ENTRYPOINT ?= $(patsubst %,--entrypoint=%,$(DOCKER_ENTRYPOINT))
+9 -4
View File
@@ -5,11 +5,16 @@ set -eu
# define MYOS path # define MYOS path
MYOS="$(dirname "$(readlink "$0" || echo "$0")")" MYOS="$(dirname "$(readlink "$0" || echo "$0")")"
# load system config # load system config: /etc/conf.d/myos (openrc convention) first, then the
[ -r /etc/default/myos ] && . /etc/default/myos # debian-style /etc/default/myos as fallback
MYOS_CONF=/etc/conf.d/myos
[ -r "$MYOS_CONF" ] || MYOS_CONF=/etc/default/myos
[ -r "$MYOS_CONF" ] && . "$MYOS_CONF"
# check myos requirements # check myos requirements
[ -f "${MYOS:-.}/Makefile" ] || { echo 'Unable to find myos Makefile' >&2; exit 1; } [ -f "${MYOS:-.}/Makefile" ] || { echo 'Unable to find myos Makefile' >&2; exit 1; }
# call myos Makefile # call myos Makefile: a WORKDIR from the config or environment wins over PWD,
IFS=$'\n'; exec env $(cat /etc/default/myos 2>/dev/null) MYOS=. WORKDIR="${PWD}" make -esC "${MYOS:-.}" "$@" # so a machine can pin its deployment dir and run myos from anywhere
IFS=$'\n'; exec env $(cat "$MYOS_CONF" 2>/dev/null) MYOS=. WORKDIR="${WORKDIR:-${PWD}}" make -esC "${MYOS:-.}" "$@"