Files
go/AI.md
T
jason 711972c88b
Build and Push / build (push) Has been cancelled
🔧 Complete Docker template update runbook 🔧
Finish syncing generated Docker template files against upstream templates:
remove dead template-files ARGs from the Dockerfile, sync bin-script
boilerplate to the current shell/sh template, migrate the non-standard
rootfs/etc dir to rootfs/tmp/etc, fix a gitignore bug that silently dropped
rootfs/tmp/, correct the README license, and document the go-tools
toolchain-stage exception in the runbook.
- AI.md: document that language toolchain build stages (e.g. `FROM golang:alpine AS go-tools`)
are intentionally exempt from the PULL_URL/GEN_DOCKERFILE_APP_DIR org rule
- Dockerfile: remove dead DEFAULT_FILE_DIR/DEFAULT_DATA_DIR/DEFAULT_CONF_DIR/DEFAULT_TEMPLATE_DIR
ARGs and their mkdir usage — the template-files directory no longer exists
- README.md: fix stale license claim (MIT) to match the actual LICENSE.md (WTFPL)
- .gitignore: anchor the tmp/ pattern to /tmp/ so it stops silently swallowing
the required rootfs/tmp/ directory
- rootfs/etc/profile.d/go.sh -> rootfs/tmp/etc/profile.d/go.sh: migrate the
non-standard rootfs/etc dir to the tmp/etc convention (rootfs/root, rootfs/tmp,
rootfs/usr are the only valid rootfs root dirs; 03-files.sh copies tmp/etc/* at build time)
- rootfs/usr/local/bin/copy, rootfs/usr/local/bin/symlink: sync APPNAME
assignment to the current template style and add the missing trap '' EXIT line
- rootfs/usr/local/bin/healthcheck: sync APPNAME assignment and add the
missing trap '' EXIT line

AI.md
Dockerfile
.gitignore
README.md
rootfs/etc/profile.d/go.sh
rootfs/tmp/
rootfs/usr/local/bin/copy
rootfs/usr/local/bin/healthcheck
rootfs/usr/local/bin/symlink
2026-07-10 19:58:22 -04:00

28 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

Template System Reference

This section documents the current state of templates/dockerfiles/ and key bin/gen-dockerfile variables. Keep it in sync whenever upstream templates change.

Template inventory

Template Final stage Init / PID 1 Base OS
alpine.template scratch.template tini Alpine
debian.template scratch.template tini Debian
ubuntu.template scratch.template tini Ubuntu
rhel.template scratch.template tini AlmaLinux
archlinux.template scratch.template tini Arch Linux (see multi-arch note)
web.template systemd.template /sbin/init Debian
xorg.template systemd.template /sbin/init Debian

Final-stage templates

scratch.template — used by all non-GUI templates.

  • ENTRYPOINT [ "tini", "-p", "SIGTERM","--", "/usr/local/bin/entrypoint.sh" ]
  • STOPSIGNAL SIGRTMIN+3

systemd.template — used by web and xorg (systemd runs as PID 1; tini is redundant).

  • ENTRYPOINT [ "/sbin/init" ]
  • STOPSIGNAL SIGRTMIN+3
  • No tini_provider stage, no COPY --from=tini_provider line.

Both templates are identical apart from ENTRYPOINT. OCI labels, ENV HOSTNAME, and VOLUME/EXPOSE/HEALTHCHECK are the same in both.

OCI label standard

Both scratch.template and systemd.template emit these labels (no others):

LABEL maintainer="${GEN_DOCKERFILE_MAINTAINER}"
LABEL org.opencontainers.image.vendor="${GEN_DOCKERFILE_VENDOR:-CasjaysDev}"
LABEL org.opencontainers.image.authors="${GEN_DOCKERFILE_AUTHOR:-CasjaysDev}"
LABEL org.opencontainers.image.licenses="${LICENSE}"
LABEL org.opencontainers.image.title="${IMAGE_NAME}"
LABEL org.opencontainers.image.description="Containerized version of ${IMAGE_NAME}"
LABEL org.opencontainers.image.created="${BUILD_DATE}"
LABEL org.opencontainers.image.version="${BUILD_VERSION}"
LABEL org.opencontainers.image.revision="${GIT_COMMIT}"
LABEL org.opencontainers.image.url="${GEN_DOCKERFILE_HUB_REPO}"
LABEL org.opencontainers.image.source="${GEN_DOCKERFILE_GIT_REPO}"
LABEL org.opencontainers.image.documentation="${GEN_DOCKERFILE_GIT_REPO}"
LABEL org.opencontainers.image.vcs-type="Git"
LABEL com.github.containers.toolbox="false"

Shell-expanded values (no \) are evaluated at template-render time by gen-dockerfile. Dollar-escaped values (\${...}) become literal Docker ARG/ENV references in the generated Dockerfile.

Removed labels (do not re-add):

  • org.opencontainers.image.base.name — belongs on the base image, not the app image
  • org.opencontainers.image.schema-version — non-spec; redundant with version
  • Any duplicate authors or source entries

HOSTNAME convention

All templates set ENV HOSTNAME="casjaysdevdocker-${IMAGE_NAME}" in every stage that declares it. The prefix is always casjaysdevdocker-, never casjaysdev-.

GEN_DOCKERFILE_APP_DIR and pull URL logic

GEN_DOCKERFILE_APP_DIR is auto-detected in bin/gen-dockerfile from the parent directory of $PWD (i.e. the GitHub org the project lives in):

GEN_DOCKERFILE_APP_DIR="${GEN_DOCKERFILE_APP_DIR:-$(basename -- "$(dirname -- "$PWD")")}"

It controls which base images GEN_DOCKER_SPECIFY_IMAGE_SOURCE_* default to:

if [ "${GEN_DOCKERFILE_APP_DIR}" = "casjaysdevdocker" ]; then
  # Pull from pre-built casjaysdev/* base images on Docker Hub
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_RHEL="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_RHEL:-casjaysdev/almalinux}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ALPINE="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ALPINE:-casjaysdev/alpine}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_DEBIAN="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_DEBIAN:-casjaysdev/debian}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_UBUNTU="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_UBUNTU:-casjaysdev/ubuntu}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ARCHLINUX="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ARCHLINUX:-casjaysdev/archlinux}"
else
  # Pull from upstream official images (dockersrc/* builds its own base images)
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_RHEL="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_RHEL:-almalinux}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ALPINE="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ALPINE:-alpine}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_DEBIAN="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_DEBIAN:-debian}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_UBUNTU="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_UBUNTU:-ubuntu}"
  GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ARCHLINUX="${GEN_DOCKER_SPECIFY_IMAGE_SOURCE_ARCHLINUX:-archlinux}"
fi

Summary:

  • casjaysdevdocker/* repos → FROM casjaysdev/<distro>:latest (already multi-arch)
  • dockersrc/* and all other orgs → FROM <distro>:latest (upstream official images)

The Docker Hub push org (casjaysdev) is unchanged regardless of GEN_DOCKERFILE_APP_DIR.

Arch Linux multi-arch (archlinux.template)

When GEN_DOCKERFILE_APP_DIR != "casjaysdevdocker" (i.e. building a base image in dockersrc/archlinux), the template emits a three-stage FROM to support both linux/amd64 and linux/arm64:

ARG TARGETARCH
ARG TARGETPLATFORM
FROM --platform=${TARGETPLATFORM} archlinux:latest AS base-amd64
FROM --platform=${TARGETPLATFORM} lopsided/archlinux-arm64v8:latest AS base-arm64
FROM base-${TARGETARCH} AS build

When GEN_DOCKERFILE_APP_DIR = "casjaysdevdocker", casjaysdev/archlinux is a multi-arch manifest so a single FROM ${PULL_URL}:${DISTRO_VERSION} AS build suffices.

web.template packages

The web template installs the following systemd + noVNC stack in the build stage:

systemd systemd-sysv dbus dbus-x11 procps
tigervnc-standalone-server novnc openbox xdotool

Default ports: SERVICE_PORT="5800", EXPOSE_PORTS="5800 5900".

xorg.template packages

The xorg template installs the following systemd + Xorg stack in the build stage:

systemd systemd-sysv dbus dbus-x11 procps
xserver-xorg x11-xserver-utils xinit

debian.template / ubuntu.template — RUN continuation fix

The first RUN block must have ; \ after the echo line so export DEBIAN_FRONTEND=noninteractive executes before apt-get:

RUN set -e; \
  echo "Updating the system"; \
  export DEBIAN_FRONTEND=noninteractive; \
  apt-get update && apt-get upgrade -yy && apt-get dist-upgrade -yy

Without the ; \ the export is a no-op and apt-get may prompt interactively.


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

# GEN_DOCKERFILE_APP_DIR is auto-detected by gen-dockerfile from the parent of $PWD.
# For repos under casjaysdevdocker/: pull base from casjaysdev/* (pre-built multi-arch).
# For repos under dockersrc/ or any other org: pull from upstream official images.
# Override by setting GEN_DOCKERFILE_APP_DIR in the environment before calling gen-dockerfile.

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="/var/lib/srv/$USER/docker/casjaysdevdocker/{name}/{name}/latest/rootfs"
mkdir -p "/var/lib/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:
      - "/var/lib/srv/$USER/docker/casjaysdevdocker/{name}/{name}/latest/rootfs/data:/data:z"
      - "/var/lib/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 "/var/lib/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/." "/var/lib/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 "/var/lib/srv/root/docker/casjaysdev/{name}/latest/data:/data:z" \
-v "/var/lib/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:
      - "/var/lib/srv/root/docker/casjaysdev/{name}/latest/data:/data:z"
      - "/var/lib/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