diff --git a/AI.md b/AI.md index ddbf359..25b3a35 100644 --- a/AI.md +++ b/AI.md @@ -1,35 +1,168 @@ -# AI.md — Docker Template Update Runbook +# CasjaysDev Docker Base Image Specification (dockersrc) -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. +**Name**: {name} + +**About this file:** This is the complete, authoritative specification for a CasjaysDev +Docker **base image** repository (`dockersrc/{name}`). It is a master template — copied +into a base image repo as that repo's `AI.md`. It is **permanent** — never delete it from +a repo that carries it. + +**Note:** `{name}` in this file is a reference token, not setup-time replacement text. Its +value is always the repo directory basename (`basename "$PWD"`). + +**Maintenance procedure:** The bootstrap/update runbook (regenerating files after upstream +template changes, creating new repos) is NOT in this file — it lives in the +`dockersrc-bootstrap` agent. This file defines the standards that procedure enforces. --- -## What This Runbook Does +# PART INDEX -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 +| PART | Title | +|------|-------| +| 0 | Critical rules | +| 1 | Repository model & structure | +| 2 | Template system reference | +| 3 | Tooling — gen-dockerfile & gen-script | +| 4 | `.env.scripts` reference | +| 5 | Runtime system — setup scripts, entrypoint, init.d | +| 6 | README.md standard layout | +| 7 | CI/CD workflows | +| 8 | Verification & commit | --- -## Template System Reference +# PART 0: CRITICAL RULES -This section documents the current state of `templates/dockerfiles/` and key -`bin/gen-dockerfile` variables. Keep it in sync whenever upstream templates change. +## Org mapping -### Template inventory +| System | Org | Example | +|--------|-----|---------| +| GitHub (source) | `dockersrc` | `https://github.com/dockersrc/{name}` | +| Docker Hub (push) | `casjaysdev` | `casjaysdev/{name}` | + +`dockersrc` repos are **OS bases and toolchains** (alpine, debian, ubuntu, almalinux, +archlinux, web, xorg, go, rust, android). Application images live in the separate +`casjaysdevdocker` org and pull FROM these images — see the apps specification +(`CASJAYSDEVDOCKER.md`). + +The Docker Hub push org is always `casjaysdev` regardless of where the repo is checked out. + +## Non-negotiable rules + +1. **`AI.md` is permanent** — never delete it from the repo. +2. **Generated files are owned by the template system** — never hand-tune content that + `gen-dockerfile` regenerates (see PART 1 ownership table); fix the upstream + `gen-dockerfile` template instead, then regenerate. +3. **Hand-crafted files are owned by the repo** — `gen-dockerfile` must never overwrite + app-specific init.d scripts, custom bin scripts, or a `05-custom.sh` with real content. +4. **Removed OCI labels stay removed** (PART 2) — never re-add `base.name`, + `schema-version`, or duplicate `authors`/`source` entries. +5. **`image.url` is a browsable page** — `https://hub.docker.com/r/casjaysdev/{name}`. + `docker.io` is only a registry pull host; it is never a label URL. +6. **`image.source` and `image.documentation` are the GitHub repo** — + `https://github.com/dockersrc/{name}`. +7. **One variant, one file set** — every published version tag has its own + `Dockerfile.{ver}`, `.env.scripts.{ver}`, and `.gitea/workflows/build.{ver}.yml`. +8. **Only `root/`, `tmp/`, and `usr/` may exist at `rootfs/` top level** (PART 1). +9. **Maintenance runs through the `dockersrc-bootstrap` agent** — do not improvise the + update procedure from memory. + +--- + +# PART 1: REPOSITORY MODEL & STRUCTURE + +## What a base image repo is + +A `dockersrc/{name}` repo builds one image family from upstream official distro images +(never from `casjaysdev/*` — base repos ARE the `casjaysdev/*` images). OS repos publish +one variant per supported release; toolchain repos (go, rust, android) publish `latest` +plus whatever the toolchain needs. + +## Standard tree + +``` +{name}/ +├── AI.md # This specification (permanent) +├── Dockerfile # [generated] latest/default variant +├── Dockerfile.{ver} # [generated] one per version variant (OS repos) +├── .dockerignore # [generated] +├── .env.scripts # [generated] build config for the default variant +├── .env.scripts.{ver} # [generated] one per version variant +├── .gitattributes # [generated] +├── .gitea/workflows/ +│ ├── build.yml # [generated] gen-dockerfile actions — default variant +│ └── build.{ver}.yml # [generated] one per version variant +├── .gitignore # [generated] +├── LICENSE.md # License (WTFPL) +├── README.md # [generated] standard layout (PART 6) +└── rootfs/ # Container filesystem overlay + ├── root/docker/setup/ # [generated*] build-time setup scripts 00–07 + ├── tmp/ # staged files installed at build time (optional) + └── usr/local/ + ├── bin/ # [generated*] entrypoint.sh, pkmgr, symlink, copy, + │ # healthcheck + [hand-crafted] repo-specific scripts + └── etc/docker/ + ├── env/ # [hand-crafted] build/runtime env fragments (optional) + ├── functions/ + │ └── entrypoint.sh # [generated] entrypoint function library + └── init.d/ # [hand-crafted] runtime init scripts (one per service) +``` + +`[generated]` — safe to regenerate; local edits will be lost. +`[generated*]` — regenerated from the template, EXCEPT files carrying repo-specific +content (`05-custom.sh` with a real body, extra bin scripts) — those follow the +hand-crafted rules in PART 5. +`[hand-crafted]` — never overwritten by the template system. + +## rootfs top-level policy + +The only valid directories at the `rootfs/` root are `root/`, `tmp/`, and `usr/`. +Anything else is a leftover from old patterns. Migration 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}` | + +`rootfs/usr/local/share/template-files/` is retired — the `DEFAULT_TEMPLATE_DIR`, +`DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, and `DEFAULT_CONF_DIR` variables were removed +from the template system; the entrypoint installs staged files from `rootfs/tmp/etc/` +at container start instead. + +## Variant detection + +A repo is a **base** repo when `Dockerfile.*` variant files exist: + +```bash +if find . -maxdepth 1 -name 'Dockerfile.*' -type f | grep -q -- .; then + REPO_TYPE="base" +else + REPO_TYPE="app" +fi +``` + +--- + +# PART 2: TEMPLATE SYSTEM REFERENCE + +Templates ship with `gen-dockerfile`, installed at +`/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/` +(`$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout). To inspect what the current +templates produce, generate a fresh reference tree in a temp dir: + +```bash +gen-dockerfile /tmp/gen-dockerfile/{org}/{repo} {distro} +``` + +See `gen-dockerfile --help` for supported distros/types. Keep this PART in sync whenever +the templates change. + +## Template inventory | Template | Final stage | Init / PID 1 | Base OS | |----------|-------------|--------------|---------| @@ -37,27 +170,27 @@ This section documents the current state of `templates/dockerfiles/` and key | `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) | +| `archlinux.template` | `scratch.template` | tini | Arch Linux (multi-arch note below) | | `web.template` | `systemd.template` | `/sbin/init` | Debian | | `xorg.template` | `systemd.template` | `/sbin/init` | Debian | -### Final-stage templates +## Final-stage templates -`scratch.template` — used by all non-GUI templates. +`scratch.template` — 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). +`systemd.template` — `web` and `xorg` (systemd is 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 +Both are identical apart from `ENTRYPOINT`. OCI labels, `ENV HOSTNAME`, and `VOLUME`/`EXPOSE`/`HEALTHCHECK` are the same in both. -### OCI label standard +## OCI label standard -Both `scratch.template` and `systemd.template` emit these labels (no others): +Both final-stage templates emit these labels (no others): ``` LABEL maintainer="${GEN_DOCKERFILE_MAINTAINER}" @@ -76,60 +209,49 @@ 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`. +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 +Resolved values for a `dockersrc` repo pushing to Docker Hub: + +| Label | Value | +|-------|-------| +| `url` | `https://hub.docker.com/r/casjaysdev/{name}` — browsable Hub page; `gen-dockerfile` derives it from the registry host (`docker.io` → `hub.docker.com/r/`) | +| `source` | `https://github.com/dockersrc/{name}` | +| `documentation` | `https://github.com/dockersrc/{name}` | + +Removed labels (never re-add): +- `org.opencontainers.image.base.name` — belongs on the base image, not this image - `org.opencontainers.image.schema-version` — non-spec; redundant with `version` - Any duplicate `authors` or `source` entries -### HOSTNAME convention +## HOSTNAME convention -All templates set `ENV HOSTNAME="casjaysdevdocker-${IMAGE_NAME}"` in every stage -that declares it. The prefix is always `casjaysdevdocker-`, never `casjaysdev-`. +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` 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` is auto-detected by `gen-dockerfile` from the parent directory +of `$PWD` (the org the checkout lives in): ```bash GEN_DOCKERFILE_APP_DIR="${GEN_DOCKERFILE_APP_DIR:-$(basename -- "$(dirname -- "$PWD")")}" ``` -It controls which base images `GEN_DOCKER_SPECIFY_IMAGE_SOURCE_*` default to: +It selects the `GEN_DOCKER_SPECIFY_IMAGE_SOURCE_*` defaults: -```bash -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/:latest` (already multi-arch) +- `casjaysdevdocker/*` repos → `FROM casjaysdev/:latest` (pre-built, multi-arch) - `dockersrc/*` and all other orgs → `FROM :latest` (upstream official images) -The Docker Hub push org (`casjaysdev`) is unchanged regardless of `GEN_DOCKERFILE_APP_DIR`. +Base repos always pull upstream — a base image never builds FROM itself. Override by +exporting `GEN_DOCKERFILE_APP_DIR` before calling `gen-dockerfile`. -### Arch Linux multi-arch (`archlinux.template`) +## 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`: +When building the base image (`GEN_DOCKERFILE_APP_DIR != "casjaysdevdocker"`), the +template emits a three-stage FROM for `linux/amd64` + `linux/arm64`: ```dockerfile ARG TARGETARCH @@ -139,12 +261,12 @@ FROM --platform=${TARGETPLATFORM} lopsided/archlinux-arm64v8:latest AS base-arm6 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. +App repos pull `casjaysdev/archlinux`, a multi-arch manifest, so a single +`FROM ${PULL_URL}:${DISTRO_VERSION} AS build` suffices there. -### `web.template` packages +## `web.template` packages -The `web` template installs the following systemd + noVNC stack in the build stage: +systemd + noVNC stack in the build stage: ``` systemd systemd-sysv dbus dbus-x11 procps @@ -153,16 +275,16 @@ tigervnc-standalone-server novnc openbox xdotool Default ports: `SERVICE_PORT="5800"`, `EXPOSE_PORTS="5800 5900"`. -### `xorg.template` packages +## `xorg.template` packages -The `xorg` template installs the following systemd + Xorg stack in the build stage: +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 +## `debian.template` / `ubuntu.template` — RUN continuation The first `RUN` block must have `; \` after the `echo` line so `export DEBIAN_FRONTEND=noninteractive` executes before `apt-get`: @@ -176,433 +298,254 @@ RUN set -e; \ Without the `; \` the export is a no-op and `apt-get` may prompt interactively. +## Template resolution order + +1. `$GEN_DOCKERFILE_CONFIG_DIR/templates/.template` (user override) +2. `/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/.template` + (installed; `$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout) + +`template_options.source` is sourced after `__set_variables`, allowing template-specific +variable overrides. + --- -## Tool Reference +# PART 3: TOOLING — gen-dockerfile & gen-script -### `gen-dockerfile` +## `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. | +| `--update` | Rewrite `.env.scripts` (add/drop vars against the current template) and update ARG/LABEL lines in every `Dockerfile`/`Dockerfile.*`. Touches no other file. | +| `--nogit` | Do not init or commit a git repo — required 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. | +| `--template NAME` | Template to use (`alpine`, `debian`, `ubuntu`, `rhel`, `archlinux`, `scratch`, `web`, `xorg`). Defaults to `alpine`. | +| `--repo NAME` | Registry repo name (image basename). Defaults to the directory name. | +| `--org NAME` | Registry owner / GitHub org (`--user` is an alias). Prefix `git:` or `reg:` to scope to one system; bare value sets both. | +| `--registry URL` | Registry provider URL (e.g. `https://docker.io`). | +| `--tag VERSION` | Image version tag (default `latest`). | +| `--add-tags TAGS` | Comma-separated additional tags (`USE_DATE` = auto date tag). | +| `--distro-name IMG` | Base image pull URL (overrides `ENV_PULL_URL`). | +| `--distro-version T` | Base image tag (overrides `ENV_DISTRO_TAG`). | +| `--startup FILE` | Generate an init.d service script at `rootfs/usr/local/etc/docker/init.d/FILE` via `gen-script other/start-service`. | +| `--dockerfile` | Regenerate the Dockerfile only. | +| `--force` | Overwrite existing files without prompting. | -Resolution order when a value is not given by a flag: flags → git remote → project dirs → defaults. +Resolution order when a value is not given by a flag: flags → git remote → project dirs → +defaults. -### `gen-script` +Special subcommand — `gen-dockerfile actions` writes `.gitea/workflows/build.yml` +(`build.{ver}.yml` for versioned tags) from the existing `Dockerfile` (PART 7). + +Base repos update `Dockerfile` AND every `Dockerfile.*` variant on `--update`; the +matching `.env.scripts.{ver}` files carry per-variant values. + +## `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. | +| `--dir PATH` | Write the generated file to `PATH/filename`. | +| `-n` / `--name VALUE` | Service name substituted into the template — fills `REPLACE_SERVICE_NAME` in `other/start-service`, pre-populating `SERVICE_NAME=` without a sed step. | +| `GEN_SCRIPT_OVERWRITE="Y"` | Overwrite the output without prompting (default `"A"` = ask). Required when the target exists, even with `GEN_SCRIPT_EDITFILE="N"`. | +| `GEN_SCRIPT_EDITFILE="N"` | Suppress the interactive editor after generation. `-e`/`--no` sets BOTH this AND `GEN_SCRIPT_OVERWRITE="Y"`; the env var alone does not. | +| `other/start-service` | Template path — positional arg 1, slash-joined words, matching the `@@Template` header. | +| `filename` | Output basename — positional arg 2, combined with `--dir`. | -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). | +Other flags: `-k`/`--keep` (never overwrite), `--replace` (new header replaces old), +`-d`/`--desc` (header description), `-p`/`--prev` (copy header metadata from a file). --- -## Session Start +# PART 4: `.env.scripts` REFERENCE -```bash -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. - ---- +Generated at the repo root; sourced by `gen-dockerfile` and by CI at build time. Base +repos carry one per variant (`.env.scripts` + `.env.scripts.{ver}`). It is a pure +`KEY="value"` file — no logic. ## Variables -```bash -name="$(basename "$PWD")" -SCRIPTS_DIR="${CASJAYSDEVDIR:-/usr/local/share/CasjaysDev/scripts}" -TEMPLATE_DIR="$SCRIPTS_DIR/templates" +| Variable | Purpose | +|----------|---------| +| `ENV_DOCKERFILE` | Dockerfile the variant builds (`Dockerfile` or `Dockerfile.{ver}`) | +| `ENV_REGISTRY_REPO` | Image name in the registry (`{name}`) | +| `ENV_REGISTRY_ORG` | Registry namespace — `casjaysdev` for base repos | +| `ENV_REGISTRY_URL` | Registry base URL (`https://docker.io`) — pull/push host, never a label URL | +| `ENV_REGISTRY_PUSH` | Full push path `org/repo` (`casjaysdev/{name}`) | +| `ENV_ADD_IMAGE_PUSH` | Extra push destinations | +| `ENV_GIT_REPO_URL` | Full Git repo URL — `https://github.com/dockersrc/{name}`; feeds the `source`/`documentation` labels, so a wrong value here regresses labels on regeneration | +| `ENV_USE_TEMPLATE` | Template name (`alpine`, `debian`, …) | +| `ENV_PULL_URL` | Base image to pull FROM | +| `ENV_DISTRO_TAG` | Tag for the pull image | +| `ENV_IMAGE_TAG` | Default image tag (`latest`, or the variant version) | +| `ENV_ADD_TAGS` | Additional comma-separated tags; `USE_DATE` auto-generates a date tag | +| `ENV_PACKAGES` | Space-separated package list | +| `ENV_VENDOR` / `ENV_AUTHOR` / `ENV_MAINTAINER` | Label metadata | +| `SERVICE_PORT` | Primary exposed port (empty for pure base images) | +| `EXPOSE_PORTS` | Additional exposed ports | +| `LANG_VERSION` | Language runtime version (toolchain repos) | +| `PHP_VERSION` / `NODE_VERSION` / `NODE_MANAGER` | Runtime versions (`system` default) | +| `WWW_ROOT_DIR` | Web root (`/usr/local/share/httpd/default`) | +| `DOCKER_ENTYPOINT_PORTS_WEB` / `DOCKER_ENTYPOINT_PORTS_SRV` | Ports passed to the entrypoint | +| `DOCKER_ENTYPOINT_HEALTH_APPS` / `DOCKER_ENTYPOINT_HEALTH_ENDPOINTS` | Healthcheck targets | -# 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" +## Legacy variable auto-migration + +`gen-dockerfile` calls `__migrate_env_script` on every run, renaming old variables: + +| Old name | Current name | +|----------|-------------| +| `ENV_IMAGE_NAME` | `ENV_REGISTRY_REPO` | +| `ENV_IMAGE_PUSH` | `ENV_REGISTRY_PUSH` | +| `ENV_HUB_BASE` | `ENV_REGISTRY_URL` | +| `ENV_ORG_NAME` | `ENV_REGISTRY_ORG` | + +Never use the old names in new files. Retired variables that must not reappear anywhere: +`DEFAULT_TEMPLATE_DIR`, `DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, `DEFAULT_CONF_DIR`. + +--- + +# PART 5: RUNTIME SYSTEM — SETUP SCRIPTS, ENTRYPOINT, INIT.D + +## Build-time setup scripts (`rootfs/root/docker/setup/`) + +Run in order inside the build stage: + +| Script | Role | +|--------|------| +| `00-init.sh` | Initialize base directory structure and environment | +| `01-system.sh` | Repos, locales, timezone, system settings | +| `02-packages.sh` | App-specific packages, package managers, language runtimes | +| `03-files.sh` | Install staged files (`rootfs/tmp/etc/*` → `/etc/*`), permissions, symlinks | +| `04-users.sh` | Create service users/groups | +| `05-custom.sh` | Application/toolchain-specific install logic | +| `06-post.sh` | Post-install configuration | +| `07-cleanup.sh` | Remove build deps, caches, temp files | + +**`05-custom.sh` ownership:** the upstream template ships an empty stub. Any repo whose +`05-custom.sh` has a real body (e.g. a toolchain repo's install logic) owns that content — +it exists only in the repo's git history, never in the template. On regeneration, keep the +existing body and pull forward only boilerplate (version-stamp header, `set` line, +shellcheck-disable line). The same rule applies to any other `0*.sh` found to contain real +logic beyond the stub. + +## Entrypoint flow + +``` +tini → /usr/local/bin/entrypoint.sh +├─ Load /usr/local/etc/docker/functions/entrypoint.sh +├─ Source env: /root/env.sh, /usr/local/etc/docker/env/*.sh, /config/env/*.sh +├─ Seed /config and /data on first run +├─ __start_init_scripts — source every init.d/*.sh in sort order +├─ Handle `healthcheck` command +└─ Execute main application +``` + +`rootfs/usr/local/bin/` generated set: `entrypoint.sh`, `pkmgr`, `symlink`, `copy`, +`healthcheck`. `pkmgr` wraps the native package manager (`apk`, `apt-get`, `dnf`, +`pacman`) behind `pkmgr update|install|remove|clean`. + +## App-specific bin scripts + +Extra scripts in `rootfs/usr/local/bin/` that `gen-dockerfile` does not generate are +repo-owned. Their `@@Template` header governs maintenance: + +- `@@Template : shell/sh` — boilerplate synced from `$TEMPLATE_DIR/scripts/shell/sh`; + `#!/usr/bin/env sh`, `set -e` only (`pipefail` is a bashism — must NOT appear) +- `@@Template : shell/bash` — synced from `shell/bash`; `set -eo pipefail` required +- No `@@Template` header — hand-written; never modified by tooling + +## init.d scripts — critical rules + +**Each service gets its own numbered init.d script. Never merge or remove services.** +`__start_init_scripts` sources every `*.sh` in sort order — multi-process repos have one +script per daemon (`01-named.sh`, `02-nginx.sh`, `03-php-fpm.sh`, …). + +init.d scripts are **regenerated, never patched in place** — old copies may call functions +removed from the current `functions/entrypoint.sh`. Generate fresh via +`gen-script other/start-service` (or `gen-dockerfile --startup`), then restore the +app-specific values. They are `#!/usr/bin/env bash` with `set -eo pipefail`. + +Required variables in every init.d script: + +```bash +SERVICE_NAME="myapp" +EXEC_CMD_BIN='myapp' +EXEC_CMD_ARGS='' +EXEC_PRE_SCRIPT='' +SERVICE_USES_PID='' +IS_WEB_SERVER="no" +IS_DATABASE_SERVICE="no" +USES_DATABASE_SERVICE="no" +DATABASE_SERVICE_TYPE="sqlite" +RUNAS_USER="root" +``` + +Directory variables: + +```bash +DATA_DIR="/data/$SERVICE_NAME" +CONF_DIR="/config/$SERVICE_NAME" +ETC_DIR="/etc/$SERVICE_NAME" +LOG_DIR="/data/logs/$SERVICE_NAME" +TMP_DIR="/tmp/$SERVICE_NAME" +RUN_DIR="/run/$SERVICE_NAME" +ROOT_FILE_PREFIX="/config/secure/auth/root" +USER_FILE_PREFIX="/config/secure/auth/user" +``` + +## Hook functions + +The `start-service` template generates all outer hooks fully implemented — customise via +the matching `*_local()` stub, which each outer hook calls automatically if defined: + +| Outer hook (do not redefine) | Customise via | +|------------------------------|---------------| +| `__run_precopy` | `__run_precopy_local` | +| `__execute_prerun` | `__execute_prerun_local` | +| `__run_pre_execute_checks` | `__run_pre_execute_checks_local` | +| `__update_conf_files` | `__update_conf_files_local` | +| `__pre_execute` | `__pre_execute_local` | +| `__post_execute` | `__post_execute_local` | +| `__pre_message` | `__pre_message_local` | +| `__update_ssl_conf` | `__update_ssl_conf_local` | +| `__create_service_env` | — | +| `__run_start_script` | — | +| `__run_secure_function` | — | + +## PID sentinel guard + +Every init.d script must guard on exactly this sentinel — leading dot, no underscores in +the filename portion; any other form silently skips the guard: + +```bash +if [ ! -f "/run/.start_init_scripts.pid" ]; then + echo "__start_init_scripts function hasn't been Initialized" >&2 + SERVICE_IS_RUNNING="no" + __script_exit 1 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. ``` +## Volumes + +- `/config` — persistent configuration +- `/data` — persistent application data + --- -## Step 1 — Sync `.env.scripts` and Dockerfile ARG lines +# PART 6: README.md STANDARD LAYOUT -Run for **all** repos (both app and base): +Base image layout (`dockersrc/{name}` → `casjaysdev/{name}`). Substitute `{name}`; +this repo family has no `-p` port mappings unless `SERVICE_PORT` is set in +`.env.scripts` — omit all port sections when it is empty. -```bash -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: - -```bash -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. - -```bash -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: - -```bash -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: - -```bash -# 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:** - -```bash -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: - -```bash -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: - -```bash -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: - -```bash -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: - -```bash -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: - -```bash -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: - -```bash -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}`) - -```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 - -```shell -dockermgr update {name} -``` - -## Install and run container - -```shell -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 - -```yaml -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 - -```shell -dockermgr download src casjaysdevdocker/{name} -``` - -OR - -```shell -git clone "https://github.com/casjaysdevdocker/{name}" "$HOME/Projects/github/casjaysdevdocker/{name}" -``` - -## Build container - -```shell -cd "$HOME/Projects/github/casjaysdevdocker/{name}" -buildx -``` - -## Authors - -🤖 casjay: [Github](https://github.com/casjay) 🤖 -⛵ casjaysdevdocker: [Github](https://github.com/casjaysdevdocker) [Docker](https://hub.docker.com/u/casjaysdevdocker) ⛵ -``` - -### Base image layout (`dockersrc/{name}`) - -```markdown +````markdown ## 👋 Welcome to {name} 🚀 {name} README @@ -672,124 +615,84 @@ cd "$HOME/Projects/github/dockersrc/{name}" && buildx all 🤖 casjay: [Github](https://github.com/casjay) 🤖 ⛵ casjaysdev: [Github](https://github.com/dockersrc) [Docker](https://hub.docker.com/u/casjaysdev) ⛵ -``` +```` --- -## Step 7 — Clean up non-standard rootfs directories +# PART 7: CI/CD WORKFLOWS -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. +## Generated workflow (`gen-dockerfile actions`) -Find non-standard dirs: +`gen-dockerfile actions` writes `.gitea/workflows/build.yml` from the current +`Dockerfile`; versioned variants get `build.{ver}.yml` (named `Build and Push {ver}`, no +schedule trigger, fixed version tag only). All actions are SHA-pinned — never tag-pinned. -```bash -find rootfs -maxdepth 1 -mindepth 1 -type d | grep -vE -- 'rootfs/(root|tmp|usr)$' -``` +- **Triggers:** `push` to `main`, monthly schedule, `workflow_dispatch` +- **Registry strategy:** always logs in to the Gitea registry via the auto-provided + `GITEA_TOKEN`; conditionally logs in to Docker Hub when `vars.DOCKER_USERNAME` is set + (`vars.DOCKER_USERNAME` + `secrets.DOCKER_PASSWORD`; `vars.DOCKER_REGISTRY` overrides + the registry, `vars.DOCKER_ORG` the namespace) +- **Platforms:** `linux/amd64,linux/arm64` +- **build-args:** only `BUILD_DATE`, `GIT_COMMIT`, `BUILD_VERSION` +- **Tags pushed:** date tag (`yymm`) + `latest` (or the fixed variant version) to both + registries +- **Annotations:** mirror the OCI label standard (PART 2), with `url`/`source`/ + `documentation` set to the workflow's repository URL -**If the directory contains only `.gitkeep` (empty placeholder):** remove it directly. +## Legacy workflow (`docker.yaml`) -```bash -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): - -```bash -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: - -```bash -rm -rf rootfs/usr/local/share/template-files -``` +A hand-crafted `.gitea/workflows/docker.yaml` may exist in older repos — reference copy in +the org-level `.github` repo. **Never overwrite it, and never use it as a template for new +work** — it uses tag-pinned actions and retired secret names. All new/updated workflows +come from `gen-dockerfile actions`. --- -## Step 8 — Verify +# PART 8: VERIFICATION & COMMIT -Run syntax checks on every script that was touched. Fix all failures before committing. +## Syntax gates + +Every touched script must pass before commit: ```bash -# 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" ;; + *bash*) bash -n "$f" || exit 1 ;; + *sh*) sh -n "$f" || exit 1 ;; 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 +for f in rootfs/root/docker/setup/0*.sh 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 - -# 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" + bash -n "$f" || exit 1 done ``` ---- +## Dead-reference gates -## Step 9 — Commit +After any regeneration: -Check what actually changed: +1. No script references an env var removed from `.env.scripts` (diff-driven check). +2. No script calls a function absent from both the current + `functions/entrypoint.sh` and the script itself. +3. No `__copy_templates` calls remain (retired with `DEFAULT_TEMPLATE_DIR`). + +## Commit ```bash 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: +Write `.git/COMMIT_MESS` from the actual diff — subject ≤64 chars, body as +`- path: change` bullets covering every changed file. Then: ```bash gitcommit --dir "$(git rev-parse --show-toplevel)" all ``` + +`git commit` / `git push` directly are forbidden. Never commit with a failing syntax +gate.