diff --git a/CHANGELOG.md b/CHANGELOG.md index 4552115..c64a5f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v1.0-beta - 2026-07-29 + +* split make files in `myos` project and docker files in `stack` project + ## v1.0-alpha - 2022-11-29 * node is host diff --git a/docker/compose/Dockerfile b/docker/compose/Dockerfile new file mode 100644 index 0000000..99e3b4b --- /dev/null +++ b/docker/compose/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:latest as dist +LABEL maintainer aynic.os +ARG DOCKER_BUILD_DIR + +ARG COMPOSE_REMOTE=https://github.com/docker/compose +ARG COMPOSE_VERSION=2.5.0 +ARG DOCKER_MACHINE=x86_64 +ARG DOCKER_SYSTEM=Linux + +RUN apk update \ + && apk add --no-cache ca-certificates \ + && OS="$(echo ${DOCKER_SYSTEM} |awk '{print tolower($0)}')" \ + && ARCH="$(echo ${DOCKER_MACHINE} |awk '{print /armv7l/ ? "armv7" : $0}')" \ + && wget -qO /usr/bin/docker-compose ${COMPOSE_REMOTE}/releases/download/v${COMPOSE_VERSION}/docker-compose-${OS}-${ARCH} \ + && chmod +x /usr/bin/docker-compose + +ENTRYPOINT ["/usr/bin/docker-compose"] + +FROM dist as master +ARG DOCKER_BUILD_DIR diff --git a/docker/myos/.bash_profile b/docker/myos/.bash_profile new file mode 100644 index 0000000..ebf1f8b --- /dev/null +++ b/docker/myos/.bash_profile @@ -0,0 +1,19 @@ +# shellcheck shell=bash source=/dev/null +# ~/.bash_profile: executed by the command interpreter for bash login shell. + +# bash-completion +if ! shopt -oq posix && [ -z "${BASH_COMPLETION_VERSINFO-}" ]; then + if [ "${BASH_VERSINFO[0]}" -gt 4 ] \ + || { [ "${BASH_VERSINFO[0]}" -eq 4 ] && [ "${BASH_VERSINFO[1]}" -ge 1 ] ;}; then + shopt -q progcomp && for file in \ + /{*/local,usr}/share/bash-completion/bash_completion \ + /etc/bash_completion; do + [ -r "$file" ] && . "$file" + done + fi + if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/bash_completion" ]; then + . "${XDG_CONFIG_HOME:-$HOME/.config}/bash_completion" + fi +fi + +[ -f ~/.sh_profile ] && . ~/.sh_profile diff --git a/docker/myos/.screenrc b/docker/myos/.screenrc new file mode 100644 index 0000000..344dec4 --- /dev/null +++ b/docker/myos/.screenrc @@ -0,0 +1,3 @@ +hardstatus alwayslastline "%{= kw}[%{G}%H%{-}] \# %?%-Lw%?[%{G}%n%f %t%{-}]%?%+Lw%?%?%=%-17< [%{B}%l%{-}]" +defscrollback 1048576 +shell -$SHELL diff --git a/docker/myos/.sh_profile b/docker/myos/.sh_profile new file mode 100644 index 0000000..0506b86 --- /dev/null +++ b/docker/myos/.sh_profile @@ -0,0 +1,39 @@ +# shellcheck shell=sh source=/dev/null +# ~/.sh_profile: executed by the command interpreter for login shells. + +# source ~/.*aliases and ~/.*functions files +for source in aliases functions; do + for file in "$HOME"/.*"$source"; do + [ -f "$file" ] || continue + # remove $HOME/. prefix from file + file="${file#${HOME}/.}" + # remove _$source suffix from $file + command="${file%_$source}" + # source file if command exists, ie ~/.bash_aliases + command -v "$command" >/dev/null 2>&1 && . "${HOME}/.$file" + # remove $source suffix from $file, ie ~/.aliases + command="${file%$source}" + # source file if command empty, ie ~/.aliases + [ -z "$command" ] && . "${HOME}/.$file" + done +done + +# source ~/.*shrc +for file in "$HOME"/.*shrc; do + [ -f "$file" ] || continue + # remove $HOME/. prefix from file + file="${file#${HOME}/.}" + # source file if match current shell + [ "$(basename "${SHELL}")" = "${file%rc}" ] && . "${HOME}/.$file" +done + +# set PATH to include user's bin +for path in /*/local/sbin /*/local/bin /*/local/*/bin "${HOME}"/.*/bin; do + [ -d "$path" ] || continue + case ":${PATH}:" in + *:"$path":*) ;; + *) export PATH="${path}:$PATH" ;; + esac +done + +[ -f ~/.profile ] && . ~/.profile diff --git a/docker/myos/.shrc b/docker/myos/.shrc new file mode 100644 index 0000000..6ffc0a6 --- /dev/null +++ b/docker/myos/.shrc @@ -0,0 +1,5 @@ +export EDITOR=vim +export GIT_PS1_SHOWUPSTREAM=auto +export GIT_PS1_SHOWDIRTYSTATE=true +export GIT_PS1_HIDE_IF_PWD_IGNORED=true +export PAGER=less diff --git a/docker/myos/.tmux.conf b/docker/myos/.tmux.conf new file mode 100644 index 0000000..5957741 --- /dev/null +++ b/docker/myos/.tmux.conf @@ -0,0 +1,32 @@ +# this is aya's tmux configuration ! +bind r source-file ~/.tmux.conf\; display-message "tmux.conf reloaded!" +bind R move-window -r +bind C-n switch-client -n\; refresh-client -S +bind C-p switch-client -p\; refresh-client -S +bind Escape copy-mode +bind Y run "tmux save-buffer - |xsel -i" +bind P run "xsel -o |tmux load-buffer -; tmux paste-buffer" +bind C-c run "tmux save-buffer - | xclip -i -sel clipboard >/dev/null" +bind C-v run "tmux set-buffer \"$(xclip -o -sel clipboard)\"; tmux paste-buffer" +bind -n S-down new-window +bind -n S-left prev +bind -n S-right next +bind -n C-left swap-window -t -1 +bind -n C-right swap-window -t +1 +set -g aggressive-resize on +set -g status-keys vi +setw -g mode-keys vi +setw -g window-status-current-bg blue +setw -g window-status-current-fg white +setw -g monitor-activity on +set -g visual-activity on +setw -g automatic-rename on +set -g default-terminal "screen" +set -g history-limit 4242 +set -g status-bg black +set -g status-fg white +set -g status-interval 60 +set -g status-left-length 30 +set -g status-left '<#[fg=green]#S#[default]> ' +set -g status-right '#[fg=yellow] %d/%m %H:%M#[default]' +set -g update-environment "SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION" diff --git a/docker/myos/Dockerfile b/docker/myos/Dockerfile new file mode 100644 index 0000000..0a72a96 --- /dev/null +++ b/docker/myos/Dockerfile @@ -0,0 +1,179 @@ +FROM alpine:latest as dist +LABEL maintainer aynic.os +ARG DOCKER_BUILD_DIR + +ARG DOCKER_MACHINE=x86_64 +ARG DOCKER_SYSTEM=Linux +ARG GIT_AUTHOR_EMAIL +ARG GIT_AUTHOR_NAME + +ENV GIT_AUTHOR_EMAIL=${GIT_AUTHOR_EMAIL} +ENV GIT_AUTHOR_NAME=${GIT_AUTHOR_NAME} +ENV GIT_COMMITTER_EMAIL=${GIT_AUTHOR_EMAIL} +ENV GIT_COMMITTER_NAME=${GIT_AUTHOR_NAME} + +RUN apk upgrade --no-cache \ + && apk add --no-cache \ + bash \ + curl \ + gettext \ + git \ + gpg \ + gpg-agent \ + make \ + nano \ + netcat-openbsd \ + openssh \ + screen \ + socat \ + tmux \ + wget \ + xz + +RUN git clone https://github.com/ingydotnet/git-subrepo \ + && cd git-subrepo \ + && git fetch origin +refs/heads/release/0.4.0: \ + && git checkout release/0.4.0 \ + && git fetch origin pull/314/head \ + && git rebase 9cbe7ba2f61552ce97fb312c8133813f970ab4a5 \ + && sed -i 's/install -C/install/' Makefile \ + && make install \ + && cd .. \ + && rm -rf git-subrepo + +ARG COMPOSE_REMOTE=https://github.com/docker/compose +ARG COMPOSE_VERSION=2.5.0 +ARG IPFS_VERSION=0.24.0 + +RUN OS="$(echo ${DOCKER_SYSTEM} |awk '{print tolower($0)}')" \ + && ARCH="$(echo ${DOCKER_MACHINE} |awk '{print /armv7l/ ? "armv7" : $0}')" \ + && wget -qO /usr/bin/docker-compose ${COMPOSE_REMOTE}/releases/download/v${COMPOSE_VERSION}/docker-compose-${OS}-${ARCH} \ + && chmod +x /usr/bin/docker-compose \ + && OS="$(echo ${DOCKER_SYSTEM} |awk '{print tolower($0)}')" \ + && ARCH="$(echo ${DOCKER_MACHINE})" \ + && wget -qO - https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.${OS}.${ARCH}.tar.xz \ + |tar --strip-components 1 -C /usr/local/bin -xJf - shellcheck-stable/shellcheck \ + && ARCH="$(echo ${DOCKER_MACHINE} |awk '{print /x86_64/ ? "amd64" : /arch64/ ? "arm64" : /armv(6|7)/ ? "arm" : $0}')" \ + && wget -qO - https://github.com/ipfs/kubo/releases/download/v${IPFS_VERSION}/kubo_v${IPFS_VERSION}_${OS}-${ARCH}.tar.gz \ + |tar --strip-components 1 -C /usr/local/bin -xzf - kubo/ipfs \ + && mkdir -p /usr/local/lib/shellspec \ + && wget -qO - https://github.com/shellspec/shellspec/archive/refs/heads/master.tar.gz \ + |tar --strip-components 1 -C /usr/local/lib/shellspec -xzf - \ + && ln -s /usr/local/lib/shellspec/shellspec /usr/local/bin/shellspec + +ADD https://raw.github.com/kvz/cronlock/master/cronlock /usr/local/bin/cronlock +RUN chmod +rx /usr/local/bin/cronlock + +# Setup environment variables; export SSH_AUTH_SOCK from socket directory +ENV SOCKET_DIR /tmp/ssh-agent +ENV SSH_AUTH_SOCK ${SOCKET_DIR}/socket +ENV SSH_AUTH_PROXY_SOCK ${SOCKET_DIR}/proxy-socket + +COPY ${DOCKER_BUILD_DIR}/docker-entrypoint.sh /docker-entrypoint.sh +ENTRYPOINT ["/docker-entrypoint.sh"] + +CMD ["start"] + +FROM dist as master +ARG DOCKER_BUILD_DIR +ARG DOCKER_GID +ARG SHELL=/bin/bash +ARG SSH_BASTION_HOSTNAME +ARG SSH_BASTION_USERNAME +ARG SSH_PRIVATE_IP_RANGE +ARG SSH_PUBLIC_HOSTS +ARG UID +ARG USER +ENV UID=${UID} +ENV GID=${UID} +ENV USER=${USER} + +RUN apk add --no-cache \ + docker \ + mysql-client \ + postgresql-client \ + sudo \ + vim \ + zsh + +# If we provide a numeric UID +RUN [ "$UID" -eq "$UID" ] 2>/dev/null \ +# Remove user with $UID if it is not our $USER + && if [ "$(getent passwd $UID |awk -F: '{print $1}')" != "$USER" ]; then \ + sed -i '/^'$(getent passwd $UID |awk -F: '{print $1}')':x:'$UID':/d' /etc/passwd; \ + sed -i '/^'$(getent group $GID |awk -F: '{print $1}')':x:'$GID':/d' /etc/group; \ + fi \ +# Force $UID if our $USER already exists + && sed -i 's/^'$USER':x:[0-9]\+:[0-9]\+:/'$USER':x:'$UID':'$GID':/' /etc/passwd \ + && sed -i 's/^'$USER':x:[0-9]\+:/'$USER':x:'$GID':/' /etc/group \ +# Create $USER if it does not exist + && if [ "$(getent passwd $UID)" = "" ]; then \ + echo "$USER:x:$UID:$GID::/home/$USER:$SHELL" >> /etc/passwd; \ + echo "$USER:\!:$(($(date +%s) / 60 / 60 / 24)):0:99999:7:::" >> /etc/shadow; \ + echo "$USER:x:$GID:" >> /etc/group; \ + fi \ + && mkdir -p /home/$USER \ + && chown $UID:$GID /home/$USER \ + || true + +# If we provide a numeric DOCKER_GID +RUN [ "$DOCKER_GID" -eq "$DOCKER_GID" ] 2>/dev/null \ + && if [ "$(getent group docker |awk -F: '{print $3}')" != "$DOCKER_GID" ]; then \ + sed -i 's/^docker:x:[0-9]\+:/docker:x:'$DOCKER_GID':/' /etc/group; \ + fi \ +|| true + +## User groups +RUN adduser $USER wheel \ +# && adduser $USER docker \ + && echo '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' >> /etc/sudoers + +RUN echo -e "\n\ +Host *\n\ + LogLevel quiet\n\ + Compression yes\n\ +" >> /etc/ssh/ssh_config \ + && if [ -n "${SSH_PRIVATE_IP_RANGE}" ] && [ -n "${SSH_BASTION_HOSTNAME}" ]; then \ + echo -e "\ +Host ${SSH_PRIVATE_IP_RANGE}\n\ + ProxyCommand ssh -q ssh-bastion nc -q0 %h 22\n\ + HostName %h\n\ + StrictHostKeyChecking no\n\ + UserKnownHostsFile /dev/null\n\ +Host ssh-bastion\n\ + HostName ${SSH_BASTION_HOSTNAME}\ +" >> /etc/ssh/ssh_config; \ + if [ -n "${SSH_BASTION_USERNAME}" ]; then \ + echo -e "\ + User ${SSH_BASTION_USERNAME}\n\ +" >> /etc/ssh/ssh_config; \ + fi \ + fi + +# Custom rc functions +COPY ${DOCKER_BUILD_DIR}/rc*.sh /etc/profile.d/ + +RUN mkdir -p $SOCKET_DIR && chown $USER $SOCKET_DIR + +VOLUME ${SOCKET_DIR} + +USER $USER +ENV SHELL=${SHELL} +WORKDIR /home/$USER + +# git config +RUN mkdir -p ~/.ssh ~/.config/git \ + && ssh-keyscan -t rsa -H ${SSH_PUBLIC_HOSTS} >> ~/.ssh/known_hosts \ + && echo -e "\ +.DS_Store\n\ +.idea/\n\ +.nfs*\n\ +.theia/settings.json\n\ +*~\n\ +*.log\n\ +*.swp\n\ +Thumbs.db\n\ +" > ~/.config/git/ignore + +# dot files +COPY ${DOCKER_BUILD_DIR}/.* /home/$USER/ diff --git a/docker/myos/docker-entrypoint.sh b/docker/myos/docker-entrypoint.sh new file mode 100755 index 0000000..adc05ba --- /dev/null +++ b/docker/myos/docker-entrypoint.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env sh +set -euo errexit + +# Print a debug message if debug mode is on ($DEBUG is not empty) +# @param message +debug_msg () +{ + if [ -n "${DEBUG:-}" -a "${DEBUG:-}" != "false" ]; then + echo "$@" + fi +} + +case "${1:-start}" in + + start) + debug_msg "Starting..." + + # Create proxy-socket for ssh-agent (to give everyone access to the ssh-agent socket) + debug_msg "Create proxy socket..." + rm -f ${SSH_AUTH_SOCK} ${SSH_AUTH_PROXY_SOCK} > /dev/null 2>&1 + socat UNIX-LISTEN:${SSH_AUTH_PROXY_SOCK},perm=0666,fork UNIX-CONNECT:${SSH_AUTH_SOCK} & + + debug_msg "Launch ssh-agent..." + exec /usr/bin/ssh-agent -a ${SSH_AUTH_SOCK} -D >/dev/null + ;; + + *) + debug_msg "Exec: $@" + exec "$@" + ;; +esac diff --git a/docker/myos/rc.sh b/docker/myos/rc.sh new file mode 100644 index 0000000..87dd6a7 --- /dev/null +++ b/docker/myos/rc.sh @@ -0,0 +1,45 @@ +# shellcheck shell=sh +# file rc.sh: Call user defined functions +## author: Yann "aya" Autissier +## license: GPL +## version: 20220630 + +case $- in + # if this is an interactive shell + *i*) + # load user stuff from ~/.rc.d/* files + for file in "${HOME}"/.rc.d/*; do + # read files only + if [ -f "${file}" ]; then + func_name=$(basename "${file}") + func_args=$(cat "${file}") + # at this stage, func_name can start with numbers to allow ordering function calls with file names starting with numbers + # func_name must start with a letter, remove all other characters at the beginning of func_name until a letter is found + while [ "${func_name}" != "" ] && [ "${func_name#[a-z]}" = "${func_name}" ]; do + # remove first char of func_name + func_name="${func_name#?}" + done + # call user function with args passed from the content of the file + command -v "${func_name}" >/dev/null 2>&1 && "${func_name}" "${func_args}" + fi + done + # load user stuff from RC_* env vars + IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}"; for line in $(printenv 2>/dev/null |awk '$0 ~ /^RC_[0-9A-Z_]*=/' |sort); do + func_name=$(printf '%s\n' "${line%%=*}" |awk '{print tolower(substr($0,4))}') + eval func_args=\$"${line%%=*}" + [ "${func_args}" = "false" ] && continue + [ "${func_args}" = "true" ] && unset func_args + # at this stage, func_name can start with numbers to allow ordering function calls with file names starting with numbers + # func_name must start with a letter, remove all other characters at the beginning of func_name until a letter is found + while [ "${func_name}" != "" ] && [ "${func_name#[a-z]}" = "${func_name}" ]; do + # remove first char of func_name + func_name="${func_name#?}" + done + # 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}" + done + unset IFS + ;; +esac + +# vim:ts=2:sw=2:sts=2:et diff --git a/docker/myos/rc_functions.sh b/docker/myos/rc_functions.sh new file mode 100644 index 0000000..7d40707 --- /dev/null +++ b/docker/myos/rc_functions.sh @@ -0,0 +1,286 @@ +# shellcheck shell=sh +# file rc_functions.sh: Define shell functions +## author: Yann "aya" Autissier +## license: GPL +## version: 20221229 + +# function force: Run a command sine die +force() { + if [ $# -gt 0 ]; then + while true; do + "$@" + sleep 1 + done + fi +} + +# function force8: Run a command sine die if not already running +force8() { + if [ $# -gt 0 ]; then + while true; do + # awk expression to match $@ + [ "$(ps wwx -o args 2>/dev/null |awk -v field="${PS_X_FIELD:-1}" ' + BEGIN { nargs=split("'"$*"'",args); } + # first field matched + $field == args[1] { + matched=1; + # match following fields + for (i=1;i<=NF-field;i++) { + if ($(i+field) == args[i+1]) { matched++; } + }; + # all fields matched + if (matched == nargs) { found++; } + } + END { print found+0; }' + )" = 0 ] && "$@" + sleep 1 + done + fi +} + +# function lang_set; Export default LANG +lang_set() { + export $(awk -F'=' '$1 == "LANG"' /etc/default/locale 2>/dev/null) >/dev/null +} + +# function load_average; Print the current load average +load_average() { + uptime 2>/dev/null |awk '{printf "%.1f\n", $(NF-2)}' +} + +# function process_count: Print number of "processes"/"running processes"/"D-state" +process_count() { + ps ax -o stat 2>/dev/null |awk ' + $1 ~ /R/ {process_running++}; + $1 ~ /D/ {process_dstate++}; + END { print NR-1"/"process_running+0"/"process_dstate+0; }' +} + +# function prompt_set: Export custom PROMPT_COMMAND +prompt_set() { + case "${TERM}" in + screen*) + ESCAPE_CODE_DCS="\033k" + ESCAPE_CODE_ST="\033\\" + ;; + linux*|xterm*|rxvt*) + ESCAPE_CODE_DCS="\033]0;" + ESCAPE_CODE_ST="\007" + ;; + *) + ;; + esac + # in a screen + if [ -n "${STY}" ]; then + export PROMPT_COMMAND='printf\ + "${ESCAPE_CODE_DCS:-\033]0;}%s${ESCAPE_CODE_ST:-\007}"\ + "${PWD##*/}"' + else + export PROMPT_COMMAND='printf\ + "${ESCAPE_CODE_DCS:-\033]0;}%s@%s:%s${ESCAPE_CODE_ST:-\007}"\ + "${USER}"\ + "${HOSTNAME%%.*}"\ + "${PWD##*/}"' + fi + unset ESCAPE_CODE_DCS ESCAPE_CODE_ST +} + +# function ps1_set: Export custom PS1 +ps1_set() { + case "$0" in + *sh) + COLOR_DGRAY="\[\033[1;30m\]" + COLOR_RED="\[\033[01;31m\]" + COLOR_GREEN="\[\033[01;32m\]" + COLOR_BROWN="\[\033[0;33m\]" + COLOR_YELLOW="\[\033[01;33m\]" + COLOR_BLUE="\[\033[01;34m\]" + COLOR_CYAN="\[\033[0;36m\]" + COLOR_GRAY="\[\033[0;37m\]" + COLOR_RESET="\[\033[0m\]" + ;; + *) + ;; + esac + + PS1_STATUS="\$?" + PS1_COUNT="${COLOR_DGRAY}[\` + case \"$PS1_STATUS\" in + 0) + printf \"${COLOR_BLUE}${PS1_STATUS}\";; + 1) + printf \"${COLOR_YELLOW}${PS1_STATUS}\";; + *) + printf \"${COLOR_RED}${PS1_STATUS}\";; + esac + type process_count >/dev/null 2>&1 && printf\ + \"${COLOR_DGRAY}|${COLOR_BLUE}%s\"\ + \"\$(process_count 2>/dev/null)\" + type user_count >/dev/null 2>&1 && printf\ + \"${PS1_COUNT}${COLOR_DGRAY}|${COLOR_BLUE}%s\"\ + \"\$(user_count 2>/dev/null)\" + type load_average >/dev/null 2>&1 && printf\ + \"${PS1_COUNT}${COLOR_DGRAY}|${COLOR_BLUE}%s\"\ + \"\$(load_average 2>/dev/null)\" + \`${COLOR_DGRAY}]${COLOR_RESET}" + PS1_END="${COLOR_DGRAY}\$( + if [ \"\$(id -u)\" = 0 ]; then + printf \"#\"; + else + printf \"\$\"; + fi + )${COLOR_RESET}" + PS1_GIT="\$( + if type __git_ps1 >/dev/null 2>&1; then + printf \"\$(__git_ps1 2>/dev/null \" (%s)\")\" + else + printf \"\$(BRANCH=\$(git rev-parse --abbrev-ref HEAD 2>/dev/null);\ + [ -n \"\${BRANCH}\" ] && printf \" (\${BRANCH})\")\" + fi + )" + PS1_GIT="${COLOR_CYAN}${PS1_GIT}${COLOR_RESET}" + PS1_HOSTNAME_COLOR="\`case \"\${ENV}${HOSTNAME%%.*}\" in + *[Pp][Rr][0Oo][Dd]*|*[Pp][Rr][Dd]*) + printf \"${COLOR_RED}\";; + *) + if [ -n \"\${ENV}\" ]; then + printf \"${COLOR_YELLOW}\"; + else + printf \"${COLOR_GREEN}\"; + fi;; + esac\`" + PS1_HOSTNAME="${PS1_HOSTNAME_COLOR}\$(hostname |sed 's/\..*//')${COLOR_RESET}" + PS1_USER_COLOR="\$( + if [ \"\$(id -u)\" = 0 ]; then + printf \"${COLOR_RED}\"; + else + printf \"${COLOR_BROWN}\"; + fi + )" + PS1_USER="${PS1_USER_COLOR}\$(id -nu):\$(id -u)${COLOR_RESET}" + PS1_WORKDIR="${COLOR_GRAY}\$( + pwd |sed 's|^'\${HOME}'\(/.*\)*$|~\1|' + )${COLOR_RESET}" + PS1="${PS1_COUNT}${PS1_USER}${COLOR_DGRAY}@${PS1_HOSTNAME}" + PS1="${PS1}${COLOR_DGRAY}:${PS1_WORKDIR}${PS1_GIT}${PS1_END} " + export 'PS1' + unset PS1_COUNT PS1_END PS1_GIT PS1_HOSTNAME PS1_HOSTNAME_COLOR\ + PS1_USER PS1_USER_COLOR PS1_STATUS PS1_WORKDIR +} + +# function screen_attach: Attach existing screen session or Create a new one +screen_attach() { + command -v screen >/dev/null 2>&1 || return + SCREEN_SESSION="$(id -nu)@$(hostname |sed 's/\..*//')" + if [ -z "${STY}" ]; then + # attach screen in tmux window 0 only ;) + [ -n "${TMUX}" ] \ + && [ "$(tmux list-window 2>/dev/null |awk '$NF == "(active)" {print $1}'\ + |sed 's/:$//')" != "0" ] \ + && return + printf 'Attaching screen.' && sleep 1\ + && printf '.' && sleep 1\ + && printf '.' && sleep 1 + exec screen -xRR -S "${SCREEN_SESSION}" + fi + unset SCREEN_SESSION +} + +# function screen_detach: Detach current screen session +screen_detach() { + screen -d +} + +# function ssh_add: Load all private keys in ~/.ssh/ to ssh agent +ssh_add() { + command -v ssh-agent >/dev/null 2>&1 && command -v ssh-add >/dev/null 2>&1 || return + SSH_AGENT_DIR="/tmp/ssh-$(id -u)" + SSH_AGENT_SOCK="${SSH_AGENT_DIR}/agent@$(hostname |sed 's/\..*//')" + # launch a new agent + if [ -z "${SSH_AUTH_SOCK}" ]; then + [ ! -d "${SSH_AGENT_DIR}" ] \ + && mkdir -p "${SSH_AGENT_DIR}" 2>/dev/null\ + && chmod 0700 "${SSH_AGENT_DIR}" + # search for an already running agent + if ps wwx -o args |awk '$1 ~ "ssh-agent$" && $3 == "'"${SSH_AGENT_SOCK}"'"' |wc -l |grep -q 0; then + rm -f "${SSH_AGENT_SOCK}" + ssh-agent -a "${SSH_AGENT_SOCK}" >/dev/null 2>&1 + fi + fi + # attach to agent + export SSH_AUTH_SOCK="${SSH_AUTH_SOCK:-${SSH_AGENT_SOCK}}" + # list private keys to add + # shellcheck disable=SC2068 + for dir in ${@:-${HOME}/.ssh}; do + if [ "${SSH_ADD_RECURSIVE:-}" = true ]; then + GREP_RECURSIVE_FLAG="r" + else + GREP_RECURSIVE_CHAR="*" + 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)")" + done + # shellcheck disable=SC2086 + printf '%s\n' ${SSH_PRIVATE_KEYS} |while read -r file; do + [ -r "${file}" ] || continue + # 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}" + done + unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_AGENT_DIR SSH_AGENT_SOCK SSH_PRIVATE_KEYS +} + +# function ssh_del: removes all private keys in ~/.ssh/ from ssh agent +ssh_del() { + command -v ssh-add >/dev/null 2>&1 || return + # attach to agent + if [ -z "${SSH_AUTH_SOCK}" ]; then + return + fi + # list private keys to del + # shellcheck disable=SC2068 + for dir in ${@:-${HOME}/.ssh}; do + if [ "${SSH_DEL_RECURSIVE:-}" = true ]; then + GREP_RECURSIVE_FLAG="r" + else + GREP_RECURSIVE_CHAR="*" + 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)")" + done + # shellcheck disable=SC2086 + printf '%s\n' ${SSH_PRIVATE_KEYS} |while read -r file; do + [ -r "${file}" ] || continue + # 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}" + done + unset GREP_RECURSIVE_CHAR GREP_RECURSIVE_FLAG SSH_PRIVATE_KEYS +} + +# function tmux_attach: Attach existing tmux session or Create a new one +tmux_attach() { + command -v tmux >/dev/null 2>&1 || return + TMUX_SESSION="$(id -nu)@$(hostname |sed 's/\..*//')" + # do not attach tmux in screen ;) + if [ -z "${TMUX}" -a -z "${STY}" ]; then + printf 'Attaching tmux.' && sleep 1\ + && printf '.' && sleep 1\ + && printf '.' && sleep 1 + exec tmux -L"${TMUX_SESSION}" new-session -A -s"${TMUX_SESSION}" + fi + unset TMUX_SESSION +} + +# function tmux_detach: Detach current tmux session +tmux_detach() { + tmux detach +} + +# function user_count: Print number of "users sessions"/"users"/"logged users" +user_count() { + ps ax -o pid,user,tty,comm 2>/dev/null |awk ' + $3 ~ /^(pts\/|tty[sS]?|[0-9]+,)[0-9]+$/ && $4 != "getty" { users_sessions++; logged[$2]++; }; + $1 ~ /^[0-9]+$/ { count[$2]++; } + END { + for (uc in count) { c = c" "uc; }; users_count=split(c,v," "); + for (ul in logged) { l = l" "ul; }; users_logged=split(l,v," "); + print users_sessions+0"/"users_count+0"/"users_logged+0; + }' +} diff --git a/make/apps/common.mk b/make/apps/common.mk index 669a1d3..9fe59f9 100644 --- a/make/apps/common.mk +++ b/make/apps/common.mk @@ -121,8 +121,8 @@ logs: docker-stack-logs ## Display application dockers logs # target ps: List application dockers # on local host -.PHONY: ps -ps: docker-stack-ps ## List application dockers +.PHONY: ps status +ps status: docker-stack-ps ## List application dockers # target rebuild: Rebuild application docker images # on local host @@ -137,8 +137,8 @@ rebuild@%: # target recreate: Recreate application dockers # on local host -.PHONY: recreate -recreate: docker-stack-recreate app-start ## Recreate application dockers +.PHONY: recreate reload +recreate reload: docker-stack-recreate app-start ## Recreate application dockers # target reinstall: Fire clean, Call .env target, Call install target # on local host diff --git a/make/apps/def.docker.mk b/make/apps/def.docker.mk index eb5c443..16d5281 100644 --- a/make/apps/def.docker.mk +++ b/make/apps/def.docker.mk @@ -28,7 +28,7 @@ DOCKER_BUILD_CACHE ?= true DOCKER_BUILD_LABEL ?= $(foreach var,$(filter $(BUILD_LABEL_VARS),$(MAKE_FILE_VARS)),$(if $($(var)),--label $(var)='$($(var))')) DOCKER_BUILD_NO_CACHE ?= false DOCKER_BUILD_TARGET ?= $(if $(filter $(ENV),$(DOCKER_BUILD_TARGETS)),$(ENV),$(DOCKER_BUILD_TARGET_DEFAULT)) -DOCKER_BUILD_TARGET_DEFAULT ?= master +DOCKER_BUILD_TARGET_DEFAULT ?= DOCKER_BUILD_TARGETS ?= $(ENV_DEPLOY) DOCKER_BUILD_VARS ?= APP BRANCH COMPOSE_VERSION DOCKER_GID DOCKER_MACHINE DOCKER_REPOSITORY DOCKER_SYSTEM GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME SSH_REMOTE_HOSTS USER VERSION DOCKER_COMPOSE ?= $(or $(shell $(call verle,$(COMPOSE_VERSION),$(shell docker compose version --short 2>/dev/null)) && printf 'docker $(DOCKER_ARGS) compose\n'),$(shell $(call verle,$(COMPOSE_VERSION),$(shell docker-compose version --short 2>/dev/null)) && printf 'docker-compose\n')) @@ -42,7 +42,7 @@ DOCKER_COMPOSE_RUN_OPTIONS ?= --rm $(DOCKER_COMPOSE_RUN_ENTRYPOINT) $(DOCKE DOCKER_COMPOSE_RUN_WORKDIR ?= $(if $(DOCKER_COMPOSE_WORKDIR),-w $(DOCKER_COMPOSE_WORKDIR)) DOCKER_COMPOSE_SERVICE_NAME ?= $(subst _,-,$(DOCKER_COMPOSE_PROJECT_NAME)) DOCKER_COMPOSE_UP_OPTIONS ?= -d -DOCKER_IMAGE_TAG ?= $(if $(filter true,$(DEPLOY)),$(if $(filter $(ENV),$(ENV_DEPLOY)),$(VERSION)),$(if $(DRONE_BUILD_NUMBER),$(DRONE_BUILD_NUMBER),$(ENV))) +DOCKER_IMAGE_TAG ?= $(if $(filter true,$(DEPLOY)),$(if $(filter $(ENV),$(ENV_DEPLOY)),$(VERSION)),$(if $(DRONE_BUILD_NUMBER),$(DRONE_BUILD_NUMBER),latest)) DOCKER_IMAGES ?= $(patsubst %/,%,$(patsubst docker/%,%,$(dir $(wildcard docker/*/Dockerfile)))) DOCKER_PLUGIN ?= rexray/s3fs:latest DOCKER_PLUGIN_ARGS ?= $(foreach var,$(DOCKER_PLUGIN_VARS),$(if $(DOCKER_PLUGIN_$(var)),$(var)='$(DOCKER_PLUGIN_$(var))')) @@ -71,23 +71,27 @@ endif # function compose-file: Search compose files to update variable COMPOSE_FILE define compose-file $(call INFO,compose-file,$(1)$(comma) $(2)$(comma) $(3)$(comma) $(4)) - $(eval file_path := $(or $(1),. $(APP_DOCKER_DIR))) + $(eval file_path := $(or $(1),$(APP_DOCKER_DIR))) $(eval file_name := $(or $(2),$(DOCKER_COMPOSE_FILE))) $(eval file_suffix := $(or $(3),$(COMPOSE_FILE_SUFFIX))) $(eval file_extension := $(or $(4),yml yaml)) $(eval COMPOSE_FILE += $(wildcard $(foreach e,$(file_extension),$(foreach n,$(file_name),$(foreach p,$(file_path),$(p)/$(n).$(e) $(p)/$(n).$(ENV).$(e) $(p)/$(ENV)/$(n).$(e) $(p)/$(ENV)/$(n).$(ENV).$(e) $(foreach s,$(file_suffix),$(p)/$(n).$(s).$(e) $(p)/$(n).$(s).$(ENV).$(e))))))) $(call debug,COMPOSE_FILE) endef -# function docker-build: Build docker image +# function docker-build: Build docker image from dockerfile 1 for service 2 with tag 3 at target 4 define docker-build - $(call INFO,docker-build,$(1)$(comma) $(2)$(comma) $(3)) - $(eval build_path := $(patsubst $(DOCKER_BUILD_PATH)/%,%,$(patsubst %/,%,$(1)))) - $(eval build_service := $(subst .,,$(call LOWERCASE,$(lastword $(subst /, ,$(build_path)))))) - $(eval build_image_tag := $(or $(2),$(DOCKER_REPOSITORY)/$(build_service):$(DOCKER_IMAGE_TAG))) - $(eval build_target := $(subst ",,$(subst ',,$(or $(3),$(DOCKER_BUILD_TARGET))))) + $(call INFO,docker-build,$(1)$(comma) $(2)$(comma) $(3)$(comma) $(4)) + $(eval build_path := $(firstword $(subst $(DOCKER_DIR_NAME), ,$(1)))) + $(eval build_dir := $(patsubst $(or $(addsuffix /,$(DOCKER_BUILD_PATH)),$(build_path))%,%,$(patsubst %/,%,$(1)))) + $(eval build_service := $(or $(2),$(subst .,,$(call LOWERCASE,$(lastword $(subst /, ,$(build_dir))))))) + $(eval build_image_tag := $(DOCKER_REPOSITORY)/$(if $(findstring :,$(build_service)),$(build_service),$(build_service):$(or $(3),$(DOCKER_IMAGE_TAG)))) + $(eval build_target := $(subst ",,$(subst ',,$(or $(4),$(DOCKER_BUILD_TARGET))))) $(eval image_id := $(shell docker images -q $(build_image_tag) 2>/dev/null)) $(eval build_image := $(or $(filter false,$(DOCKER_BUILD_CACHE)),$(if $(image_id),,true))) - $(if $(build_image),$(RUN) docker build $(DOCKER_BUILD_ARGS) --build-arg DOCKER_BUILD_PATH="$(build_path)" $(DOCKER_BUILD_LABEL) --build_image_tag $(build_image_tag) $(if $(build_target),--build_target $(build_target)) -f $(build_path)/Dockerfile $(or $(DOCKER_BUILD_PATH),.),$(call INFO,docker image $(build_image_tag) has id $(image_id))) + $(call debug,build_path build_dir build_image_tag) + $(if $(build_image),\ + $(RUN) docker build $(DOCKER_BUILD_ARGS) --build-arg DOCKER_BUILD_DIR="$(build_dir)" $(DOCKER_BUILD_LABEL) --tag $(build_image_tag) $(if $(build_target),--target $(build_target)) -f $(build_dir)/Dockerfile $(or $(DOCKER_BUILD_PATH),$(build_path),.) \ + ,$(call INFO,docker image $(build_image_tag) has id $(image_id))) endef # function docker-commit: Commit docker image define docker-commit @@ -106,7 +110,7 @@ define docker-compose $(if $(DOCKER_COMPOSE), $(call env-exec,$(RUN) $(DOCKER_COMPOSE) $(DOCKER_COMPOSE_ARGS) $(patsubst %,-f %,$(COMPOSE_FILE)) -p $(COMPOSE_PROJECT_NAME) $(1)) , $(if $(DOCKER_RUN), - $(call docker-build,$(MYOS)/docker/compose,docker/compose:$(COMPOSE_VERSION)) + $(call docker-build,$(call docker-path,compose),docker/compose,$(COMPOSE_VERSION)) $(call docker-run,docker/compose:$(COMPOSE_VERSION) $(DOCKER_COMPOSE_ARGS),$(patsubst %,-f %,$(COMPOSE_FILE)) -p $(COMPOSE_PROJECT_NAME) $(1)) , $(call env-exec,$(RUN) docker-compose $(DOCKER_COMPOSE_ARGS) $(patsubst %,-f %,$(COMPOSE_FILE)) -p $(COMPOSE_PROJECT_NAME) $(1)) ) @@ -153,6 +157,7 @@ define docker-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_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))))))) + $(call debug,stack_path) $(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))) $(call env-vars,$(COMPOSE_FILE)) diff --git a/make/apps/def.mk b/make/apps/def.mk index ce75ae1..6f46cf2 100644 --- a/make/apps/def.mk +++ b/make/apps/def.mk @@ -1,5 +1,3 @@ -APP_DIR ?= $(CURDIR) -APP_DOCKER_DIR ?= $(DOCKER_DIR) APP_DOMAIN ?= $(patsubst %,$(APP_DOMAIN_PREFIX)%,$(DOMAIN)) APP_DOMAIN_PREFIX ?= $(if $(HOST_STACK),,$(if $(filter true,$(APP_HOST_MULTI_USER)),$(USER).)) APP_HOST ?= $(patsubst %,$(APP_HOST_PREFIX)%,$(if $(filter true,$(APP_HOST_MULTI_APP)),$(APP_NAME).)$(APP_DOMAIN))$(if $(HOST_STACK),$(if $(HOST_LB),$(space)$(DOMAIN))) diff --git a/make/apps/docker.mk b/make/apps/docker.mk index 2a40b74..a0a7c56 100644 --- a/make/apps/docker.mk +++ b/make/apps/docker.mk @@ -9,9 +9,13 @@ docker-build: docker-image-myos # target docker-build-%: Call docker-build for each Dockerfile in docker/% folder .PHONY: docker-build-% docker-build-%: stack - $(if $(wildcard docker/$*/Dockerfile),$(call docker-build,docker/$*)) - $(if $(findstring :,$*),$(eval DOCKER_FILE := $(wildcard docker/$(subst :,/,$*)/Dockerfile)),$(eval DOCKER_FILE := $(wildcard docker/$*/*/Dockerfile))) - $(foreach dockerfile,$(DOCKER_FILE),$(call docker-build,$(dir $(dockerfile)),$(DOCKER_REPOSITORY)/$(word 2,$(subst /, ,$(dir $(dockerfile)))):$(lastword $(subst /, ,$(dir $(dockerfile)))),"")) + $(if $(findstring :,$*), \ + $(eval DOCKER_FILE := $(wildcard $(DOCKER_DIR:%=%/$(subst :,/,$*)/Dockerfile) $(DOCKER_DIR:%=%/*/$(subst :,/,$*)/Dockerfile))) \ + ,$(eval DOCKER_FILE := $(wildcard $(DOCKER_DIR:%=%/$*/Dockerfile) $(DOCKER_DIR:%=%/$*/*/Dockerfile) $(DOCKER_DIR:%=%/*/$*/Dockerfile)))) + $(call debug,DOCKER_FILE) + $(foreach dockerfile,$(DOCKER_FILE), \ + $(eval docker_service := $(patsubst %/,%,$(lastword $(subst $(DOCKER_DIR_NAME)/, ,$(dir $(dockerfile)))))) \ + $(call docker-build,$(dir $(dockerfile)),$(if $(findstring :,$*),$(call pop,$(docker_service)),$(docker_service)),$(if $(findstring :,$*),$(lastword $(subst /, ,$(dir $(dockerfile))))))||:) # target docker-commit: Call docker-commit for each SERVICES .PHONY: docker-commit @@ -164,6 +168,10 @@ docker-network-rm-%: ||: ; \ fi +.PHONY: docker-path-% +docker-path-%: + echo $(call docker-path,$*) + # target docker-plugin-install: Run 'docker plugin install DOCKER_PLUGIN_OPTIONS DOCKER_PLUGIN' .PHONY: docker-plugin-install docker-plugin-install: @@ -264,3 +272,7 @@ docker-volume-rm: docker-volume-rm-$(COMPOSE_PROJECT_NAME) .PHONY: docker-volume-rm-% docker-volume-rm-%: docker volume ls |awk '$$2 ~ /^$*/ {print $$2}' |sort -u |while read volume; do $(RUN) docker volume rm $$volume; done + +.PHONY: stack-path-% +stack-path-%: + echo $(call stack-path,$*) diff --git a/make/apps/myos/ssh.mk b/make/apps/myos/ssh.mk index 4de7d99..5bd69ab 100644 --- a/make/apps/myos/ssh.mk +++ b/make/apps/myos/ssh.mk @@ -3,7 +3,7 @@ # target ssh: Call ssh-connect ARGS or SHELL .PHONY: ssh -ssh: ssh-get-PrivateIpAddress-$(SERVER_NAME) ## Connect to first remote host +ssh: # ssh-get-PrivateIpAddress-$(SERVER_NAME) ## Connect to first remote host $(call ssh-connect,$(AWS_INSTANCE_IP),$(if $(ARGS),$(ARGS),$(SHELL))) # target ssh-add: Fire ssh-key and ssh-add file SSH_PRIVATE_KEYS in folder SSH_DIR @@ -15,7 +15,7 @@ ssh-add: ssh-key # target ssh-connect: Call ssh-connect make connect SERVICE .PHONY: ssh-connect -ssh-connect: ssh-get-PrivateIpAddress-$(SERVER_NAME) +ssh-connect: # ssh-get-PrivateIpAddress-$(SERVER_NAME) $(call ssh-connect,$(AWS_INSTANCE_IP),make connect COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) ENV=$(ENV) $(if $(SERVICE),SERVICE=$(SERVICE))) # target ssh-del: ssh-add -d file SSH_PRIVATE_KEYS in folder SSH_DIR @@ -26,7 +26,7 @@ ssh-del: # target ssh-exec: Call ssh-exec make exec SERVICE ARGS .PHONY: ssh-exec -ssh-exec: ssh-get-PrivateIpAddress-$(SERVER_NAME) +ssh-exec: # ssh-get-PrivateIpAddress-$(SERVER_NAME) $(call ssh-exec,$(AWS_INSTANCE_IP),make exec COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) ENV=$(ENV) $(if $(SERVICE),SERVICE=$(SERVICE)) $(if $(ARGS),ARGS='\''"$(ARGS)"'\'')) # target ssh-get-PrivateIpAddress-%: Fire aws-ec2-get-instances-PrivateIpAddress-% @@ -43,5 +43,5 @@ endif # target ssh-run: Call ssh-run make run SERVICE ARGS .PHONY: ssh-run -ssh-run: ssh-get-PrivateIpAddress-$(SERVER_NAME) +ssh-run: # ssh-get-PrivateIpAddress-$(SERVER_NAME) $(call ssh-exec,$(AWS_INSTANCE_IP),make run $(if $(SERVICE),SERVICE=$(SERVICE)) $(if $(ARGS),ARGS='\''"$(ARGS)"'\'')) diff --git a/make/common.mk b/make/common.mk index 478f17e..13b127d 100644 --- a/make/common.mk +++ b/make/common.mk @@ -15,12 +15,13 @@ $(APP) $(APP_DIR): ## it includes apps/$(app)/*.mk file and hydrates APP_* variables ## ex: APP_REPOSITORY_URL is set with value from variable $(APP)_REPOSITORY_URL .PHONY: app-% -app-%: APP_DIR := $(RELATIVE)$(APP) +app-%: APP_DIR := $(or $(WORKDIR), $(RELATIVE)$(APP)) app-%: $(APP_DIR) $(eval APP_REPOSITORY_URL :=) $(eval COMPOSE_FILE :=) $(eval STACK :=) $(eval app := $(subst -$(lastword $(subst -, ,$*)),,$*)) + $(eval app_dir := $(or $(WORKDIR), $(RELATIVE)$(app))) $(eval command := $(lastword $(subst -, ,$*))) $(eval include $(wildcard $(foreach stack_dir,$(STACK_DIR),$(stack_dir)/$(app).mk $(stack_dir)/$(app)/*.mk))) $(foreach var,$(filter $(call UPPERCASE,$(app))_%,$(MAKE_FILE_VARS)), \ @@ -28,7 +29,7 @@ app-%: $(APP_DIR) $(eval $(subst $(call UPPERCASE,$(app))_,APP_,$(var)) := $($(var))) \ ) \ ) - $(if $(wildcard $(RELATIVE)$(app)), \ + $(if $(wildcard $(app_dir)), \ $(if $(filter app-$(command),$(.VARIABLES)), \ $(call app-bootstrap,$(app)) \ $(call app-$(command)) \ @@ -43,7 +44,7 @@ app-%: $(APP_DIR) $(if $(APP_REPOSITORY_URL), \ $(call app-install) \ $(call app-bootstrap) \ - ,$(call WARNING,Unable to find app,$(app),in dir,$(RELATIVE)$(app)) \ + ,$(call WARNING,Unable to find app,$(app),in dir,$(app_dir)) \ ) \ ) diff --git a/make/def.app.mk b/make/def.app.mk index 90a0024..3d43ac0 100644 --- a/make/def.app.mk +++ b/make/def.app.mk @@ -5,7 +5,7 @@ define app-attach $(call INFO,app-attach,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,logs -f $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-attach) @@ -22,7 +22,7 @@ define app-bootstrap $(eval COMPOSE_PROJECT_NAME := $(or $(DOCKER_COMPOSE_PROJECT_NAME),$(subst .,,$(call LOWERCASE,$(USER)-$(APP_NAME)-$(ENV)$(addprefix -,$(subst /,,$(subst -,,$(APP_PATH)))))))) $(eval COMPOSE_SERVICE_NAME := $(or $(DOCKER_COMPOSE_SERVICE_NAME),$(subst _,-,$(COMPOSE_PROJECT_NAME)))) $(eval DOCKER_BUILD_PATH := $(APP_DIR)) - $(call compose-file,$(APP_DIR) $(APP_DIR)/$(or $(APP_DOCKER_DIR),$(DOCKER_DIR)),docker-compose) + $(call compose-file,$(APP_DOCKER_DIR:%=$(APP_DIR)/%)) $(call compose-file,$(MYOS_STACK),$(MYOS_STACK_FILE)) $(eval APP_COMPOSE_FILE ?= $(COMPOSE_FILE)) $(call docker-stack,$(APP)) @@ -34,7 +34,7 @@ define app-build $(call INFO,app-build,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,build $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-build, $(dir $(dockerfile)), $(DOCKER_IMAGE), "" ) @@ -48,7 +48,7 @@ define app-clean $(eval DOCKER_COMPOSE_DOWN_OPTIONS += --rmi all --volumes) $(if $(APP_COMPOSE_FILE), $(call docker-compose,down $(DOCKER_COMPOSE_DOWN_OPTIONS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-rm) @@ -70,7 +70,7 @@ define app-connect $(call INFO,app-connect,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,exec $(or $(SERVICE),$(DOCKER_SERVICE)) $(DOCKER_SHELL)) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-connect) @@ -94,12 +94,25 @@ define app-docker ) endef +# function app-docker-file: eval DOCKER_FILE in dir 1 or APP_DIR +define app-docker-file + $(call INFO,app-docker-file,$(1)) + $(eval dir := $(or $(1),$(APP_DIR))) + $(eval DOCKER_FILE := $(wildcard $(dir)/$(DOCKER_DIR_NAME)/*/Dockerfile $(dir)/$(DOCKER_DIR_NAME)/Dockerfile $(dir)/Dockerfile)) + $(if $(DOCKER_FILE), + , $(eval DOCKER_FILE := $(wildcard $(dir)/*/Dockerfile $(dir)/*/*/Dockerfile $(dir)/*/*/*/Dockerfile)) + ) + $(if $(DOCKER_FILE), + , $(call ERROR,Unable to find a,Dockerfile,in dir,$(dir)) + ) +endef + # function app-down: Call docker rm for each Dockerfile in dir 1 define app-down $(call INFO,app-down,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,down $(DOCKER_COMPOSE_DOWN_OPTIONS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-rm) @@ -113,7 +126,7 @@ define app-exec $(eval args := $(or $(2), $(ARGS))) $(if $(APP_COMPOSE_FILE), $(call docker-compose,exec $(or $(SERVICE),$(DOCKER_SERVICE)) $(args)) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call exec,$(args)) @@ -137,7 +150,7 @@ define app-logs $(call INFO,app-logs,$(1)$(comma) $(2)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,logs $(DOCKER_COMPOSE_LOGS_OPTIONS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-logs) @@ -150,7 +163,7 @@ define app-ps $(call INFO,app-ps,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,ps $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(eval DOCKERS += $(DOCKER_NAME)) @@ -183,7 +196,7 @@ define app-run $(eval DOCKER_RUN_OPTIONS += -it) $(if $(APP_COMPOSE_FILE), $(call docker-compose,run $(DOCKER_RUN_OPTIONS) $(or $(SERVICE),$(DOCKER_SERVICE)) $(args)) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(if $(shell docker images -q $(DOCKER_IMAGE) 2>/dev/null), @@ -208,7 +221,7 @@ define app-start $(call INFO,app-start,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,start $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-start) @@ -221,7 +234,7 @@ define app-stop $(call INFO,app-stop,$(1)$(comma)) $(if $(APP_COMPOSE_FILE), $(call docker-compose,stop $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE))) - , $(call docker-file,$(1)) + , $(call app-docker-file,$(1)) $(foreach dockerfile,$(DOCKER_FILE), $(call app-docker,$(dockerfile)) $(call docker-stop) diff --git a/make/def.docker.mk b/make/def.docker.mk index 6f7e541..58eef3b 100644 --- a/make/def.docker.mk +++ b/make/def.docker.mk @@ -1,4 +1,5 @@ -DOCKER_DIR ?= docker +DOCKER_DIR ?= $(sort $(realpath $(wildcard $(patsubst %,%/docker,$(APP_DIR) $(SHARE_DIR) $(patsubst %,%/myos,$(SHARE_DIR)))))) +DOCKER_DIR_NAME ?= docker DOCKER_ENV_ARGS ?= $(docker_env_args) DOCKER_EXEC_OPTIONS ?= DOCKER_GID ?= $(call gid,docker) @@ -32,11 +33,13 @@ HOST_DOCKER_VOLUME ?= $(HOST_COMPOSE_PROJECT_NAME) HOST_GID ?= $(HOST_UID) HOST_UID ?= 123 HOST_STACK ?= $(filter host,$(firstword $(subst /, ,$(STACK)))) -MYOS_STACK ?= $(foreach stack_dir,$(STACK_DIR),$(MYOS)/$(stack_dir)/myos) +MYOS_STACK ?= $(wildcard $(foreach stack_dir,$(STACK_DIR),$(stack_dir)/myos)) MYOS_STACK_FILE ?= networks volumes RESU_DOCKER_REPOSITORY ?= $(subst -,/,$(USER_COMPOSE_PROJECT_NAME)) +SHARE_DIR ?= . .. ~/.local/share /usr/local/share /usr/share STACK ?= $(APP) -STACK_DIR ?= ../mystack +STACK_DIR ?= $(sort $(realpath $(wildcard $(patsubst %,%/$(STACK_DIR_NAME),$(APP_DIR) $(SHARE_DIR) $(patsubst %,%/myos,$(SHARE_DIR)))))) +STACK_DIR_NAME ?= stack USER_COMPOSE_PROJECT_NAME ?= $(subst .,-,$(RESU)) USER_COMPOSE_SERVICE_NAME ?= $(USER_COMPOSE_PROJECT_NAME) USER_DOCKER_IMAGE ?= $(USER_DOCKER_REPOSITORY):${DOCKER_IMAGE_TAG} @@ -152,6 +155,11 @@ define docker-exec ) endef +# function docker-exited: Print exited dockers matching DOCKER_NAME +define docker-exited +$(shell docker ps -q -f status=exited $(patsubst %,-f name=%,$(or $(1), ^$(DOCKER_NAME)$$, ^$)) 2>/dev/null) +endef + # function docker-logs: Print logs of docker 1 or DOCKER_NAME define docker-logs $(call INFO,docker-logs,$(1)) @@ -162,19 +170,18 @@ define docker-logs ) endef -# function docker-file: eval DOCKER_FILE in dir 1 or APP_DIR -define docker-file - $(call INFO,docker-file,$(1)$(comma)) - $(eval dir := $(or $(1),$(APP_DIR))) - $(eval DOCKER_FILE := $(wildcard $(dir)/$(DOCKER_DIR)/*/Dockerfile $(dir)/$(DOCKER_DIR)/Dockerfile $(dir)/Dockerfile)) - $(if $(DOCKER_FILE), - , $(call ERROR,Unable to find a,Dockerfile,in dir,$(dir)) - ) -endef - -# function docker-exited: Print exited dockers matching DOCKER_NAME -define docker-exited -$(shell docker ps -q -f status=exited $(patsubst %,-f name=%,$(or $(1), ^$(DOCKER_NAME)$$, ^$)) 2>/dev/null) +# function docker-path: eval docker_path for docker 1 in DOCKER_DIR 2 +define docker-path + $(strip + $(call INFO,docker-path,$(1)$(comma) $(2)) + $(eval docker_name := $(or $(subst :,/,$(patsubst %:$(DOCKER_IMAGE_TAG),%,$(1))),myos)) + $(eval docker_dir := $(or $(2),$(DOCKER_DIR))) + $(eval docker_path := $(foreach dir,$(docker_dir),$(patsubst %/Dockerfile,%,$(wildcard $(patsubst %,%/$(docker_name)/Dockerfile,$(dir)))))) + $(call debug,docker_name docker_dir docker_path) + $(if $(docker_path), + $(docker_path) + , $(call ERROR,Unable to find a docker,$(docker_name),in dir,$(docker_dir)) + )) endef # function docker-running: Print running dockers matching DOCKER_NAME @@ -224,3 +231,16 @@ define docker-volume-copy $(RUN) docker volume inspect $(to) >/dev/null 2>&1 || $(RUN) docker volume create $(to) >/dev/null $(RUN) docker run --rm -v $(from):/from -v $(to):/to alpine ash -c "cd /from; cp -a . /to" endef + +# function stack-path: eval stack_path for stack 1 in STACK_DIR 2 +define stack-path + $(strip + $(call INFO,stack-path,$(1)$(comma) $(2)) + $(eval stack_name := $(or $(firstword $(subst :, ,$(patsubst %.yml,%,$(notdir $(1))))),myos)) + $(eval stack_dir := $(or $(2),$(STACK_DIR))) + $(eval stack_path := $(foreach dir,$(stack_dir),$(patsubst %/,%,$(wildcard $(patsubst %,%/$(stack_name)/,$(dir)))))) + $(if $(stack_path), + $(stack_path) + , $(call ERROR,Unable to find a stack,$(stack_name),in dir,$(stack_dir)) + )) +endef diff --git a/make/def.mk b/make/def.mk index eb8be80..1100d5c 100644 --- a/make/def.mk +++ b/make/def.mk @@ -8,8 +8,10 @@ percent ?= % quote ?= ' rbracket ?= ) APP ?= $(if $(wildcard .git),$(notdir $(CURDIR))) +APP_DIR ?= $(or $(filter-out .,$(WORKDIR)), $(CURDIR)) +APP_DOCKER_DIR ?= . $(DOCKER_DIR_NAME) APP_NAME ?= $(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(APP))))) -APP_TYPE ?= $(if $(SUBREPO),subrepo) $(if $(filter .,$(MYOS)),myos) +APP_TYPE ?= $(if $(SUBREPO),subrepo) $(if $(filter .,$(MYOS)),myos) $(if $(wildcard .git),git) APPS ?= $(if $(MONOREPO),$(sort $(patsubst $(MONOREPO_DIR)/%/.git,%,$(wildcard $(MONOREPO_DIR)/*/.git)))) APPS_NAME ?= $(foreach app,$(APPS),$(or $(shell awk -F '=' '$$1 == "APP" {print $$2}' $(or $(wildcard $(MONOREPO_DIR)/$(app)/.env),$(wildcard $(MONOREPO_DIR)/$(app)/.env.$(ENV)),$(MONOREPO_DIR)/$(app)/.env.dist) 2>/dev/null),$(app))) BRANCH ?= $(GIT_BRANCH) @@ -40,7 +42,7 @@ CONFIG_REPOSITORY_PATH ?= $(shell printf '$(CONFIG_REPOSITORY_URI)\n' | CONFIG_REPOSITORY_SCHEME ?= $(shell printf '$(CONFIG_REPOSITORY_URL)\n' |sed 's|://.*||;') CONFIG_REPOSITORY_URI ?= $(shell printf '$(CONFIG_REPOSITORY_URL)\n' |sed 's|.*://||;') CONFIG_REPOSITORY_URL ?= $(call pop,$(APP_UPSTREAM_REPOSITORY))/$(notdir $(CONFIG)) -CONTEXT ?= ENV $(shell awk 'BEGIN {FS="="}; $$1 !~ /^(\#|$$)/ {print $$1}' .env.dist 2>/dev/null) +CONTEXT ?= ENV ENV_PATH $(shell awk 'BEGIN {FS="="}; $$1 !~ /^(\#|$$)/ {print $$1}' .env.dist 2>/dev/null) CONTEXT_DEBUG ?= MAKEFILE_LIST DOCKER_ENV_ARGS ENV_ARGS APPS GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME MAKE_DIR MAKE_SUBDIRS MAKE_CMD_ARGS MAKE_ENV_ARGS UID USER DEBUG ?= DOCKER ?= $(shell type -p docker) @@ -50,11 +52,11 @@ DRONE ?= false DRYRUN ?= false DRYRUN_RECURSIVE ?= false ELAPSED_TIME = $(shell $(call TIME)) -ENV ?= master +ENV ?= local ENV_ARGS ?= $(env_args) ENV_FILE ?= $(wildcard $(if $(filter-out myos,$(MYOS)),$(MONOREPO_DIR)/.env) $(CONFIG)/$(ENV)/$(APP)/.env $(ENV_PATH)/.env) ENV_LIST ?= $(shell ls .git/refs/heads/ 2>/dev/null) -ENV_PATH ?= . +ENV_PATH ?= $(WORKDIR) ENV_RESET ?= false GID ?= $(shell id -g 2>/dev/null) GIDS ?= $(shell id -G 2>/dev/null) @@ -110,6 +112,7 @@ TAG ?= $(GIT_TAG) UID ?= $(shell id -u 2>/dev/null) USER ?= $(shell id -nu 2>/dev/null) VERSION ?= $(GIT_VERSION) +WORKDIR ?= . ifneq ($(DEBUG),) CONTEXT += $(CONTEXT_DEBUG) @@ -151,7 +154,7 @@ DEBUG_FD := 2 # macro debug: print variable content when DEBUG debug = $(if $(DEBUG), \ printf '${COLOR_INFO}$(APP)${COLOR_RESET}[${COLOR_VALUE}$(MAKELEVEL)${COLOR_RESET}]$(if $@, ${COLOR_VALUE}$@${COLOR_RESET}):${COLOR_RESET} ' >&$(DEBUG_FD) \ - $(foreach variable,$(1),&& printf '${COLOR_DEBUG}$(variable):${COLOR_RESET} ${COLOR_VALUE}$($(variable))${COLOR_RESET}, ' >&$(DEBUG_FD)) \ + $(foreach variable,$(1),&& printf '${COLOR_DEBUG}$(variable):${COLOR_RESET} ${COLOR_VALUE}' && $(call PRINTF,$($(variable))) && printf '${COLOR_RESET}, ' >&$(DEBUG_FD)) \ && printf '\n' >&$(DEBUG_FD) \ ) diff --git a/make/include.mk b/make/include.mk index 13af1b1..053f3cd 100644 --- a/make/include.mk +++ b/make/include.mk @@ -14,13 +14,13 @@ MAKE_LATEST := $(MAKE_DIR)/end.mk include $(wildcard $(MAKE_FIRST)) ## it includes $(MAKE_DIR)/$(MAKE_SUBDIRS)/def.mk $(MAKE_DIR)/$(MAKE_SUBDIRS)/def.*.mk include $(foreach subdir,$(MAKE_SUBDIRS),$(wildcard $(MAKE_DIR)/$(subdir)/def.mk $(MAKE_DIR)/$(subdir)/def.*.mk)) -## if not in $(MYOS) nor $(MONOREPO), it includes def.mk def.*.mk */def.mk */def.*.mk -include $(if $(filter-out . myos,$(MYOS)),$(wildcard def.mk def.*.mk */def.mk */def.*.mk)) ## it includes $(MAKE_DIR)/*.mk include $(filter-out $(wildcard $(MAKE_FILE) $(MAKE_FIRST) $(MAKE_LATEST)),$(wildcard $(MAKE_DIR)/*.mk)) ## it includes $(MAKE_DIR)/$(MAKE_SUBDIRS)/*.mk include $(foreach subdir,$(MAKE_SUBDIRS),$(filter-out $(wildcard $(MAKE_DIR)/$(subdir)/def.mk $(MAKE_DIR)/$(subdir)/def.*.mk),$(wildcard $(MAKE_DIR)/$(subdir)/*.mk))) -## if not in $(MYOS) nor $(MONOREPO), it includes *.mk */*.mk, else stack/*.mk if in $(MYOS) -include $(if $(filter-out myos,$(MYOS)),$(if $(filter-out .,$(MYOS)),$(filter-out $(wildcard def.mk def.*.mk */def.mk */def.*.mk),$(wildcard *.mk */*.mk)),$(foreach stack_dir,$(STACK_DIR),$(wildcard $(stack_dir)/*.mk $(stack_dir)/*/*.mk)))) +## if not in $(MYOS) nor $(MONOREPO), it includes def.mk def.*.mk */def.mk */def.*.mk *.mk */*.mk +include $(if $(filter-out . myos,$(MYOS)),$(wildcard def.mk def.*.mk */def.mk */def.*.mk) $(filter-out $(wildcard def.mk def.*.mk */def.mk */def.*.mk stack/*.mk),$(wildcard *.mk */*.mk))) +## it includes $(STACK_DIR)/*.mk $(STACK_DIR)/*/*.mk +include $(foreach stack_dir,$(STACK_DIR),$(wildcard $(stack_dir)/*.mk $(stack_dir)/*/*.mk)) ## it includes $(MAKE_LATEST) include $(wildcard $(MAKE_LATEST)) diff --git a/myos b/myos new file mode 100755 index 0000000..3fc3760 --- /dev/null +++ b/myos @@ -0,0 +1,15 @@ +#!/bin/bash +#shellcheck source=/dev/null disable=SC2046 +set -eu + +# define MYOS path +MYOS="$(dirname "$(readlink "$0" || echo "$0")")" + +# load system config +[ -r /etc/default/myos ] && . /etc/default/myos + +# check myos requirements +[ -f "${MYOS:-.}/Makefile" ] || { echo 'Unable to find myos Makefile' >&2; exit 1; } + +# call myos Makefile +IFS=$'\n'; exec env $(cat /etc/default/myos 2>/dev/null) MYOS=. WORKDIR="${PWD}" make -esC "${MYOS:-.}" "$@" diff --git a/stack/myos/networks.yml b/stack/myos/networks.yml new file mode 100644 index 0000000..de19239 --- /dev/null +++ b/stack/myos/networks.yml @@ -0,0 +1,9 @@ +networks: + default: + name: ${DOCKER_NETWORK_DEFAULT:-_default} + private: + external: true + name: ${DOCKER_NETWORK_PRIVATE:-docker} + public: + external: true + name: ${DOCKER_NETWORK_PUBLIC:-localhost} diff --git a/stack/myos/volumes.backup.local.yml b/stack/myos/volumes.backup.local.yml new file mode 100644 index 0000000..919abe7 --- /dev/null +++ b/stack/myos/volumes.backup.local.yml @@ -0,0 +1,9 @@ +version: '3.6' + +volumes: + backup: + driver: local + driver_opts: + device: /var/lib/backup + o: bind + type: none diff --git a/stack/myos/volumes.dns.local.yml b/stack/myos/volumes.dns.local.yml new file mode 100644 index 0000000..acb34a8 --- /dev/null +++ b/stack/myos/volumes.dns.local.yml @@ -0,0 +1,9 @@ +version: '3.6' + +volumes: + dns: + driver: local + driver_opts: + device: /dns + o: bind + type: none diff --git a/stack/myos/volumes.home.local.yml b/stack/myos/volumes.home.local.yml new file mode 100644 index 0000000..fd0a743 --- /dev/null +++ b/stack/myos/volumes.home.local.yml @@ -0,0 +1,9 @@ +version: '3.6' + +volumes: + home: + driver: local + driver_opts: + device: /home + o: bind + type: none diff --git a/stack/myos/volumes.log.local.yml b/stack/myos/volumes.log.local.yml new file mode 100644 index 0000000..01c9452 --- /dev/null +++ b/stack/myos/volumes.log.local.yml @@ -0,0 +1,9 @@ +version: '3.6' + +volumes: + log: + driver: local + driver_opts: + device: /var/log + o: bind + type: none diff --git a/stack/myos/volumes.www.local.yml b/stack/myos/volumes.www.local.yml new file mode 100644 index 0000000..28340d3 --- /dev/null +++ b/stack/myos/volumes.www.local.yml @@ -0,0 +1,9 @@ +version: '3.6' + +volumes: + www: + driver: local + driver_opts: + device: /var/www + o: bind + type: none