Regalamiunsorriso/sync
2026-04-07 18:02:17 +02:00
..
promote-sync.sh First commit 2026-04-07 18:02:17 +02:00
README.md First commit 2026-04-07 18:02:17 +02:00

Server Sync Plan

Goal

Create a server-side Bash script that promotes files uploaded with WinSCP from a staging area in the user home directory into the live site tree, while keeping the live file ownership and permissions intact.

Constraints

  • The remote server is FreeBSD.
  • Direct SSH-based sync from the local machine is unreliable because of the server banner and client compatibility issues.
  • Uploads can only be done with WinSCP into the user-owned area.
  • Final deployment into the live site requires elevation.
  • The first version should cover these paths only:
    • www
    • rus
    • WEB-INF/classes
  • RUS uppercase should be ignored.

Deployment Model

Use a two-step flow:

  1. Upload changed files with WinSCP into a staging tree under /home/marco/regalamiunsorriso.
  2. Log into the server and run a Bash script with sudo to promote the staged files into /home/sites/regalamiunsorriso.

This avoids client-side SSH problems and keeps all privileged filesystem changes on the server.

Proposed Directory Layout

Staging root:

/home/marco/regalamiunsorriso/

Live root:

/home/sites/regalamiunsorriso/

Recommended staging layout:

/home/marco/regalamiunsorriso/
  incoming/
    www/
    rus/
    WEB-INF/
      classes/
  logs/
  bin/

Recommended script location:

/home/marco/regalamiunsorriso/bin/promote-sync.sh

First-Version Scope

The first script should be intentionally conservative.

It should:

  • Process only files staged under incoming/www, incoming/rus, and incoming/WEB-INF/classes.
  • Refuse to touch paths outside the live root.
  • Ignore RUS uppercase completely.
  • Update existing live files in place.
  • Create parent directories only when they already exist in the allowed live trees or when their ownership and mode policy is explicit.
  • Write a deployment log with timestamp, source path, destination path, and result.

It should not, in version 1:

  • Delete live files.
  • Try to sync the whole tree automatically.
  • Touch WEB-INF/lib.
  • Invent permissions for brand new files unless a clear policy is defined.

Why Existing Files First

Keeping the original permissions intact is easy to guarantee for files that already exist in the live tree, because the script can read the current owner, group, and mode from the destination and reapply them after the content update.

New files are a separate problem. For them, there is no original metadata to preserve. The safe approach is:

  • either block them in version 1,
  • or allow them only through an explicit manifest with declared owner, group, and mode.

Permission Strategy

For each staged file that maps to an existing live file:

  1. Read current live metadata.
  2. Copy the staged content to a temporary file under the destination directory.
  3. Apply the original owner, group, and mode to the temporary file.
  4. Atomically replace the live file.
  5. Optionally verify that metadata still matches the original values.

On FreeBSD, metadata can be read with stat -f.

Example values to capture:

owner=$(stat -f '%Su' "$dest")
group=$(stat -f '%Sg' "$dest")
mode=$(stat -f '%Lp' "$dest")

Then reapply with:

chown "$owner:$group" "$tmp"
chmod "$mode" "$tmp"
mv -f "$tmp" "$dest"

This approach is safer than blindly using cp -p, because the script explicitly preserves the metadata already present on the live file.

Safety Rules

The script should fail closed.

Required safeguards:

  • set -euo pipefail

  • hardcoded allowed roots only

  • path normalization before use

  • reject symlinks in the staging area for version 1

  • reject any path containing ..

  • reject destinations that do not resolve under /home/sites/regalamiunsorriso

  • dry-run mode

  • per-file logging

  • summary at the end with counts for updated, skipped, and failed files

  • Exclude these symlinked subdirectories and internal www trees: mypics, mypics-archivio, mypics2, www/_img/, www/_news/, and www/_tmp/.

Suggested Script Interface

./promote-sync.sh --dry-run
./promote-sync.sh --apply
./promote-sync.sh --apply --only www
./promote-sync.sh --apply --only rus
./promote-sync.sh --apply --only WEB-INF/classes

Suggested options:

  • --dry-run: print actions without changing files
  • --apply: execute the promotion
  • --only <scope>: limit to one allowed subtree
  • --log <file>: write to a specific log file
  • --allow-new <manifest>: optional future extension for approved new files
  1. Set fixed values for STAGE_ROOT and LIVE_ROOT.
  2. Build the list of allowed staging subtrees.
  3. Walk staged files only.
  4. For each staged file:
    • map it to the live destination
    • validate the destination is inside the live root
    • verify the destination exists for version 1
    • capture current owner, group, and mode from the live file
    • copy staged content into a temporary file in the destination directory
    • apply captured metadata
    • replace the live file atomically
    • log success or failure
  5. Print a final summary.
  6. Leave staged files in place unless an explicit cleanup option is requested.

Logging Format

Plain text is enough for version 1.

Example:

2026-03-28T21:10:03Z APPLY www/index.jsp -> /home/sites/regalamiunsorriso/www/index.jsp OK
2026-03-28T21:10:05Z APPLY rus/admin/menu/edit.jsp -> /home/sites/regalamiunsorriso/rus/admin/menu/edit.jsp OK
2026-03-28T21:10:06Z SKIP WEB-INF/classes/new.properties reason=destination-missing

Operational Workflow

  1. Prepare changed files locally.
  2. Upload only those files with WinSCP into the mirrored staging tree under /home/marco/regalamiunsorriso/incoming.
  3. Log into the server.
  4. Run a dry run first.
  5. Review the output.
  6. Run the apply mode with sudo.
  7. Verify the live site.
  8. Archive or clean the staged files only after validation.

Open Decisions Before Script Implementation

These should be fixed before writing version 1 of the script:

  • Whether Bash is available as /usr/bin/env bash or needs an absolute FreeBSD package path such as /usr/local/bin/bash.
  • Whether sudo can run the script directly as root or only specific commands.
  • Whether brand new files should be blocked entirely or allowed through a manifest.
  • Where logs should live permanently.
  • Whether there are any directories inside www or rus that must never be deployed from staging.

Proposed Implementation Phases

Phase 1

  • Write the promotion script.
  • Support dry run and apply mode.
  • Support existing-file updates only.
  • Support www, rus, and WEB-INF/classes.
  • Add logging and path safety checks.

Phase 2

  • Add manifest support for approved new files.
  • Add optional cleanup or archive of deployed staged files.
  • Add per-scope execution and better summaries.

Phase 3

  • Add optional checksum comparison to skip unchanged content.
  • Add rollback support via timestamped backup copies.
  • Add a small local helper to prepare the WinSCP staging tree from a list of changed files.

The next file to create should be the actual server script in sync/promote-sync.sh, based directly on the rules above.

Initial-Copy Mode and Permission Database

Version 1 must also include a conservative --init-sync mode that copies the live site into the user staging area and records the original live-file metadata into a permission database. This achieves two goals:

  • lets marco receive a full copy of the trees for local editing with readable files;
  • captures original owner/group/mode so the promotion step can reapply them exactly.

Placement and format

  • Permission database path (suggested): /home/marco/regalamiunsorriso/metadata/perms.jsonl (newline-delimited JSON). Store a backup as /home/marco/regalamiunsorriso/metadata/perms.jsonl.bak before each init-sync.
  • Each record should contain: relpath, owner, group, mode (octal string), type (file|dir), and optional sha256.

Example record:

{"relpath":"www/index.jsp","owner":"www","group":"www","mode":"0644","type":"file","sha256":"..."}

Init-sync behavior

  1. Validate the script is run with sufficient privilege (the script should be invoked with sudo for this mode).
  2. Build the list of allowed live subtrees (only www, rus, WEB-INF/classes).
  3. Walk the live tree and for each file/dir:
  • record metadata into the perms.jsonl file
  • copy the content into the staging incoming/ tree preserving relative paths
  • after copy, chown marco:marco and set permissive modes so marco can read and edit (files 0644, dirs 0755) — this provides a safe editable copy for WinSCP uploads
  1. Ensure symlinked exclusions (e.g., mypics*) are skipped during copy.

Promotion behavior (apply step uses the DB)

  1. When promoting a staged file back to live, the script looks up the matching relpath in perms.jsonl.
  2. If a record is found, the script will:
  • write the staged content to a temporary file under the destination directory
  • apply the recorded owner:group and mode to the temporary file
  • atomically move the temporary file over the live file
  1. If no record exists for a path and --allow-new is not used, the script should skip the file and log a destination-missing-perms reason.

Security and sanity

  • The --init-sync operation must be explicit and dangerous-only: require --apply-init or a clear confirmation step.
  • Keep a timestamped copy of the permission DB for quick audits and possible rollbacks.
  • Validate the owner:group values read from the live system are valid accounts on the target system before applying them during promotion.

Small example usages

# create staging and record current live metadata (requires sudo)
sudo ./promote-sync.sh --init-sync --perms-db /home/marco/regalamiunsorriso/metadata/perms.jsonl --apply-init

# dry-run promotion using the recorded perms
./promote-sync.sh --dry-run --perms-db /home/marco/regalamiunsorriso/metadata/perms.jsonl

# actual promotion
sudo ./promote-sync.sh --apply --perms-db /home/marco/regalamiunsorriso/metadata/perms.jsonl

Notes

  • This design keeps all privileged operations on the server and ensures files copied for local editing are readable by marco while preserving authoritative live permissions for when changes are promoted.
  • Later phases may add ACL support or an allowlist manifest to permit well-audited new files to be created on the live site with declared metadata.