fix(ci): guard migration SQL files against exceeding the manifest version #53

Merged
jmiller merged 1 commits from fix/ci-schema-version-guard into main 2026-07-12 21:38:35 +00:00
+47 -21
View File
@@ -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 "<extension" "$XML_FILE" 2>/dev/null; then
MANIFEST="$XML_FILE"
break
fi
done
if [ -n "$MANIFEST" ]; then
MANIFEST_VERSION=$(grep -oP '<version>\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 <root>/sql/updates/mysql
COMP_ROOT=$(dirname "$(dirname "$(dirname "$UPDATE_DIR")")")
# Resolve this component's OWN manifest (XML with <extension> at its root)
COMP_MANIFEST=""
for XML_FILE in "$COMP_ROOT"/*.xml; do
[ -f "$XML_FILE" ] || continue
if grep -q "<extension" "$XML_FILE" 2>/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 '<version>\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