diff --git a/legacy/helper/posix-shell/log.sh b/legacy/helper/posix-shell/log.sh index ef1fece..461f5ba 100644 --- a/legacy/helper/posix-shell/log.sh +++ b/legacy/helper/posix-shell/log.sh @@ -1,7 +1,123 @@ #!/bin/dash +# Hectic shell logger +# +# Usage: +# # Including +# . +# +# # Required +# colors.sh +# +# # In your script (recommended: do NOT export HECTIC_NAMESPACE) +# HECTIC_NAMESPACE="my-script" # optional, defaults to basename "$0" +# # # Then use: +# log info 'starting up' +# log debug "value=${val}" +# log error "failed: ${WHITE}${reason}${NC} red text again" +# +# # Note: +# When you use NC to reset terminal colors inside log output, +# it resets back to the log level’s color instead of the terminal default. + +: "${HLOG_NAMESPACE:="$(basename "$0")"}" +: "${HLOG_LEVEL:=trace}" # e.g. "info;ns1=debug;ns2=trace" + +validate_log_level_spec() { + spec=$HLOG_LEVEL + + levels="trace debug info notice warn error" + + ok_level() { + for l in $levels; do + [ "$l" = "$1" ] && return 0 + done + return 1 + } + + oldIFS=$IFS + IFS=';' + # shellcheck disable=SC2086 + set -- $spec + IFS=$oldIFS + + for tok; do + case $tok in + *=*) + ns=${tok%%=*} + lvl=${tok#*=} + [ -n "$ns" ] || return 1 + ok_level "$lvl" || return 1 + ;; + *) + ok_level "$tok" || return 1 + ;; + esac + done + + return 0 +} + +validate_log_level_spec || { printf "%b%b\n" "${BBLACK}${HLOG_NAMESPACE}> " "${color}invalid HLOG_LEVEL syntax${NC}" "$@" >&2; exit 1; } + +log_level_num() { + case $1 in + trace) printf %s 0 ;; + debug) printf %s 1 ;; + info) printf %s 2 ;; + notice) printf %s 3 ;; + warn) printf %s 4 ;; + error) printf %s 5 ;; + *) printf %s 2 ;; # default info + esac +} + + +log_effective_level() { + spec=$HLOG_LEVEL + ns=$HLOG_NAMESPACE + + default_level= + ns_level= + + oldIFS=$IFS + IFS=';' + # shellcheck disable=SC2086 + set -- $spec + IFS=$oldIFS + + for tok; do + case $tok in + *=*) + name=${tok%%=*} + lvl=${tok#*=} + [ "$name" = "$ns" ] && ns_level=$lvl + ;; + *) + [ -z "$default_level" ] && default_level=$tok + ;; + esac + done + + printf '%s\n' "${ns_level:-${default_level:-info}}" +} + +log_allowed() { + msg_level="${1:?}" + eff_level="$(log_effective_level)" + + msg_n="$(log_level_num "$msg_level")" + eff_n="$(log_level_num "$eff_level")" + + [ "$msg_n" -ge "$eff_n" ] +} + +# log(level, text...) log() { + delimetr=${DELIMETR:-' '}; level="${1:?}"; shift + log_allowed "$level" || return 0 + case "$level" in trace) color="$MAGENTA" ;; debug) color="$BLUE" ;; @@ -15,7 +131,7 @@ log() { # shellcheck disable=SC1003 - fmt="$(printf "%s" "${1:?}" | sed 's/\\033\[0m/''\'"$color"'/g')" + fmt="$(printf "%s$delimetr" "$@" | sed 's/\\033\[0m/''\'"$color"'/g')" shift - printf "%b\n" "$color$fmt$NC" "$@" >&2 + printf "%b%b\n" "${BBLACK}${HLOG_NAMESPACE}> " "$color$fmt$NC" >&2 } diff --git a/package/male-amusements/default.nix b/package/male-amusements/default.nix new file mode 100644 index 0000000..3f9fca5 --- /dev/null +++ b/package/male-amusements/default.nix @@ -0,0 +1,18 @@ +{ symlinkJoin, dash, hectic }: let + shell = "${dash}/bin/dash"; + bashOptions = [ + "errexit" + "nounset" + ]; + + show-megumin = hectic.writeShellApplication { + inherit shell bashOptions; + name = "show-megumin"; + runtimeInputs = [ ]; + text = builtins.readFile ./show-megumin.sh; + }; +in +symlinkJoin { + name = "sentinèlla"; + paths = [ show-megumin ]; +} diff --git a/package/male-amusements/show-megumin.sh b/package/male-amusements/show-megumin.sh new file mode 100644 index 0000000..e69de29 diff --git a/package/migrator/migrator.sh b/package/migrator/migrator.sh index 203dca8..a28ac23 100644 --- a/package/migrator/migrator.sh +++ b/package/migrator/migrator.sh @@ -9,10 +9,18 @@ MIGRATION_DIR="${MIGRATION_DIR:-migration}" quote() { printf "'%s'" "$(printf %s "$1" | sed "s/'/'\\\\''/g")"; } REMAINING_ARS= +# cat filename | sha256sum() +# sha256sum(filename) +sha256sum() { + local file + file="${1:-'-'}" + cksum --algorithm=sha256 --untagged "$file" | awk '{printf $1}' +} + while [ $# -gt 0 ]; do log debug "$1" case $1 in - migrate|create|fetch|list) + migrate|create|fetch|list|init) [ "${SUBCOMMAND+x}" ] && { printf 'ambiguous subcommand, decide %s or %s\n' "$SUBCOMMAND" "$1"; exit 1; } SUBCOMMAND=$1 shift @@ -25,10 +33,6 @@ while [ $# -gt 0 ]; do INHERITS_LIST="${INHERITS_LIST:+$INHERITS_LIST\"}$2" shift 2 ;; - --init-dry-run) - INIT_DRY_RUN=1 - shift - ;; --*|-*) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; # unknown global -> pass through *) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; esac @@ -36,22 +40,79 @@ done INHERITS_LIST="$(printf '%s' "$INHERITS_LIST" | sed -E 's/"/,/g; s/([^,]+)/"\1"/g')" +# shellcheck disable=SC2120 init() { + while [ $# -gt 0 ]; do + case $1 in + --dry-run) + INIT_DRY_RUN=1 + shift + ;; + --db-url|-u) + DB_URL="$2" + shift 2 + ;; + --*|-*) + printf 'init argument %s does not exists' "$1" + exit 1 + ;; + *) + printf 'init command %s does not exists' "$1" + exit 1 + ;; + esac + done + + error_handler_no_db_url + + [ "${INIT_DRY_RUN+x}" ] && { printf '%s\n' "$(init_sql)"; exit; } + + psql_args="$(form_psql_args)" + + # shellcheck disable=SC2086 + if ! printf '%s' "$(init_sql)" | psql $psql_args; then + log error "Migration failed: ${WHITE}$fs_migration${NC}" + return 3 + fi +} + +# error_handler_no_db_url() +error_handler_no_db_url() { + [ "${DB_URL+x}" ] || { log error "no ${WHITE}DB_URL${NC} or ${WHITE}--db-url${NC} specified"; exit 1; } +} + +init_sql() { log debug "inherits: ${WHITE}${INHERITS_LIST}${NC}" local create_table create_table="$(printf '%s\n' \ + "BEGIN;" \ + "CREATE DOMAIN hectic.migration_name AS TEXT CHECK (VALUE ~ '^[0-9]{15}-.*$');" \ + '' \ + "CREATE DOMAIN hectic.sha256 AS CHAR(64) CHECK (VALUE ~ '^[0-9a-f]{64}$');" \ + '' \ + 'CREATE FUNCTION hectic.sha256_lower() RETURNS trigger AS $$' \ + 'BEGIN' \ + ' NEW.hash = lower(NEW.hash);' \ + ' RETURN NEW;' \ + 'END;' \ + '$$ LANGUAGE plpgsql;' \ + '' \ + 'CREATE TRIGGER hectic.t_sha256_lower' \ + 'BEFORE INSERT OR UPDATE ON hectic.migration' \ + 'FOR EACH ROW EXECUTE FUNCTION sha256_lower();' \ + '' \ 'CREATE SCHEMA IF NOT EXISTS hectic;' \ 'CREATE TABLE IF NOT EXISTS hectic.migration (' \ - ' id SERIAL PRIMARY KEY,' \ - ' name TEXT UNIQUE NOT NULL,'\ - ' applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()' \ - ')')" + ' id SERIAL PRIMARY KEY,' \ + ' name hectic.migration_name UNIQUE NOT NULL,'\ + ' hash hectic.sha256 UNIQUE NOT NULL,'\ + ' applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()' \ + ')' \ + 'COMMIT;')" printf '%s INHERITS(%s)' "$create_table" "$INHERITS_LIST" } -[ "${INIT_DRY_RUN+x}" ] && { printf '%s\n' "$(init)"; exit; } - [ "${SUBCOMMAND+x}" ] || { log error "no subcomand specified"; exit 1; } help() { @@ -142,21 +203,20 @@ migrate() { ;; --*|-*) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; # unknown global -> pass through *) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; - #--*|-*) - # printf 'migrate argument %s does not exists' "$1" - # exit 1 - #;; - #*) - # printf 'migrate subcommand %s does not exists' "$1" - # exit 1 - #;; esac done + error_handler_no_db_url + + [ -n "$FORCE" ] && { + log error "migrate --force not implemented" + exit 1 + } + init fs_migrations=$( - find "$MIGRATION_DIR" -maxdepth 1 -type f -name '*.sql' \ + find "$MIGRATION_DIR" -maxdepth 1 -type d -regex '^.*/[0-9]{15}-.*$' \ | sort \ | xargs -n1 basename ) @@ -186,17 +246,25 @@ migrate() { eval "set -- $REMAINING_ARS" #target_migration="$("migrate_$MIGRATE_SUBCOMMAND" "$@")" + +} + +form_psql_args() { + psql_args="-d $DB_URL -v ON_ERROR_STOP=1" + for var in $VARIABLE_LIST; do + psql_args="$psql_args -v $var" + done +} + +migrate_inner() { printf '%s\n' "$fs_migrations" | while IFS= read -r fs_migration; do # skip already applied migrations printf '%s' "$db_migrations" | grep -qxF "$fs_migration" && continue - psql_args="-d $DB_URL" - for var in $VARIABLE_LIST; do - psql_args="$psql_args -v $var" - done + psql_args="$(form_psql_args)" escaped_name=$(printf "%s" "$fs_migration" | sed "s/'/''/g") - escaped_path=$(printf "%s/%s" "$MIGRATION_DIR" "$fs_migration" | sed "s/'/''/g") + escaped_path=$(printf "%s/%s/up.sql" "$MIGRATION_DIR" "$fs_migration" | sed "s/'/''/g") # shellcheck disable=SC2086 if ! psql $psql_args <