From e3167ce6e5accd6ca0c9dbf4b96529d03145299f Mon Sep 17 00:00:00 2001 From: yukkop Date: Mon, 3 Nov 2025 18:55:35 +0000 Subject: [PATCH] feat(package): +migrator --- package/migrator/default.nix | 20 ++++++ package/migrator/migrator.sh | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 package/migrator/default.nix create mode 100644 package/migrator/migrator.sh diff --git a/package/migrator/default.nix b/package/migrator/default.nix new file mode 100644 index 0000000..7944a88 --- /dev/null +++ b/package/migrator/default.nix @@ -0,0 +1,20 @@ +{ dash, hectic }: +let + shell = "${dash}/bin/dash"; + bashOptions = [ + "errexit" + "nounset" + ]; + + migrator = hectic.writeShellApplication { + inherit shell bashOptions; + name = "migrator"; + runtimeInputs = [ ]; + + text = '' + ${builtins.readFile hectic.helpers.posix-shell.log} + ${builtins.readFile ./migrator.sh} + ''; + }; +in +migrator diff --git a/package/migrator/migrator.sh b/package/migrator/migrator.sh new file mode 100644 index 0000000..5464f18 --- /dev/null +++ b/package/migrator/migrator.sh @@ -0,0 +1,125 @@ +#!/bin/dash + +if ! command -v psql >/dev/null; then + log error "Required tool (psql) are not installed." + exit 127 +fi + +MIGRATION_DIR="${MIGRATION_DIR:-migration}" + +while [ $# -gt 0 ]; do + case $1 in + migrate|create|fetch) + [ -n "$SUBCOMMAND" ] || (printf 'ambiguous subcommand, decide %s or %s' "$SUBCOMMAND" "$1"; exit 1) + SUBCOMMAND=$1 + shift + ;; + --migration-dir|-d) + MIGRATION_DIR=$2 + shift 2 + ;; + --inherits) + INHERITS_LIST="${INHERITS_LIST:+$INHERITS_LIST }$2" + shift 2 + ;; + --*|-*) ;; # skip all unrecognized arguments + *) + printf 'subcommand %s does not exists' "$1" + exit 1 + ;; + esac +done + +[ -z "$SUBCOMMAND" ] || (log error "no subcomand specified"; exit 1) + +help() { + # inherits: List one or more tables the migration table must inherit from + echo help +} + +migrate() { + while [ $# -gt 0 ]; do + case $1 in + up|down|to) + [ -n "$MIGRATE_SUBCOMMAND" ] || (printf 'ambiguous migrate subcommand, decide %s or %s' "$MIGRATE_SUBCOMMAND" "$1"; exit 1) + MIGRATE_SUBCOMMAND="$1" + ;; + --db-url|-u) + DB_URL="$2" + shift 2 + ;; + --force|-f) + FORCE=1 + shift + ;; + --set|-v) + VARIABLE_LIST="${VARIABLE_LIST:+$VARIABLE_LIST }$2" + shift 2 + ;; + --*|-*) + printf 'argument %s does not exists' "$1" + exit 1 + ;; + *) + printf 'migrate subcommand %s does not exists' "$1" + exit 1 + ;; + esac + done + + # Get the list of new migrations from disk + fs_migrations=$( + find "$MIGRATION_DIR" -maxdepth 1 -type f -name '*.sql' \ + | sort \ + | xargs -n1 basename + ) + + # Get the list of already applied migrations from DB + db_migrations=$( + psql -Atqc "SELECT name FROM hectic.migration ORDER BY name ASC" \ + | awk NF + ) + + # Check if the DB migrations form a proper prefix of disk migrations + # (meaning all DB-applied migration filenames should appear in the same order at the start). + i=0 + for db_migration in $db_migrations; do + fs_migration=$(echo "$fs_migrations" | sed -n "$((i+1))p") + if [ -z "$fs_migration" ] || [ "$fs_migration" != "$db_migration" ]; then + if [ -z "$FORCE" ]; then + echo "Unrelated migration tree detected. Use --force to proceed." >&2 + exit 2 + else + echo "Unrelated migration tree forced. Proceeding..." >&2 + break + fi + fi + i=$((i+1)) + done +} + +create() { + while [ $# -gt 0 ]; do + case $1 in + --name|-n) + # shellcheck disable=SC2034 + NAME=$2 + shift 2 + ;; + esac + done +} + +fetch() { + while [ $# -gt 0 ]; do + case $1 in + --db-url|-u) + # shellcheck disable=SC2034 + DB_URL=$2 + shift 2 + ;; + esac + done +} + +"$SUBCOMMAND"