Files
jason f77849c6e9
Build and Push / build (push) Has been cancelled
📝 Migrate /var/lib/srv docker paths to /srv in docs 📝
Update volume mount examples from /var/lib/srv/$USER/docker
to /srv/$USER/docker.
- AI.md README.md : update path references to /srv/$USER/docker

AI.md
README.md
2026-07-10 13:16:37 -04:00

21 KiB

AI.md — Docker Template Update Runbook

Run this whenever upstream templates in casjay-dotfiles/scripts are updated. This file is permanent — do not delete it. It is the maintenance runbook for this repo.


What This Runbook Does

The upstream Docker templates in casjay-dotfiles/scripts change over time. Generated files that are left in place may call removed functions, source removed templates, or reference removed env vars — causing runtime failures. This runbook brings every generated file in the repo up to date.

Files updated:

  • .env.scripts — vars synced to current template (added/removed)
  • Dockerfile / Dockerfile.* — removed ARG lines dropped, new ones added
  • rootfs/usr/local/bin/* — all template-generated bin scripts replaced from temp dir
  • rootfs/usr/local/etc/docker/functions/entrypoint.sh — replaced from temp dir
  • rootfs/usr/local/etc/docker/init.d/*.sh — regenerated; app-specific values restored
  • rootfs/root/docker/setup/00-*.sh through 07-*.sh — replaced from temp dir
  • README.md — rewritten to current standard layout
  • Non-standard rootfs root-level directories — files migrated; stale dirs removed

Tool Reference

gen-dockerfile

Usage: gen-dockerfile [options] [dir] [template] [repo-name] [git-repo-url]

Flags used in this runbook:

Flag Meaning
--update Rewrite .env.scripts (add/drop vars against current template) and update ARG/LABEL lines in every Dockerfile/Dockerfile.*. Does not touch any other file.
--nogit Do not init or commit a git repo — required when running inside an existing repo.
--dir PATH Operate on / write output to PATH instead of $PWD.
--template NAME Template to use (alpine, debian, rhel, scratch, web, xorg). Defaults to alpine if omitted.
--repo NAME Registry repo name (image basename). Defaults to the directory name if omitted.
--org NAME Alias for --user. Sets the registry owner / GitHub org.

Resolution order when a value is not given by a flag: flags → git remote → project dirs → defaults.

gen-script

Usage: gen-script [options] [template] [filename]

Flags and env vars used in this runbook:

Flag / env var Meaning
--dir PATH Write the generated file to PATH instead of $PWD. The output file is PATH/filename.
-n / --name VALUE Sets the service name substituted into the generated file. In the other/start-service template this fills REPLACE_SERVICE_NAME — e.g. --name nginx pre-populates SERVICE_NAME=nginx in the output without a separate sed step.
GEN_SCRIPT_OVERWRITE="Y" Overwrite the output file without prompting. Default is "A" (ask). Must be set when the target file already exists or gen-script will prompt even with GEN_SCRIPT_EDITFILE="N".
GEN_SCRIPT_EDITFILE="N" Suppress the interactive editor prompt after generation. Note: -e/--no sets BOTH this AND GEN_SCRIPT_OVERWRITE="Y" in one flag; setting this env var alone does NOT set OVERWRITE.
other/start-service Template path — words joined by /, matching the @@Template header in the existing script. This arg is positional (first non-flag arg).
filename Output file basename — second positional arg. Combined with --dir to form the full output path.

Other available flags (for reference, not used in this runbook):

Flag Meaning
-k / --keep Do not overwrite an existing file.
--replace Import and create a new header to replace an older one.
-d / --desc Set the description in the generated file header.
-p / --prev Set the header based on an existing file (copies its metadata).

Session Start

git status --porcelain
# If dirty:
git stash push -m "session-start auto-stash"
git pull
# If stashed:
git stash pop
# If stash pop conflicts: report the conflicting files and stop — never auto-resolve

If git pull fails (no remote, offline, diverged): report it and stop.


Variables

name="$(basename "$PWD")"
SCRIPTS_DIR="${CASJAYSDEVDIR:-/usr/local/share/CasjaysDev/scripts}"
TEMPLATE_DIR="$SCRIPTS_DIR/templates"

# Detect repo type: base repos (dockersrc) have Dockerfile.* variant files
if find . -maxdepth 1 -name 'Dockerfile.*' -type f | grep -q -- .; then
  REPO_TYPE="base"
  org="dockersrc"
else
  REPO_TYPE="app"
  org="casjaysdevdocker"
fi

Step 1 — Sync .env.scripts and Dockerfile ARG lines

Run for all repos (both app and base):

gen-dockerfile --update --nogit --dir .

This rewrites .env.scripts against the current dotenv template: adds vars the template now includes, drops vars it no longer includes (e.g. DEFAULT_TEMPLATE_DIR, DEFAULT_FILE_DIR, DEFAULT_DATA_DIR, DEFAULT_CONF_DIR), and preserves all project-specific values (ENV_REGISTRY_REPO, ENV_USE_TEMPLATE, ENV_PACKAGES, etc.).

It also updates ARG lines in every Dockerfile:

  • App repos (REPO_TYPE=app): updates Dockerfile only — ARG IMAGE_NAME=, ARG IMAGE_REPO=, LABEL org.opencontainers.*, and any removed ARG lines.
  • Base repos (REPO_TYPE=base): same changes applied to Dockerfile AND all Dockerfile.* variant files.

All other file content is untouched.

After running, capture the list of removed vars for use in Step 5:

removed_vars="$(git diff .env.scripts | grep -- '^-[A-Z_][A-Z0-9_]*=' | sed 's/^-//' | cut -d= -f1)"
printf 'Removed vars: %s\n' "$removed_vars"

Step 2 — Regenerate all rootfs files from temp dir

Generate a complete fresh tree into a temp dir. Every file produced here is the authoritative replacement for its counterpart in this repo — old copies may reference removed functions or templates and will cause runtime failures if left in place.

tmpdir="$(mktemp -d "/tmp/gen-${name}-XXXXXX")"

if [ "$REPO_TYPE" = "app" ]; then
  template="$(grep -- '^ENV_USE_TEMPLATE=' .env.scripts | cut -d= -f2 | tr -d '"')"
else
  template="$(grep -- 'using the' Dockerfile | head -1 | sed 's/.*using the \([^ ]*\) template.*/\1/')"
fi

gen-dockerfile --dir "$tmpdir" --nogit --template "$template" --repo "$name" --org "$org"

Copy every file the temp dir produced that already exists in this repo — skip nothing:

find "$tmpdir/rootfs" -type f | while read -r src; do
  rel="${src#"$tmpdir/rootfs/"}"
  dest="rootfs/$rel"
  if [ -f "$dest" ]; then
    cp -f "$src" "$dest"
  fi
done

rm -rf "$tmpdir"

This covers: rootfs/usr/local/bin/entrypoint.sh, rootfs/usr/local/bin/pkmgr, rootfs/usr/local/bin/symlink, rootfs/usr/local/bin/copy, rootfs/usr/local/bin/healthcheck, rootfs/usr/local/etc/docker/functions/entrypoint.sh, rootfs/root/docker/setup/00-*.sh through 07-*.sh, and every other file gen-dockerfile generates. The copy condition (-f "$dest") means files not already in this repo are not added — only existing files are updated.


Step 3 — Update app-specific bin scripts

Some repos have extra scripts in rootfs/usr/local/bin/ that gen-dockerfile does not generate — they are app-specific (e.g. check-record, get_dns_record). These were not touched in Step 2.

For each such script, read its @@Template header (line beginning # @@Template):

Has @@Template : shell/sh Update boilerplate in-place from $TEMPLATE_DIR/scripts/shell/sh. Read the template, diff against the existing script, apply only the boilerplate changes (version stamp, shellcheck disable line, set line, trap lines). These are #!/usr/bin/env sh scripts — set -e is correct; -o pipefail is a bashism and must NOT appear. The app-specific logic body is untouched.

Has @@Template : shell/bash (or another template path) Same process, using the matching template file. These are #!/usr/bin/env bash scripts — set -eo pipefail is required.

No @@Template header Hand-written app logic. Do not modify it.

After each edit run the appropriate syntax check:

# sh scripts
sh -n "$script"
# bash scripts
bash -n "$script"

Step 4 — Regenerate init.d/*.sh

init.d/*.sh scripts must be regenerated from the current template — never updated in-place. Old copies may call functions that have since been removed from entrypoint.sh, causing failures. Each script also contains app-specific content that must be preserved; extract it before regenerating and restore it into the new file.

For each *.sh in rootfs/usr/local/etc/docker/init.d/ with @@Template : other/start-service in its header:

1. Read the existing script AND $TEMPLATE_DIR/scripts/other/start-service.

Diff the two. Every line or block present in the existing script but absent from the template is app-specific content. Record all of it. It typically includes:

  • SERVICE_NAME= value
  • EXEC_CMD_BIN= value
  • EXEC_CMD_ARGS= value
  • DATA_DIR=, CONF_DIR=, ETC_DIR=, TMP_DIR=, RUN_DIR=, LOG_DIR= values
  • SERVICE_USER= and SERVICE_GROUP= values
  • Extra export or variable declarations for this service
  • Service-specific env file sourcing (e.g. . "/config/env/nginx.sh")
  • Custom code inside function bodies (pre-start checks, post-start waits, etc.)
  • App-specific functions defined at the top of the file (e.g. __rndc_key, __tsig_key)

2. Regenerate from the template:

init_d_dir="rootfs/usr/local/etc/docker/init.d"
filename="$(basename "$init_script")"
svcname="$(grep -- '^SERVICE_NAME=' "$init_script" | cut -d= -f2 | tr -d '"')"
# GEN_SCRIPT_OVERWRITE="Y"  — overwrite the existing file without prompting (default is "A"/ask)
# GEN_SCRIPT_EDITFILE="N"   — suppress the interactive editor after generation
# --dir                     — write the output file to init_d_dir/filename
# --name                    — pre-fills REPLACE_SERVICE_NAME in the template with the service name,
#                             so SERVICE_NAME= is correct in the generated file without a separate sed step
# other/start-service       — template path (positional arg 1, slash-joined words)
# "$filename"               — output file basename (positional arg 2); combined with --dir for full path
GEN_SCRIPT_OVERWRITE="Y" GEN_SCRIPT_EDITFILE="N" gen-script --dir "$init_d_dir" --name "$svcname" other/start-service "$filename"

The regenerated file is #!/usr/bin/env bash — it must use set -eo pipefail. If gen-script emits set -e only, fix it:

sed -i 's/^set -e$/set -eo pipefail/' "$init_d_dir/$filename"

3. Restore all app-specific content.

SERVICE_NAME is already correct — --name "$svcname" pre-filled it during generation. For all other app-specific KEY=value lines recorded in step 1:

sed -i "s|^EXEC_CMD_BIN=.*|EXEC_CMD_BIN=\"/usr/sbin/named\"|" "$init_d_dir/$filename"
sed -i "s|^EXEC_CMD_ARGS=.*|EXEC_CMD_ARGS=\"-f -u named\"|"   "$init_d_dir/$filename"

For multi-line function bodies and custom functions, use Edit to splice them into the correct location (same function or section they occupied before).

The final script must:

  • Only call functions defined in the current rootfs/usr/local/etc/docker/functions/entrypoint.sh or defined within the script itself
  • Contain all app-specific variable values and custom logic from the old version
  • Pass bash -n "$init_d_dir/$filename" with no errors

Step 5 — Audit for dead variable and function references

After regeneration, app-specific code preserved in Steps 3 and 4 may still reference env vars removed in Step 1 or functions no longer present in the current entrypoint.sh. Find and fix every such reference before committing.

5a — Dead env var references

Use $removed_vars captured in Step 1. For each removed var, search all scripts:

for var in $removed_vars; do
  grep -rn -- "\$$var\|\${$var" rootfs/ 2>/dev/null | grep -v -- '\.git'
done

Fix every hit based on context:

Removed var Replacement
DEFAULT_TEMPLATE_DIR Remove the code that used it. The template-files directory no longer exists. If the code was copying default configs into /config or /etc, the entrypoint now handles that from rootfs/tmp/etc/ at container start.
DEFAULT_FILE_DIR Same as above — remove usages.
DEFAULT_CONF_DIR Replace with ${CONF_DIR:-/etc/$SERVICE_NAME} or the service-specific hardcoded path.
DEFAULT_DATA_DIR Replace with ${DATA_DIR:-/var/$SERVICE_NAME} or the service-specific path.
Any other removed var Determine from context whether to remove the block or substitute the correct current var.

Also search for __copy_templates calls — that function copied from $DEFAULT_TEMPLATE_DIR and is now a no-op since the directory is gone. Remove any call to it in app-specific code:

grep -rn -- '__copy_templates' rootfs/usr/local/etc/docker/init.d/ rootfs/usr/local/bin/

5b — Dead function calls

The fresh rootfs/usr/local/etc/docker/functions/entrypoint.sh from Step 2 is the ground truth for what functions are available at container runtime. Extract all defined names:

defined_fns="$(grep -oE -- '^__[a-zA-Z_]+' \
  rootfs/usr/local/etc/docker/functions/entrypoint.sh | sort -u)"

For each script NOT fully replaced from the temp dir (init.d scripts, custom bin scripts), find calls to functions that are neither in $defined_fns nor defined within the script itself:

for script in rootfs/usr/local/etc/docker/init.d/*.sh rootfs/usr/local/bin/*; do
  [ -f "$script" ] || continue
  local_fns="$(grep -oE -- '^__[a-zA-Z_]+' "$script" | sort -u)"
  grep -oE -- '__[a-zA-Z_]+' "$script" | sort -u | while read -r fn; do
    if ! printf '%s\n' $defined_fns $local_fns | grep -qx -- "$fn"; then
      printf 'DEAD: %s in %s\n' "$fn" "$script"
    fi
  done
done

For each dead call found:

  • Check whether the function was renamed in the current template (e.g. __get_ip__get_ip4 or __get_ip6) and update the call.
  • If the function was removed with no replacement, remove the call and any surrounding block that only makes sense with it.
  • When unsure, check $TEMPLATE_DIR/scripts/ for the current equivalent.

Fix every dead reference before proceeding.


Step 6 — Update README.md

Rewrite README.md to match the current state. Use the existing file as a base; update any stale values (wrong image name, wrong org, wrong ports).

Read SERVICE_PORT from .env.scripts for the port value (app repos). Omit all -p and ports: sections when SERVICE_PORT is empty or unset.

App container layout (casjaysdevdocker/{name})

## 👋 Welcome to {name} 🚀

{name} README


## Install my system scripts

```shell
 sudo bash -c "$(curl -q -LSsf "https://github.com/systemmgr/installer/raw/main/install.sh")"
 sudo systemmgr --config && sudo systemmgr install scripts

Automatic install/update

dockermgr update {name}

Install and run container

dockerHome="/srv/$USER/docker/casjaysdevdocker/{name}/{name}/latest/rootfs"
mkdir -p "/srv/$USER/docker/{name}/rootfs"
git clone "https://github.com/dockermgr/{name}" "$HOME/.local/share/CasjaysDev/dockermgr/{name}"
cp -Rfva "$HOME/.local/share/CasjaysDev/dockermgr/{name}/rootfs/." "$dockerHome/"
docker run -d \
--restart always \
--privileged \
--name casjaysdevdocker-{name}-latest \
--hostname {name} \
-e TZ=${TIMEZONE:-America/New_York} \
-v "$dockerHome/data:/data:z" \
-v "$dockerHome/config:/config:z" \
-p {port}:{port} \
casjaysdevdocker/{name}:latest

via docker-compose

version: "2"
services:
  ProjectName:
    image: casjaysdevdocker/{name}
    container_name: casjaysdevdocker-{name}
    environment:
      - TZ=America/New_York
      - HOSTNAME={name}
    volumes:
      - "/srv/$USER/docker/casjaysdevdocker/{name}/{name}/latest/rootfs/data:/data:z"
      - "/srv/$USER/docker/casjaysdevdocker/{name}/{name}/latest/rootfs/config:/config:z"
    ports:
      - {port}:{port}
    restart: always

Get source files

dockermgr download src casjaysdevdocker/{name}

OR

git clone "https://github.com/casjaysdevdocker/{name}" "$HOME/Projects/github/casjaysdevdocker/{name}"

Build container

cd "$HOME/Projects/github/casjaysdevdocker/{name}"
buildx

Authors

🤖 casjay: Github 🤖 casjaysdevdocker: Github Docker


### Base image layout (`dockersrc/{name}`)

```markdown
## 👋 Welcome to {name} 🚀

{name} README


## Install my system scripts

```shell
 sudo bash -c "$(curl -q -LSsf "https://github.com/systemmgr/installer/raw/main/install.sh")"
 sudo systemmgr --config && sudo systemmgr install scripts

Automatic install/update

dockermgr update os {name}

Install and run container

mkdir -p "/srv/root/docker/casjaysdev/{name}/latest"
git clone "https://github.com/dockermgr/{name}" "$HOME/.local/share/CasjaysDev/dockermgr/{name}"
cp -Rfva "$HOME/.local/share/CasjaysDev/dockermgr/{name}/rootfs/." "/srv/root/docker/casjaysdev/{name}/latest/"
docker run -d \
--restart always \
--privileged \
--name casjaysdev-{name}-latest \
--hostname {name} \
-e TZ=${TIMEZONE:-America/New_York} \
-v "/srv/root/docker/casjaysdev/{name}/latest/data:/data:z" \
-v "/srv/root/docker/casjaysdev/{name}/latest/config:/config:z" \
casjaysdev/{name}:latest

via docker-compose

version: "2"
services:
  ProjectName:
    image: casjaysdev/{name}
    container_name: casjaysdev-{name}-latest
    environment:
      - TZ=America/New_York
      - HOSTNAME={name}
    volumes:
      - "/srv/root/docker/casjaysdev/{name}/latest/data:/data:z"
      - "/srv/root/docker/casjaysdev/{name}/latest/config:/config:z"
    restart: always

Get source files

dockermgr download src os {name}

Build container

git clone "https://github.com/dockersrc/{name}" "$HOME/Projects/github/dockersrc/{name}"
cd "$HOME/Projects/github/dockersrc/{name}" && buildx all

Authors

🤖 casjay: Github 🤖 casjaysdev: Github Docker


---

## Step 7 — Clean up non-standard rootfs directories

The only valid directories at the `rootfs/` root level are `root/`, `tmp/`, and `usr/`. Any other
directory is a leftover from old patterns and must be cleaned up.

Find non-standard dirs:

```bash
find rootfs -maxdepth 1 -mindepth 1 -type d | grep -vE -- 'rootfs/(root|tmp|usr)$'

If the directory contains only .gitkeep (empty placeholder): remove it directly.

rm -rf "rootfs/{dir}"

If the directory contains actual files: migrate them to the correct location first, then remove.

Migration path map:

Old rootfs path Correct rootfs path
rootfs/etc/{path} rootfs/tmp/etc/{path}
rootfs/config/{path} rootfs/tmp/etc/{path}
rootfs/data/{path} rootfs/tmp/var/{path}
rootfs/var/{path} rootfs/tmp/var/{path}
rootfs/opt/{path} rootfs/tmp/opt/{path}
rootfs/share/{path} rootfs/usr/local/share/{path}

Migration pattern (adapt src_dir and dest_dir per the table above):

src_dir="rootfs/etc"
dest_dir="rootfs/tmp/etc"
find "$src_dir" -type f | while read -r src; do
  rel="${src#"$src_dir/"}"
  dest="$dest_dir/$rel"
  mkdir -p "$(dirname -- "$dest")"
  mv "$src" "$dest"
done
rm -rf "$src_dir"

Also remove rootfs/usr/local/share/template-files/ if it exists — the DEFAULT_TEMPLATE_DIR, DEFAULT_FILE_DIR, DEFAULT_DATA_DIR, and DEFAULT_CONF_DIR variables were removed from the template and this directory is no longer used at build time:

rm -rf rootfs/usr/local/share/template-files

Step 8 — Verify

Run syntax checks on every script that was touched. Fix all failures before committing.

# bin scripts (check shebang to pick the right interpreter)
for f in rootfs/usr/local/bin/*; do
  [ -f "$f" ] || continue
  case "$(head -1 "$f")" in
    *bash*) bash -n "$f" && printf 'OK: %s\n' "$f" || printf 'FAIL: %s\n' "$f" ;;
    *sh*)   sh -n "$f"   && printf 'OK: %s\n' "$f" || printf 'FAIL: %s\n' "$f" ;;
  esac
done

# entrypoint.sh and setup scripts are bash
bash -n rootfs/usr/local/etc/docker/functions/entrypoint.sh

for f in rootfs/root/docker/setup/0*.sh; do
  [ -f "$f" ] || continue
  bash -n "$f" && printf 'OK: %s\n' "$f" || printf 'FAIL: %s\n' "$f"
done

# init.d scripts are bash
for f in rootfs/usr/local/etc/docker/init.d/*.sh; do
  [ -f "$f" ] || continue
  bash -n "$f" && printf 'OK: %s\n' "$f" || printf 'FAIL: %s\n' "$f"
done

Step 9 — Commit

Check what actually changed:

git status --porcelain
git diff --stat

Write .git/COMMIT_MESS listing only the files that actually changed per git diff --stat. Subject line ≤64 chars. Body as - path: change bullets. Include only what changed in this run.

Example template (adjust bullets to match actual diff):

✨ Update to latest docker template revision ✨

- .env.scripts: synced vars to current template
- Dockerfile: removed stale ARG lines, updated IMAGE_NAME/REPO/LABEL
- rootfs/usr/local/bin/*: regenerated from current template via gen-dockerfile
- rootfs/usr/local/etc/docker/functions/entrypoint.sh: replaced from template
- rootfs/usr/local/etc/docker/init.d/*.sh: regenerated; app-specific values restored
- rootfs/root/docker/setup/: regenerated from current template
- README.md: updated to current standard layout
- rootfs/{old-dirs}: files migrated to rootfs/tmp/; stale directories removed

Then commit:

gitcommit --dir "$(git rev-parse --show-toplevel)" all