Files
development-scripts-3darch/references/shared-modules.md
T
oleg-lukasonokandClaude Opus 5 31a5408a6c Add development-scripts agent skill
Ports generic-scripts-module-v1 into a routed skill: module anatomy, shared
loggers/base modules, Taskfile integration, workflows, and pattern decisions.

Fixes a latent defect in the loggers example that silenced all logging in the
module scaffold: loggers/lib/--index-api.sh now sources --env-vars-reader.sh,
and the reader preserves already-exported values so caller overrides still win.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 22:18:55 +03:00

5.3 KiB

Shared Modules — loggers and base

The two foundation modules every other module depends on, and the bootstrap rule that decides whether logging works at all.

Working implementations are in assets/loggers/ and assets/base/.


1. loggers — support-only library

No api/ commands, no Taskfile.yml. It exists to be sourced.

.scripts/loggers/
  lib/
    --env-vars-reader.sh        # level flags + padding strings
    --index-api.sh              # sources the reader, then every logger function
    --index.sh
    -debug.sh  -error.sh  -info.sh  -trace.sh  -warn.sh
    -empty-line.sh  -enable-trailing-new-line.sh  -waiting-dot.sh

Consume it from any module:

. ./.scripts/loggers/lib/--index-api.sh

Available functions:

_loggers_info  "${FUNCTION_NAME}" "message"
_loggers_debug "${FUNCTION_NAME}" "message"
_loggers_warn  "${FUNCTION_NAME}" "message"
_loggers_error "${FUNCTION_NAME}" "message"
_loggers_trace "${FUNCTION_NAME}" "message"
_loggers_emptyLine

Levels are controlled by environment flags, defaulting to everything except trace:

LOGGER_IS_ENABLED_ERROR=true
LOGGER_IS_ENABLED_INFO=true
LOGGER_IS_ENABLED_WARN=true
LOGGER_IS_ENABLED_DEBUG=true
LOGGER_IS_ENABLED_TRACE=false

Export any of them before invoking a command to change verbosity for that run:

LOGGER_IS_ENABLED_DEBUG=false ./.scripts/<module>/api/<command>.sh

Rules:

  1. Do not add api/ wrappers to loggers without a real user-facing requirement.
  2. Keep logger helpers small and side-effect-light.
  3. Log to stderr — stdout stays clean so command output remains pipeable.
  4. Dependent modules source the logger API directly.

2. base — shared foundation

Has public api/ wrappers but no Taskfile.yml. Provides local environment handling and local-storage helpers over .env files.

.scripts/base/
  api/
    local-storage-ensure-existance-file-dot-env.sh
    local-storage-read-lines-from-file-dot-env.sh
    local-storage-reset-file-dot-env.sh
    local-storage-save-env-variable-2-file-dot-env.sh
    local-storage-view-file-dot-env.sh
  lib/
    --env-vars-reader.sh  --env-vars-validator.sh
    --index-api.sh  --index.sh
    -export-env-variable.sh
    -export-env-variables-from-file-dot-env.sh
    -export-env-variables-from-file.sh
    -get-env-variable-key-from-line.sh
    -get-env-variable-value-from-line.sh
    -log-env-vars.sh
    -prompt-for-env-var.sh
    local-storage/
      --index-api.sh
      -ensure-existance-file.sh          -ensure-existance-file-dot-env.sh
      -read-lines-from-file.sh           -read-lines-from-file-dot-env.sh
      -reset-file.sh                     -reset-file-dot-env.sh
      -save-env-variable-2-file.sh       -save-env-variable-2-file-dot-env.sh
      -view-file.sh                      -view-file-dot-env.sh

Which entrypoint to source — this matters

# From another module, when you only need base's shared functions:
. ./.scripts/base/lib/--index-api.sh

# Only from base's own api/ wrappers:
. ./.scripts/base/lib/--index.sh

base/lib/--index.sh runs base's reader and validator, and reads .env and environment-specific files as a side effect. Sourcing it from a library function pulls that whole environment into an unrelated command — values appear that nobody set, and the failure is hard to trace because nothing in the calling module mentions .env.

--index-api.sh defines functions and nothing else, which is why it is the correct dependency edge between modules.

Rules:

  1. Prefer base/lib/--index-api.sh from other modules.
  2. Nested lib packages such as lib/local-storage/ carry their own --index-api.sh and are sourced from the parent's.
  3. Never log secret values — redact tokens, API keys, passwords, kube config, credentials.

3. The logging bootstrap rule

A logger function whose level flag is unset prints nothing and returns 0.

Every _loggers_* function is guarded by its level flag:

if [ "${LOGGER_IS_ENABLED_INFO}" = true ]; then ... fi

An unset flag is not true, so the call is a silent no-op with a success exit code. Nothing fails, nothing warns — the command simply produces no output, which reads like a bug in the command rather than in its bootstrap.

Those flags are exported by loggers/lib/--env-vars-reader.sh. For logging to work, that reader must have run before any logger call.

In the shipped assets/loggers/, --index-api.sh sources the reader itself, so any module that sources the logger API gets working logging with no extra step. The reader uses ${VAR:-default} throughout, so it is safe to source repeatedly and a caller's override still wins.

If you are adapting an existing loggers module rather than using the shipped one, check this first. A --index-api.sh that sources only the logger function files, without the reader, silences every module that depends on it — while bash -n passes and every command exits 0.

Quick check in any repository:

grep -n 'env-vars-reader' .scripts/loggers/lib/--index-api.sh \
  || echo "WARNING: loggers API does not bootstrap its level flags — logging may be silent"

Direct test:

bash -c '. ./.scripts/loggers/lib/--index-api.sh; _loggers_info "check" "visible?"'

No output means the flags were never exported.