From 9a7e7d677a4412f8ad68472724ad96691da2a6b3 Mon Sep 17 00:00:00 2001 From: yukkop Date: Mon, 17 Nov 2025 16:26:25 +0000 Subject: [PATCH] feat(package): `migrator`: some migrate up works and init --- package/migrator/migrator.sh | 141 ++++++++++++------ test/package/migrator/test/arguments.sh | 27 ++++ .../test/init-migrator-with-inherits.sh | 76 ++++++++++ test/package/migrator/test/init-migrator.sh | 50 +++---- test/package/migrator/test/migrate-up/run.sh | 22 +-- .../migration/20251004192425-some-changes.sql | 1 - .../20251004192425-some-changes/placeholder | 0 .../migration/20251004292448-some-changes.sql | 1 - .../20251004292448-some-changes/down.sql | 0 .../20251004292448-some-changes/up.sql | 0 .../20251104172425-third-migration.sql | 1 - .../20251104172425-third-migration/up.sql | 0 .../migration/20251104192427-an-other-one.sql | 1 - .../20251104192427-an-other-one/up.sql | 0 .../migration/20251104292469-almoust-last.sql | 1 - .../20251104292469-almoust-last/down.sql | 0 .../20251104292469-almoust-last/up.sql | 0 .../migration/20251204152446-very-last.sql | 1 - .../20251204152446-very-last/down.sql | 0 .../migration/20251204152446-very-last/up.sql | 0 .../migrator/test/migrations-list/run.sh | 43 +++++- 21 files changed, 276 insertions(+), 89 deletions(-) create mode 100644 test/package/migrator/test/arguments.sh create mode 100644 test/package/migrator/test/init-migrator-with-inherits.sh delete mode 100644 test/package/migrator/test/migrations-list/migration/20251004192425-some-changes.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251004192425-some-changes/placeholder delete mode 100644 test/package/migrator/test/migrations-list/migration/20251004292448-some-changes.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251004292448-some-changes/down.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251004292448-some-changes/up.sql delete mode 100644 test/package/migrator/test/migrations-list/migration/20251104172425-third-migration.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251104172425-third-migration/up.sql delete mode 100644 test/package/migrator/test/migrations-list/migration/20251104192427-an-other-one.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251104192427-an-other-one/up.sql delete mode 100644 test/package/migrator/test/migrations-list/migration/20251104292469-almoust-last.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251104292469-almoust-last/down.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251104292469-almoust-last/up.sql delete mode 100644 test/package/migrator/test/migrations-list/migration/20251204152446-very-last.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251204152446-very-last/down.sql create mode 100644 test/package/migrator/test/migrations-list/migration/20251204152446-very-last/up.sql diff --git a/package/migrator/migrator.sh b/package/migrator/migrator.sh index 1823d27..1a10846 100644 --- a/package/migrator/migrator.sh +++ b/package/migrator/migrator.sh @@ -17,6 +17,7 @@ if ! command -v psql >/dev/null; then exit 127 fi +VERSION='0.0.1' MIGRATION_DIR="${MIGRATION_DIR:-migration}" quote() { printf "'%s'" "$(printf %s "$1" | sed "s/'/'\\\\''/g")"; } REMAINING_ARS= @@ -29,14 +30,14 @@ sha256sum() { cksum --algorithm=sha256 --untagged "$file" | awk '{printf $1}' } -INHERITS_LIST= -VARIABLE_LIST= - while [ $# -gt 0 ]; do log debug "$1" case $1 in migrate|create|fetch|list|init) - [ "${SUBCOMMAND+x}" ] && { printf 'ambiguous subcommand, decide %s or %s\n' "$SUBCOMMAND" "$1"; exit 2; } + [ "${SUBCOMMAND+x}" ] && { + log error "ambiguous subcommand, decide ${WHITE}$SUBCOMMAND ${NC}or ${WHITE}$1"; + exit 2; + } SUBCOMMAND=$1 shift ;; @@ -45,7 +46,7 @@ while [ $# -gt 0 ]; do shift 2 ;; --inherits) - INHERITS_LIST="${INHERITS_LIST:+$INHERITS_LIST\"}$2" + INHERITS_LIST="${INHERITS_LIST+$INHERITS_LIST\"}$2" shift 2 ;; --*|-*) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; # unknown global -> pass through @@ -53,7 +54,7 @@ while [ $# -gt 0 ]; do esac done -INHERITS_LIST="$(printf '%s' "$INHERITS_LIST" | sed -E 's/"/,/g; s/([^,]+)/"\1"/g')" +[ ${INHERITS_LIST+x} ] && INHERITS_LIST="$(printf '%s' "$INHERITS_LIST" | sed -E 's/"/,/g; s/([^,]+)/"\1"/g')" # shellcheck disable=SC2120 init() { @@ -68,7 +69,7 @@ init() { shift 2 ;; --set|-v) - VARIABLE_LIST="${VARIABLE_LIST:+$VARIABLE_LIST }$2" + VARIABLE_LIST="${VARIABLE_LIST+$VARIABLE_LIST }$2" shift 2 ;; --*|-*) @@ -88,24 +89,26 @@ init() { psql_args="$(form_psql_args)" - oldIFS="$IFS" - IFS=',' - check_inherits= - for table in $INHERITS_LIST; do - check_inherits="$(printf '%s\nSELECT 1 FROM %s LIMIT 1;' "$check_inherits" "$table")" - done - IFS="$oldIFS" + [ ${INHERITS_LIST+x} ] && { + oldIFS="$IFS" + IFS=',' + check_inherits= + for table in $INHERITS_LIST; do + check_inherits="$(printf '%s\nSELECT 1 FROM %s LIMIT 1;' "$check_inherits" "$table")" + done + IFS="$oldIFS" - check_inherits=$(printf '%s\n' \ - 'BEGIN;' \ - "$check_inherits" \ - 'COMMIT;') + check_inherits=$(printf '%s\n' \ + 'BEGIN;' \ + "$check_inherits" \ + 'COMMIT;') - # shellcheck disable=SC2086 - if ! psql $psql_args -c "$check_inherits"; then - log error "init failed: ${WHITE}one of inherits table does not exists: ${CYAN}$INHERITS_LIST" - exit 5 - fi + # shellcheck disable=SC2086 + if ! psql $psql_args -c "$check_inherits"; then + log error "init failed: ${WHITE}one of inherits table does not exists: ${CYAN}$INHERITS_LIST" + exit 5 + fi + } # shellcheck disable=SC2086 if ! psql $psql_args -c "$(init_sql)"; then @@ -120,7 +123,6 @@ error_handler_no_db_url() { } init_sql() { - log debug "inherits: ${WHITE}${INHERITS_LIST}${NC}" local sql sql="$(printf '%s\n' \ "BEGIN;" \ @@ -138,15 +140,24 @@ init_sql() { 'END;' \ '$$ LANGUAGE plpgsql;' \ '' \ - 'CREATE SCHEMA IF NOT EXISTS hectic;' \ + 'CREATE TABLE IF NOT EXISTS hectic.version (' \ + ' name TEXT PRIMARY KEY,' \ + ' version TEXT NOT NULL,' \ + ' installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()' \ + ');' \ + '' \ + "INSERT INTO hectic.version (name, version) VALUES ('migrator', '$VERSION')" \ + '' \ 'CREATE TABLE IF NOT EXISTS hectic.migration (' \ ' id SERIAL PRIMARY KEY,' \ - ' name hectic.migration_name UNIQUE NOT NULL,'\ - ' hash hectic.sha256 UNIQUE NOT NULL,'\ + ' name hectic.migration_name UNIQUE NOT NULL,' \ + ' hash hectic.sha256 UNIQUE NOT NULL,' \ ' applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()' \ ')')" - sql="$(printf '%s INHERITS(%s);\n' "$sql" "$INHERITS_LIST")" + [ ${INHERITS_LIST+x} ] && sql="$(printf '%s INHERITS(%s)' "$sql" "$INHERITS_LIST")" + + sql="$(printf '%s;\n' "$sql")" printf '%s\n' \ "$sql" \ @@ -224,14 +235,22 @@ migrate_to() { [ "${MIGRATION_NAME+x}" ] || { log error "no migration name specified"; exit 1; } } +migration_list() { + find "$MIGRATION_DIR" -maxdepth 1 -type d -regextype posix-extended -regex '^.*/[0-9]{14}-.*$' -printf '%f\n' | sort +} + migrate() { local fs_migrations db_migrations db_migration fs_migration psql_args var #target_migration 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+x}" ] && { + log error "ambiguous migrate subcommand, decide ${WHITE}$MIGRATE_SUBCOMMAND ${NC}or ${WHITE}$1"; + exit 2 + } MIGRATE_SUBCOMMAND="$1" + shift ;; --db-url|-u) DB_URL="$2" @@ -242,7 +261,7 @@ migrate() { shift ;; --set|-v) - VARIABLE_LIST="${VARIABLE_LIST:+$VARIABLE_LIST }$2" + VARIABLE_LIST="${VARIABLE_LIST+$VARIABLE_LIST }$2" shift 2 ;; --*|-*) REMAINING_ARS="$REMAINING_ARS $(quote "$1")"; shift ;; # unknown global -> pass through @@ -252,24 +271,24 @@ migrate() { error_handler_no_db_url - [ -n "$FORCE" ] && { + [ "${FORCE+x}" ] && { log error "migrate --force not implemented" exit 1 } init - fs_migrations=$( - find "$MIGRATION_DIR" -maxdepth 1 -type d -regex '^.*/[0-9]{15}-.*$' \ - | sort \ - | xargs -n1 basename - ) + fs_migrations=$(migration_list) db_migrations=$( - psql -Atqc "SELECT name FROM hectic.migration ORDER BY name ASC" \ + psql "$DB_URL" --no-align --tuples-only --quiet \ + --command "SELECT name FROM hectic.migration ORDER BY name ASC" \ | awk NF ) + db_mig_count=$(printf '%s' "$db_migrations" | wc) + log debug "$db_mig_count" + # 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 @@ -295,7 +314,7 @@ migrate() { form_psql_args() { psql_args="-d $DB_URL -v ON_ERROR_STOP=1" - for var in $VARIABLE_LIST; do + for var in ${VARIABLE_LIST:-}; do psql_args="$psql_args -v $var" done } @@ -307,14 +326,20 @@ migrate_inner() { psql_args="$(form_psql_args)" - escaped_name=$(printf "%s" "$fs_migration" | sed "s/'/''/g") - escaped_path=$(printf "%s/%s/up.sql" "$MIGRATION_DIR" "$fs_migration" | sed "s/'/''/g") + direction=1 + mig_direction=$([ "$direction" -gt 0 ] && printf 'up.sql' || printf 'down.sql') + + escaped_name=$(printf '%s' "$fs_migration" | sed "s/'/''/g") + mig_path=$(printf '%s/%s/%s' "$MIGRATION_DIR" "$fs_migration" "$mig_direction") + escaped_path=$(printf '%s' "$mig_path" | sed "s/'/''/g") + + log trace "mig name: $escaped_name; mig path: $escaped_path" # shellcheck disable=SC2086 if ! psql $psql_args < result -exit 1 +printf '20251004192425-some-changes +20251004292448-some-changes +20251104172425-third-migration +20251104192427-an-other-one +20251104292469-almoust-last +20251204152446-very-last' > expected + +#printf 'result\n[\n%s\n]\n' "$(cat result)" +#printf 'expected\n[\n%s\n]\n' "$(cat expected)" + +diff -q result expected || { + log error "test failed: ${WHITE}unexpected result" + exit 1 +} + +log notice "test case: ${WHITE}getting list of local migrations with info" +if ! result="$(migrator list)"; then + log error "test failed: ${WHITE}error during execution" + exit 1 +fi + +printf '%s' "$result" > result + +printf '20251004192425-some-changes: missing up.sql down.sql +20251004292448-some-changes +20251104172425-third-migration: missing down.sql +20251104192427-an-other-one: missing down.sql +20251104292469-almoust-last +20251204152446-very-last' > expected + +#printf 'result\n[\n%s\n]\n' "$(cat result)" +#printf 'expected\n[\n%s\n]\n' "$(cat expected)" + +diff -q result expected || { + log error "test failed: ${WHITE}unexpected result" + exit 1 +} log notice "test passed"