From 8c579a28411fb394ac651775fbb79d56bd8a6fe9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 12 Jul 2026 21:37:03 +0000 Subject: [PATCH] fix(ci): guard migration SQL files against exceeding the manifest version Rework the SQL-schema check so each component is validated against ITS OWN manifest version instead of a single package-level version applied to every sql/updates/mysql dir (which mis-flagged independently-versioned sub-extensions). Add check #11: fail if any update file version exceeds its component manifest version. This catches migration files drifting onto a different numbering lane and getting ahead of the release version (e.g. 01.14.00.sql while the manifest is 01.01.40) at PR time instead of surfacing as a runtime schema mismatch. Authored-by: Moko Consulting Claude-Session: https://claude.ai/code/session_01VXrBk7psPzJPHVuBDJwt2x --- .mokogitea/workflows/ci-joomla.yml | 68 +++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/.mokogitea/workflows/ci-joomla.yml b/.mokogitea/workflows/ci-joomla.yml index d493d6d..3aa5223 100644 --- a/.mokogitea/workflows/ci-joomla.yml +++ b/.mokogitea/workflows/ci-joomla.yml @@ -502,28 +502,54 @@ jobs: done <<< "$INSTALL_FILES" fi - # 10. Current manifest version should have an update SQL file - MANIFEST="" - for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do - if grep -q "/dev/null; then - MANIFEST="$XML_FILE" - break - fi - done - if [ -n "$MANIFEST" ]; then - MANIFEST_VERSION=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1) - if [ -n "$MANIFEST_VERSION" ]; then - UPDATE_DIRS=$(find . -path "*/sql/updates/mysql" -type d -not -path "./.git/*" 2>/dev/null) - if [ -n "$UPDATE_DIRS" ]; then - while IFS= read -r UPDATE_DIR; do - if [ ! -f "${UPDATE_DIR}/${MANIFEST_VERSION}.sql" ]; then - echo "::error file=${MANIFEST}::No update SQL file for current version ${MANIFEST_VERSION}" - echo "- **Missing \`${UPDATE_DIR}/${MANIFEST_VERSION}.sql\`** — update file must exist for manifest version" >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - fi - done <<< "$UPDATE_DIRS" + # 10 & 11. Per-component schema/version consistency. Each component + # versions independently, so resolve every sql/updates/mysql dir + # against ITS OWN manifest (not one package-level version): + # (10) the component's manifest version must have a matching update file + # (11) no update file may exceed the component's manifest version + # (this is the guard that catches migration files drifting onto + # a different numbering lane and getting ahead of the release) + UPDATE_DIRS=$(find . -path "*/sql/updates/mysql" -type d -not -path "./.git/*" 2>/dev/null) + if [ -n "$UPDATE_DIRS" ]; then + while IFS= read -r UPDATE_DIR; do + # Component root is three levels up from /sql/updates/mysql + COMP_ROOT=$(dirname "$(dirname "$(dirname "$UPDATE_DIR")")") + # Resolve this component's OWN manifest (XML with at its root) + COMP_MANIFEST="" + for XML_FILE in "$COMP_ROOT"/*.xml; do + [ -f "$XML_FILE" ] || continue + if grep -q "/dev/null; then + COMP_MANIFEST="$XML_FILE" + break + fi + done + if [ -z "$COMP_MANIFEST" ]; then + echo "- No component manifest found for \`${UPDATE_DIR}\` — skipping version match" >> $GITHUB_STEP_SUMMARY + continue fi - fi + COMP_VERSION=$(grep -oP '\K[^<]+' "$COMP_MANIFEST" 2>/dev/null | head -1) + [ -z "$COMP_VERSION" ] && continue + # Normalise: strip any pre-release suffix (-dev, -rc.N, ...) for comparison + CV_BASE=$(echo "$COMP_VERSION" | sed -E 's/-.*$//') + # (10) a matching update file must exist for the component version + if [ ! -f "${UPDATE_DIR}/${CV_BASE}.sql" ]; then + echo "::error file=${COMP_MANIFEST}::No update SQL file for version ${CV_BASE}" + echo "- **Missing \`${UPDATE_DIR}/${CV_BASE}.sql\`** — update file must exist for manifest version \`${CV_BASE}\`" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + fi + # (11) no update file may exceed the component version + for UFILE in "$UPDATE_DIR"/*.sql; do + [ -f "$UFILE" ] || continue + UVER=$(basename "$UFILE" .sql) + echo "$UVER" | grep -qP '^\d+\.\d+\.\d+$' || continue + TOP=$(printf '%s\n%s\n' "$CV_BASE" "$UVER" | sort -V | tail -1) + if [ "$UVER" != "$CV_BASE" ] && [ "$TOP" = "$UVER" ]; then + echo "::error file=${UFILE}::Update file ${UVER} exceeds manifest version ${CV_BASE}" + echo "- **Update file \`${UVER}.sql\` exceeds manifest version \`${CV_BASE}\`** — renumber onto the release lane (must be <= manifest)" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + fi + done + done <<< "$UPDATE_DIRS" fi fi