#!/usr/bin/env bash set -Eeuo pipefail umask 022 # STEP023 - SERVER ATTESO: 215.6 / ax-accessi # # Allinea in presenze.distribuite il fix generico gia' presente su Silvelox # per modificaAssTmp(): # # input data: # 2026-07-01 # oppure: # 2026-07-01 00:00:00 # # output sempre: # from = 2026-07-01 00:00:00 # to = 2026-07-01 23:59:59 # # Evita risultati errati come: # 2026-07-01 00:00:00 00:00:00 # # Silvelox viene usato esclusivamente come riferimento in lettura. # Nessun altro portale custom viene modificato. START_DIR="$PWD" STAMP="$(date '+%Y%m%d_%H%M%S')" BASE="STEP023_2156_DISTRIBUITE_TEMPORAL_ASSOCIATION_SILVELOX_FIX_PATCH_TEST_COMMIT_ZIP_ONLY_${STAMP}" WORKDIR="${START_DIR}/.${BASE}_work_$$" OUTZIP="${START_DIR}/${BASE}.zip" LOGFILE="${WORKDIR}/run.log" REPORT="${WORKDIR}/report.txt" BACKUP_DIR="${WORKDIR}/backup" PATCH_DIR="${WORKDIR}/patched" PACKAGED=0 FAILURES=0 WARNINGS=0 COMMITTED=0 ROLLED_BACK=0 EXPECTED_HOST="${STEP_EXPECTED_HOST:-ax-accessi}" EXPECTED_IP="${STEP_EXPECTED_IP:-192.168.215.6}" SKIP_SERVER_CHECK="${STEP_SKIP_SERVER_CHECK:-0}" SOURCE_DIR="${STEP_SOURCE_DIR:-/var/www/html/presenze.silvelox}" TARGET_DIR="${STEP_TARGET_DIR:-/var/www/html/presenze.distribuite}" mkdir -p "$WORKDIR" "$BACKUP_DIR" "$PATCH_DIR" : >"$LOGFILE" : >"$REPORT" log() { printf '%s\n' "$*" | tee -a "$LOGFILE"; } report() { printf '%s\n' "$*" >>"$REPORT"; } fail() { FAILURES=$((FAILURES + 1)); log "ERRORE: $*"; report "ERRORE: $*"; } warn() { WARNINGS=$((WARNINGS + 1)); log "ATTENZIONE: $*"; report "ATTENZIONE: $*"; } package_results() { local rc="${1:-0}" local zip_rc=0 [[ "$PACKAGED" -eq 1 ]] && return 0 PACKAGED=1 report "" report "Esito finale: errori=${FAILURES}, avvisi=${WARNINGS}, commit=${COMMITTED}, rollback=${ROLLED_BACK}, exit_code=${rc}" rm -f -- "$OUTZIP" if ! command -v zip >/dev/null 2>&1; then rm -rf -- "$WORKDIR" printf '%s\n' "ERRORE: comando zip non disponibile" >&2 return 127 fi (cd "$WORKDIR" && zip -qr "$OUTZIP" .) || zip_rc=$? if [[ "$zip_rc" -eq 0 ]]; then zip -T "$OUTZIP" >>"$LOGFILE" 2>&1 || zip_rc=$? fi rm -rf -- "$WORKDIR" if [[ "$zip_rc" -ne 0 ]]; then rm -f -- "$OUTZIP" printf '%s\n' "ERRORE: creazione/verifica ZIP fallita (rc=${zip_rc})" >&2 return "$zip_rc" fi printf '%s\n' "ZIP CREATO E VERIFICATO: $OUTZIP" } on_exit() { local rc=$? trap - EXIT INT TERM HUP package_results "$rc" || rc=$? exit "$rc" } trap on_exit EXIT trap 'exit 130' INT trap 'exit 143' TERM HUP log "$BASE" log "SERVER ATTESO: 215.6 / ax-accessi" log "SORGENTE SOLA LETTURA: $SOURCE_DIR" log "TARGET ESCLUSIVO: $TARGET_DIR" log "ALTRI PORTALI CUSTOM: NON TOCCATI" log "MODALITA': PATCH + TEST + COMMIT" log "Avvio: $(date --iso-8601=seconds)" ACTUAL_HOST="$(hostname -s 2>/dev/null || hostname)" ACTUAL_IPS="$(hostname -I 2>/dev/null | xargs || true)" { echo "STEP: $BASE" echo "Server atteso: 215.6 / ax-accessi" echo "Sorgente sola lettura: $SOURCE_DIR" echo "Target esclusivo: $TARGET_DIR" echo "Altri portali custom: NON TOCCATI" echo "Modalita': PATCH + TEST + COMMIT" echo "Data: $(date --iso-8601=seconds)" echo "Hostname: $ACTUAL_HOST" echo "IP: $ACTUAL_IPS" echo } >>"$REPORT" for cmd in grep awk wc sort zip hostname xargs tr find sha256sum stat cp mv chmod chown diff sed dirname basename readlink; do command -v "$cmd" >/dev/null 2>&1 || fail "comando richiesto non disponibile: $cmd" done [[ "$FAILURES" -eq 0 ]] || exit 20 if [[ "$SKIP_SERVER_CHECK" != "1" ]]; then [[ "$ACTUAL_HOST" == "$EXPECTED_HOST" ]] \ || fail "hostname ${ACTUAL_HOST}; previsto ${EXPECTED_HOST}" if [[ -n "$EXPECTED_IP" ]] && ! grep -qw -- "$EXPECTED_IP" <<<"$ACTUAL_IPS"; then fail "IP ${EXPECTED_IP} non presente tra: ${ACTUAL_IPS}" fi fi [[ "$FAILURES" -eq 0 ]] || exit 21 [[ "$SOURCE_DIR" == "/var/www/html/presenze.silvelox" ]] \ || fail "sorgente inattesa: $SOURCE_DIR" [[ "$TARGET_DIR" == "/var/www/html/presenze.distribuite" ]] \ || fail "target inatteso: $TARGET_DIR" [[ -d "$SOURCE_DIR" ]] || fail "sorgente assente: $SOURCE_DIR" [[ -d "$TARGET_DIR" ]] || fail "target assente: $TARGET_DIR" [[ "$FAILURES" -eq 0 ]] || exit 22 PHP_BIN="${STEP_PHP_BIN:-}" if [[ -z "$PHP_BIN" ]]; then if command -v php >/dev/null 2>&1; then PHP_BIN="$(command -v php)" else fail "php CLI non disponibile" exit 23 fi fi log "[1/8] Individuazione univoca del controller su Silvelox" mapfile -d '' SOURCE_CANDIDATES < <( find "$SOURCE_DIR" \ -xdev \ \( \ -path "${SOURCE_DIR}/node_modules" \ -o -path "${SOURCE_DIR}/bower_components" \ -o -path "${SOURCE_DIR}/templates_c" \ -o -path "${SOURCE_DIR}/libs/stimulsoft_legacy" \ -o -path "${SOURCE_DIR}/document" \ -o -path "${SOURCE_DIR}/documents" \ \) -prune \ -o -type f -name '*.php' \ -exec grep -Iq 'function modificaAssTmp' {} \; \ -print0 2>>"$LOGFILE" ) SOURCE_COUNT="${#SOURCE_CANDIDATES[@]}" printf 'indice\tpercorso\n' >"${WORKDIR}/01_SOURCE_CANDIDATES.tsv" for i in "${!SOURCE_CANDIDATES[@]}"; do printf '%s\t%s\n' "$i" "${SOURCE_CANDIDATES[$i]}" \ >>"${WORKDIR}/01_SOURCE_CANDIDATES.tsv" done [[ "$SOURCE_COUNT" -eq 1 ]] \ || fail "controller modificaAssTmp su Silvelox trovato ${SOURCE_COUNT} volte; atteso 1" [[ "$FAILURES" -eq 0 ]] || exit 24 SOURCE_FILE="${SOURCE_CANDIDATES[0]}" RELATIVE_FILE="${SOURCE_FILE#${SOURCE_DIR}/}" TARGET_FILE="${TARGET_DIR}/${RELATIVE_FILE}" [[ "$SOURCE_FILE" == "${SOURCE_DIR}/"* ]] \ || fail "file sorgente fuori da Silvelox: $SOURCE_FILE" [[ "$TARGET_FILE" == "${TARGET_DIR}/"* ]] \ || fail "file target fuori da presenze.distribuite: $TARGET_FILE" [[ -f "$TARGET_FILE" ]] \ || fail "file corrispondente assente nel target: $TARGET_FILE" [[ ! -L "$SOURCE_FILE" ]] || fail "file sorgente non deve essere symlink" [[ ! -L "$TARGET_FILE" ]] || fail "file target non deve essere symlink" [[ "$FAILURES" -eq 0 ]] || exit 25 log "[2/8] Verifica che Silvelox contenga il fix corretto" FIX_FROM='$from = substr(trim((string) $data->from), 0, 10) . '\'' 00:00:00'\'';' FIX_TO='$to = substr(trim((string) $data->to), 0, 10) . '\'' 23:59:59'\'';' OLD_FROM='$from = $data->from . '\'' 00:00:00'\'';' OLD_TO='$to = $data->to . '\'' 23:59:59'\'';' SOURCE_FIX_FROM_COUNT="$(grep -Fxc "$FIX_FROM" < <(sed -E 's/^[[:space:]]+//' "$SOURCE_FILE") || true)" SOURCE_FIX_TO_COUNT="$(grep -Fxc "$FIX_TO" < <(sed -E 's/^[[:space:]]+//' "$SOURCE_FILE") || true)" [[ "$SOURCE_FIX_FROM_COUNT" == "1" ]] \ || fail "Silvelox non contiene esattamente una riga fix FROM" [[ "$SOURCE_FIX_TO_COUNT" == "1" ]] \ || fail "Silvelox non contiene esattamente una riga fix TO" "$PHP_BIN" -l "$SOURCE_FILE" >"${WORKDIR}/02_SOURCE_PHP_LINT.txt" 2>&1 \ || fail "php -l fallito sul controller Silvelox" SOURCE_HASH_BEFORE="$(sha256sum "$SOURCE_FILE" | awk '{print $1}')" [[ "$FAILURES" -eq 0 ]] || exit 26 log "[3/8] Analisi stato target" TARGET_HASH_BEFORE="$(sha256sum "$TARGET_FILE" | awk '{print $1}')" TARGET_MODE="$(stat -c '%a' "$TARGET_FILE")" TARGET_UID="$(stat -c '%u' "$TARGET_FILE")" TARGET_GID="$(stat -c '%g' "$TARGET_FILE")" TARGET_FIXED_FROM_COUNT="$(grep -Fxc "$FIX_FROM" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" TARGET_FIXED_TO_COUNT="$(grep -Fxc "$FIX_TO" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" TARGET_OLD_FROM_COUNT="$(grep -Fxc "$OLD_FROM" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" TARGET_OLD_TO_COUNT="$(grep -Fxc "$OLD_TO" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" { printf 'voce\tvalore\n' printf 'relative_file\t%s\n' "$RELATIVE_FILE" printf 'source_file\t%s\n' "$SOURCE_FILE" printf 'target_file\t%s\n' "$TARGET_FILE" printf 'source_hash_before\t%s\n' "$SOURCE_HASH_BEFORE" printf 'target_hash_before\t%s\n' "$TARGET_HASH_BEFORE" printf 'target_fixed_from_count\t%s\n' "$TARGET_FIXED_FROM_COUNT" printf 'target_fixed_to_count\t%s\n' "$TARGET_FIXED_TO_COUNT" printf 'target_old_from_count\t%s\n' "$TARGET_OLD_FROM_COUNT" printf 'target_old_to_count\t%s\n' "$TARGET_OLD_TO_COUNT" } >"${WORKDIR}/03_PRECHECKS.tsv" if [[ "$TARGET_FIXED_FROM_COUNT" == "1" && "$TARGET_FIXED_TO_COUNT" == "1" \ && "$TARGET_OLD_FROM_COUNT" == "0" && "$TARGET_OLD_TO_COUNT" == "0" ]]; then TARGET_STATE="GIA_CORRETTO" elif [[ "$TARGET_FIXED_FROM_COUNT" == "0" && "$TARGET_FIXED_TO_COUNT" == "0" \ && "$TARGET_OLD_FROM_COUNT" == "1" && "$TARGET_OLD_TO_COUNT" == "1" ]]; then TARGET_STATE="DA_CORREGGERE" else fail "stato target ambiguo: fix_from=${TARGET_FIXED_FROM_COUNT}, fix_to=${TARGET_FIXED_TO_COUNT}, old_from=${TARGET_OLD_FROM_COUNT}, old_to=${TARGET_OLD_TO_COUNT}" fi "$PHP_BIN" -l "$TARGET_FILE" >"${WORKDIR}/04_TARGET_PHP_LINT_BEFORE.txt" 2>&1 \ || fail "php -l fallito sul controller target prima della patch" [[ "$FAILURES" -eq 0 ]] || exit 27 log "[4/8] Test funzionale della normalizzazione date" "$PHP_BIN" -r ' $cases = [ ["2026-07-01", "2026-07-01 00:00:00", "2026-07-01 23:59:59"], ["2026-07-01 08:39:00", "2026-07-01 00:00:00", "2026-07-01 23:59:59"], [" 2026-07-01 08:39:00 ", "2026-07-01 00:00:00", "2026-07-01 23:59:59"] ]; echo "input\tfrom\tto\tesito\n"; foreach ($cases as $case) { $input = $case[0]; $from = substr(trim((string) $input), 0, 10) . " 00:00:00"; $to = substr(trim((string) $input), 0, 10) . " 23:59:59"; $ok = ($from === $case[1] && $to === $case[2]) ? "OK" : "ERRORE"; echo str_replace(["\t", "\r", "\n"], " ", $input), "\t", $from, "\t", $to, "\t", $ok, "\n"; if ($ok !== "OK") { exit(10); } } ' >"${WORKDIR}/05_NORMALIZATION_TEST.tsv" 2>>"$LOGFILE" \ || fail "test funzionale normalizzazione date fallito" [[ "$FAILURES" -eq 0 ]] || exit 28 log "[5/8] Preparazione patch e backup" cp -a -- "$TARGET_FILE" "${BACKUP_DIR}/controllerCtrl.php.before_step023" cp -a -- "$TARGET_FILE" "${PATCH_DIR}/controllerCtrl.php" PATCHER="${WORKDIR}/patch.php" cat >"$PATCHER" <<'PHP' from), 0, 10) . \' 00:00:00\';'; $fixedTo = '$to = substr(trim((string) $data->to), 0, 10) . \' 23:59:59\';'; $oldFrom = '$from = $data->from . \' 00:00:00\';'; $oldTo = '$to = $data->to . \' 23:59:59\';'; $fixedFromCount = substr_count($content, $fixedFrom); $fixedToCount = substr_count($content, $fixedTo); $oldFromCount = substr_count($content, $oldFrom); $oldToCount = substr_count($content, $oldTo); if ( $fixedFromCount === 1 && $fixedToCount === 1 && $oldFromCount === 0 && $oldToCount === 0 ) { echo "ALREADY_FIXED\n"; exit(0); } if ( $fixedFromCount !== 0 || $fixedToCount !== 0 || $oldFromCount !== 1 || $oldToCount !== 1 ) { fwrite( STDERR, "Stato ambiguo: fixedFrom={$fixedFromCount}, fixedTo={$fixedToCount}, oldFrom={$oldFromCount}, oldTo={$oldToCount}\n" ); exit(4); } $content = str_replace($oldFrom, $fixedFrom, $content, $replaceFrom); $content = str_replace($oldTo, $fixedTo, $content, $replaceTo); if ($replaceFrom !== 1 || $replaceTo !== 1) { fwrite(STDERR, "Sostituzione inattesa: from={$replaceFrom}, to={$replaceTo}\n"); exit(5); } if (file_put_contents($file, $content) === false) { fwrite(STDERR, "Scrittura file fallita\n"); exit(6); } echo "PATCHED\n"; PHP "$PHP_BIN" -l "$PATCHER" >"${WORKDIR}/06_PATCHER_PHP_LINT.txt" 2>&1 \ || fail "php -l fallito sul patcher" PATCH_RESULT="$("$PHP_BIN" "$PATCHER" "${PATCH_DIR}/controllerCtrl.php" 2>>"$LOGFILE")" \ || fail "patcher fallito" "$PHP_BIN" -l "${PATCH_DIR}/controllerCtrl.php" >"${WORKDIR}/07_PATCHED_PHP_LINT.txt" 2>&1 \ || fail "php -l fallito sul file patchato" PATCHED_FIX_FROM_COUNT="$(grep -Fxc "$FIX_FROM" < <(sed -E 's/^[[:space:]]+//' "${PATCH_DIR}/controllerCtrl.php") || true)" PATCHED_FIX_TO_COUNT="$(grep -Fxc "$FIX_TO" < <(sed -E 's/^[[:space:]]+//' "${PATCH_DIR}/controllerCtrl.php") || true)" PATCHED_OLD_FROM_COUNT="$(grep -Fxc "$OLD_FROM" < <(sed -E 's/^[[:space:]]+//' "${PATCH_DIR}/controllerCtrl.php") || true)" PATCHED_OLD_TO_COUNT="$(grep -Fxc "$OLD_TO" < <(sed -E 's/^[[:space:]]+//' "${PATCH_DIR}/controllerCtrl.php") || true)" [[ "$PATCHED_FIX_FROM_COUNT" == "1" ]] || fail "fix FROM assente nel file patchato" [[ "$PATCHED_FIX_TO_COUNT" == "1" ]] || fail "fix TO assente nel file patchato" [[ "$PATCHED_OLD_FROM_COUNT" == "0" ]] || fail "vecchia riga FROM ancora presente" [[ "$PATCHED_OLD_TO_COUNT" == "0" ]] || fail "vecchia riga TO ancora presente" DIFF_RC=0 diff -u \ "${BACKUP_DIR}/controllerCtrl.php.before_step023" \ "${PATCH_DIR}/controllerCtrl.php" \ >"${WORKDIR}/08_PATCH.diff" || DIFF_RC=$? if [[ "$TARGET_STATE" == "DA_CORREGGERE" ]]; then [[ "$DIFF_RC" -eq 1 ]] || fail "diff atteso non rilevato" else [[ "$DIFF_RC" -eq 0 ]] || fail "target giĆ  corretto ma il file patchato risulta diverso" fi [[ "$FAILURES" -eq 0 ]] || exit 29 rollback_target() { if [[ -f "${BACKUP_DIR}/controllerCtrl.php.before_step023" ]]; then cp -a -- "${BACKUP_DIR}/controllerCtrl.php.before_step023" "$TARGET_FILE" ROLLED_BACK=1 log "ROLLBACK AUTOMATICO COMPLETATO" else fail "backup per rollback assente" fi } log "[6/8] Commit atomico sul solo target" if [[ "$TARGET_STATE" == "DA_CORREGGERE" ]]; then TARGET_TMP="${TARGET_FILE}.step023.$$" rm -f -- "$TARGET_TMP" cp -a -- "${PATCH_DIR}/controllerCtrl.php" "$TARGET_TMP" chmod "$TARGET_MODE" "$TARGET_TMP" chown "$TARGET_UID:$TARGET_GID" "$TARGET_TMP" mv -f -- "$TARGET_TMP" "$TARGET_FILE" COMMITTED=1 else COMMITTED=0 log "TARGET GIA' CORRETTO: nessuna scrittura necessaria" fi log "[7/8] Postcheck e confronto con il fix Silvelox" TARGET_HASH_AFTER="$(sha256sum "$TARGET_FILE" | awk '{print $1}')" SOURCE_HASH_AFTER="$(sha256sum "$SOURCE_FILE" | awk '{print $1}')" FINAL_FIX_FROM_COUNT="$(grep -Fxc "$FIX_FROM" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" FINAL_FIX_TO_COUNT="$(grep -Fxc "$FIX_TO" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" FINAL_OLD_FROM_COUNT="$(grep -Fxc "$OLD_FROM" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" FINAL_OLD_TO_COUNT="$(grep -Fxc "$OLD_TO" < <(sed -E 's/^[[:space:]]+//' "$TARGET_FILE") || true)" POST_OK=1 [[ "$SOURCE_HASH_AFTER" == "$SOURCE_HASH_BEFORE" ]] || POST_OK=0 [[ "$FINAL_FIX_FROM_COUNT" == "1" ]] || POST_OK=0 [[ "$FINAL_FIX_TO_COUNT" == "1" ]] || POST_OK=0 [[ "$FINAL_OLD_FROM_COUNT" == "0" ]] || POST_OK=0 [[ "$FINAL_OLD_TO_COUNT" == "0" ]] || POST_OK=0 if ! "$PHP_BIN" -l "$TARGET_FILE" >"${WORKDIR}/09_TARGET_PHP_LINT_AFTER.txt" 2>&1; then POST_OK=0 fi if [[ "$POST_OK" -ne 1 ]]; then fail "postcheck finale fallito" rollback_target exit 30 fi { printf 'controllo\tatteso\trilevato\n' printf 'source_unchanged\tSI\t%s\n' "$([[ "$SOURCE_HASH_AFTER" == "$SOURCE_HASH_BEFORE" ]] && echo SI || echo NO)" printf 'target_fix_from_count\t1\t%s\n' "$FINAL_FIX_FROM_COUNT" printf 'target_fix_to_count\t1\t%s\n' "$FINAL_FIX_TO_COUNT" printf 'target_old_from_count\t0\t%s\n' "$FINAL_OLD_FROM_COUNT" printf 'target_old_to_count\t0\t%s\n' "$FINAL_OLD_TO_COUNT" printf 'target_state_before\t-\t%s\n' "$TARGET_STATE" printf 'patch_result\t-\t%s\n' "$PATCH_RESULT" printf 'commit_eseguito\t-\t%s\n' "$COMMITTED" printf 'target_hash_before\t-\t%s\n' "$TARGET_HASH_BEFORE" printf 'target_hash_after\t-\t%s\n' "$TARGET_HASH_AFTER" } >"${WORKDIR}/10_POSTCHECKS.tsv" log "[8/8] Esito" { echo "PATCH COMPLETATA" echo "Server: 215.6 / ax-accessi" echo "Sorgente Silvelox modificata: NO" echo "Target: $TARGET_FILE" echo "File relativo: $RELATIVE_FILE" echo "Stato iniziale target: $TARGET_STATE" echo "Commit eseguito: $COMMITTED" echo "Fix FROM: substr(trim((string) data->from), 0, 10) + 00:00:00" echo "Fix TO: substr(trim((string) data->to), 0, 10) + 23:59:59" echo "Test input data semplice: OK" echo "Test input data con orario: OK" echo "PHP lint sorgente/target/patcher: OK" echo "Rollback automatico: non necessario" echo "Altri portali custom toccati: NO" } >"${WORKDIR}/99_SUMMARY.txt" report "Allineato il fix modificaAssTmp da Silvelox a presenze.distribuite." report "Silvelox non modificato." report "Altri portali custom non toccati." log "PATCH COMPLETATA E VERIFICATA"