set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/.." && pwd)"
migrations_dir="$repo_root/crates/aven-core/migrations"
migrations_relative="crates/aven-core/migrations"
target_ref="${WM_TARGET_BRANCH:-main}"
error() {
printf 'error: %s\n' "$*" >&2
}
migration_timestamp() {
local path="$1"
local name
name="$(basename "$path")"
printf '%s' "${name%%_*}"
}
if [[ ! -d "$migrations_dir" ]]; then
error "missing migrations directory: $migrations_dir"
exit 1
fi
mapfile -t migration_paths < <(find "$migrations_dir" -maxdepth 1 -type f -name '*.sql' -print | sort)
previous_timestamp=""
previous_path=""
for path in "${migration_paths[@]}"; do
name="$(basename "$path")"
if [[ ! "$name" =~ ^[0-9]{14}_[a-z0-9]+(_[a-z0-9]+)*\.sql$ ]]; then
error "migration filename must match YYYYMMDDHHMMSS_lower_snake.sql: $migrations_relative/$name"
exit 1
fi
timestamp="$(migration_timestamp "$path")"
if [[ -n "$previous_timestamp" && "$timestamp" == "$previous_timestamp" ]]; then
error "duplicate migration timestamp: $timestamp"
error " $migrations_relative/$(basename "$previous_path")"
error " $migrations_relative/$name"
exit 1
fi
previous_timestamp="$timestamp"
previous_path="$path"
done
if ! git -C "$repo_root" rev-parse --verify --quiet "$target_ref^{commit}" >/dev/null; then
printf 'skip migration target-order check: target ref %s not found\n' "$target_ref" >&2
exit 0
fi
mapfile -t merge_bases < <(git -C "$repo_root" merge-base --all HEAD "$target_ref" 2>/dev/null || true)
if [[ "${#merge_bases[@]}" -ne 1 ]]; then
printf 'skip migration target-order check: expected one merge base with %s, got %s\n' \
"$target_ref" \
"${#merge_bases[@]}" >&2
exit 0
fi
merge_base="${merge_bases[0]}"
mapfile -t target_migrations < <(git -C "$repo_root" ls-tree -r --name-only "$target_ref" -- migrations crates/aven-core/migrations '*.sql' | sort)
latest_target_timestamp=""
for path in "${target_migrations[@]}"; do
name="$(basename "$path")"
if [[ "$name" =~ ^[0-9]{14}_.*\.sql$ ]]; then
timestamp="${name%%_*}"
if [[ -z "$latest_target_timestamp" || "$timestamp" > "$latest_target_timestamp" ]]; then
latest_target_timestamp="$timestamp"
fi
fi
done
if [[ -z "$latest_target_timestamp" ]]; then
exit 0
fi
mapfile -t added_migrations < <(
{
git -C "$repo_root" diff --name-status --diff-filter=AR "$merge_base" -- crates/aven-core/migrations \
| awk '$1 == "A" { print $2 } $1 ~ /^R/ { print $3 }'
git -C "$repo_root" ls-files --others --exclude-standard -- 'crates/aven-core/migrations/*.sql'
} | sort -u
)
for path in "${added_migrations[@]}"; do
[[ "$path" == crates/aven-core/migrations/*.sql ]] || continue
[[ -f "$repo_root/$path" ]] || continue
name="$(basename "$path")"
if git -C "$repo_root" cat-file -e "$target_ref:migrations/$name" 2>/dev/null; then
continue
fi
timestamp="$(migration_timestamp "$path")"
if [[ "$timestamp" < "$latest_target_timestamp" || "$timestamp" == "$latest_target_timestamp" ]]; then
error "migration is not after latest $target_ref migration: $path"
error "latest $target_ref migration timestamp is $latest_target_timestamp"
error "create migrations with: just migration-new <name>"
exit 1
fi
done