diff --git a/.gitea/workflows/build-publish.yml b/.gitea/workflows/build-publish.yml index cf86631..040c6a7 100644 --- a/.gitea/workflows/build-publish.yml +++ b/.gitea/workflows/build-publish.yml @@ -66,16 +66,20 @@ jobs: "${pkg_dir}/usr/lib" \ "${pkg_dir}/usr/nginx_lua" \ "${pkg_dir}/usr/share/twiy/defaults/nginx" \ - "${pkg_dir}/usr/share/twiy/defaults/hostdata" + "${pkg_dir}/nginx/live" "${pkg_dir}/nginx/conf.d" \ + "${pkg_dir}/nginx/config" "${pkg_dir}/nginx/modsec" \ + "${pkg_dir}/nginx/modules" cp /usr/sbin/nginx "${pkg_dir}/usr/sbin/" - # Pristine configs + default site go into a defaults stash, NOT the - # live /nginx and /hostdata trees. postinst seeds them from here - # without clobbering local edits (writes .new when a target - # already exists). The live files stay untracked by dpkg, so an - # upgrade never overwrites a customised config. - cp -R /nginx/. "${pkg_dir}/usr/share/twiy/defaults/nginx/" || true - cp -R /hostdata/default "${pkg_dir}/usr/share/twiy/defaults/hostdata/" || true + # /nginx ships as an EMPTY, dpkg-owned skeleton (above): the dirs + # are tracked so upgrades from the old layout don't warn about + # "unable to delete old directory /nginx", but NO config file under + # it is tracked. The pristine configs go into a defaults stash; + # postinst places them into /nginx only when missing and never + # overwrites an admin-edited file (drops .new instead). + # /hostdata is intentionally NOT packaged or seeded — postinst only + # ensures the directory exists and never removes it. + cp -R /nginx/. "${pkg_dir}/usr/share/twiy/defaults/nginx/" || true cp "${unit_src}" "${pkg_dir}/etc/systemd/system/nginx.service" cp -R /usr/nginx_lua "${pkg_dir}/usr/" || true @@ -95,10 +99,14 @@ jobs: "${pkg_name}" "${VERSION}" "${ARCH}" "${conflicts}" "${conflicts}" "${pkg_name}" "${TARGET}" \ > "${deb_dir}/control" - # Shared maintainer script: seeds /nginx + /hostdata from the - # defaults stash without overwriting files the admin already has. + # Shared maintainer scripts: + # preinst — backs up /nginx before an upgrade unpacks (so admin + # configs survive the migration off dpkg tracking). + # postinst — restores that backup, then seeds /nginx defaults + # without overwriting any file already there. + cp "${REPO_ROOT}/build/deb/preinst" "${deb_dir}/preinst" cp "${REPO_ROOT}/build/deb/postinst" "${deb_dir}/postinst" - chmod 755 "${deb_dir}/postinst" + chmod 755 "${deb_dir}/preinst" "${deb_dir}/postinst" dpkg-deb --build "${pkg_dir}" } diff --git a/build/deb/postinst b/build/deb/postinst index 4a40bc7..ba44f0d 100755 --- a/build/deb/postinst +++ b/build/deb/postinst @@ -1,20 +1,35 @@ #!/bin/sh # postinst — shared by the twiy and twiy-raweb packages. # -# Pristine configs and the default site ship under /usr/share/twiy/defaults -# (owned/tracked by dpkg), NOT under the live /nginx and /hostdata trees. We -# seed the live trees from the stash here: -# - target missing -> install the packaged copy -# - target present -> leave it untouched; drop our copy as .new -# Because dpkg does not track the live files, an install or upgrade never -# overwrites a config the admin has edited (e.g. you get nginx.conf.new, not a -# clobbered nginx.conf). +# Config files live under /nginx but are NOT tracked by dpkg. The package +# ships an empty /nginx skeleton (so dpkg keeps the dirs across upgrades) plus +# a pristine copy of every config under /usr/share/twiy/defaults/nginx. We +# place configs from that stash here and NEVER overwrite a file that already +# exists — our copy is dropped beside it as .new instead (e.g. +# nginx.conf.new). An upgrade therefore never changes an admin-edited config. +# +# /hostdata is left entirely to the admin: we only make sure the dir exists, +# and we never touch or remove its contents. set -e -# nginx runtime user (idempotent). useradd -r -s /bin/false nginx 2>/dev/null || true +# Existing dirs are left exactly as they are (mkdir -p is a no-op then). +mkdir -p /nginx /hostdata + +# Migration: older releases shipped /nginx/* as dpkg-tracked files, so the +# upgrade unpack deletes them before this script runs. preinst stashed a copy +# first — restore it now, without clobbering anything already present. +if [ -d /var/backups/twiy-nginx ]; then + cp -an /var/backups/twiy-nginx/. /nginx/ 2>/dev/null || true + rm -rf /var/backups/twiy-nginx +fi + +# Seed packaged defaults: +# - target absent -> install it +# - target present, differs -> keep theirs, drop ours as .new +# - target present, same -> do nothing seed_tree() { stash="$1" target="$2" @@ -24,21 +39,16 @@ seed_tree() { dst="$target/$rel" install -d "$(dirname "$dst")" if [ -e "$dst" ]; then - cp -p "$src" "$dst.new" # keep admin's file; offer ours as .new + cmp -s "$src" "$dst" || cp -p "$src" "$dst.new" else cp -p "$src" "$dst" fi done } +seed_tree /usr/share/twiy/defaults/nginx /nginx -seed_tree /usr/share/twiy/defaults/nginx /nginx -seed_tree /usr/share/twiy/defaults/hostdata /hostdata - -# Empty include dirs referenced by nginx.conf (`include conf.d/*;`) that ship -# with no files of their own. install -d /nginx/conf.d /nginx/config install -d -o nginx -g nginx -m 0755 /var/log/nginx - chown -R nginx:nginx /var/log/nginx /nginx 2>/dev/null || true systemctl daemon-reload 2>/dev/null || true diff --git a/build/deb/preinst b/build/deb/preinst new file mode 100755 index 0000000..d5a0c63 --- /dev/null +++ b/build/deb/preinst @@ -0,0 +1,18 @@ +#!/bin/sh +# preinst — shared by the twiy and twiy-raweb packages. +# +# Older releases shipped /nginx as dpkg-tracked files. When upgrading from one +# of those, dpkg deletes the old /nginx/* files during unpack (they are no +# longer part of the package) BEFORE postinst runs. Stash a copy of the live +# config tree first so postinst can restore any admin-edited config and it +# survives the migration. Never touched on a fresh install. + +set -e + +if [ "$1" = upgrade ] && [ -d /nginx ]; then + rm -rf /var/backups/twiy-nginx + mkdir -p /var/backups + cp -a /nginx /var/backups/twiy-nginx +fi + +exit 0