feat(package): migrator: mvp
This commit is contained in:
@@ -123,7 +123,7 @@ BEGIN
|
||||
RAISE EXCEPTION 'Incampetible migrator versions: % and $VERSION', version; -- TODO(yukkop): show versions
|
||||
END IF;
|
||||
ELSE
|
||||
CREATE DOMAIN hectic.migration_name AS TEXT CHECK (VALUE ~ '^[0-9]{15}-.*');
|
||||
CREATE DOMAIN hectic.migration_name AS TEXT CHECK (VALUE ~ '^[0-9]{14}-.*');
|
||||
CREATE DOMAIN hectic.sha256 AS CHAR(64) CHECK (VALUE ~ '^[0-9a-f]{64}$');
|
||||
|
||||
CREATE FUNCTION hectic.sha256_lower() RETURNS trigger AS \$fn$
|
||||
@@ -180,13 +180,34 @@ migrate_down() {
|
||||
exit 1;
|
||||
;;
|
||||
*)
|
||||
DOWN_NUMBER=$2
|
||||
shift 2;
|
||||
DOWN_NUMBER=$1
|
||||
shift;
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
: "$DOWN_NUMBER"
|
||||
# Calculate target migration: current - DOWN_NUMBER
|
||||
if [ -z "$db_migrations" ]; then
|
||||
log error "cannot migrate down: no migrations applied"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_migration=$(printf '%s\n' "$db_migrations" | tail -n1)
|
||||
current_idx=$(index_of "$fs_migrations" "$current_migration")
|
||||
target_line=$((current_idx - DOWN_NUMBER))
|
||||
|
||||
if [ "$target_line" -lt 0 ]; then
|
||||
log error "cannot migrate down $DOWN_NUMBER step(s): would go before first migration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# target_line of 0 means migrate down to nothing (revert all)
|
||||
if [ "$target_line" -eq 0 ]; then
|
||||
printf ''
|
||||
else
|
||||
target_migration=$(printf '%s' "$fs_migrations" | sed -n "${target_line}p")
|
||||
printf '%s' "$target_migration"
|
||||
fi
|
||||
}
|
||||
|
||||
migrate_up() {
|
||||
@@ -202,14 +223,29 @@ migrate_up() {
|
||||
exit 1;
|
||||
;;
|
||||
*)
|
||||
UP_NUMBER=$2
|
||||
shift 2;
|
||||
UP_NUMBER=$1
|
||||
shift;
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
: "$UP_NUMBER"
|
||||
#ls "$MIGRATION_DIR" -1 | sort
|
||||
# Calculate target migration: current + UP_NUMBER
|
||||
if [ -z "$db_migrations" ]; then
|
||||
target_line=$UP_NUMBER
|
||||
else
|
||||
current_migration=$(printf '%s\n' "$db_migrations" | tail -n1)
|
||||
current_idx=$(index_of "$fs_migrations" "$current_migration")
|
||||
target_line=$((current_idx + UP_NUMBER))
|
||||
fi
|
||||
|
||||
target_migration=$(printf '%s' "$fs_migrations" | sed -n "${target_line}p")
|
||||
|
||||
if [ -z "$target_migration" ]; then
|
||||
log error "cannot migrate up $UP_NUMBER step(s): not enough migrations"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s' "$target_migration"
|
||||
}
|
||||
|
||||
migrate_to() {
|
||||
@@ -339,24 +375,107 @@ migrate() {
|
||||
log debug "[$WHITE$fs_migrations$NC]"
|
||||
log debug "$target_migration"
|
||||
|
||||
target_idx=$(index_of "$fs_migrations" "$target_migration")
|
||||
if [ -z "$target_migration" ]; then
|
||||
target_idx=0
|
||||
else
|
||||
target_idx=$(index_of "$fs_migrations" "$target_migration")
|
||||
fi
|
||||
|
||||
log debug "indexes $WHITE$current_idx$NC $WHITE${target_idx}"
|
||||
|
||||
if [ "$target_idx" -eq "$current_idx" ]; then
|
||||
log notice "database already at ${WHITE}$target_migration${NC}"
|
||||
if [ "$target_idx" -eq 0 ]; then
|
||||
log notice "database already at clean state (no migrations)"
|
||||
else
|
||||
log notice "database already at ${WHITE}$target_migration${NC}"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Apply migrations
|
||||
psql_args="$(form_psql_args)"
|
||||
|
||||
if [ "$target_idx" -gt "$current_idx" ]; then
|
||||
# Migrate UP
|
||||
log info "migrating up from index $current_idx to $target_idx"
|
||||
|
||||
i=$((current_idx + 1))
|
||||
while [ "$i" -le "$target_idx" ]; do
|
||||
fs_migration=$(printf '%s' "$fs_migrations" | sed -n "${i}p")
|
||||
|
||||
escaped_name=$(printf '%s' "$fs_migration" | sed "s/'/''/g")
|
||||
mig_path="$MIGRATION_DIR/$fs_migration/up.sql"
|
||||
escaped_path=$(printf '%s' "$mig_path" | sed "s/'/''/g")
|
||||
|
||||
if [ ! -f "$mig_path" ]; then
|
||||
log error "migration file not found: ${WHITE}$mig_path${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mig_hash=$(sha256sum "$mig_path")
|
||||
log info "applying migration ${WHITE}$fs_migration${NC} (up)"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args "$DB_URL" <<SQL
|
||||
BEGIN;
|
||||
\i '$escaped_path'
|
||||
INSERT INTO hectic.migration (name, hash) VALUES ('$escaped_name', '$mig_hash');
|
||||
COMMIT;
|
||||
SQL
|
||||
then
|
||||
log error "migration failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
log notice "successfully migrated to ${WHITE}$target_migration${NC}"
|
||||
|
||||
elif [ "$target_idx" -lt "$current_idx" ]; then
|
||||
# Migrate DOWN
|
||||
log info "migrating down from index $current_idx to $target_idx"
|
||||
|
||||
i=$current_idx
|
||||
while [ "$i" -gt "$target_idx" ]; do
|
||||
fs_migration=$(printf '%s' "$fs_migrations" | sed -n "${i}p")
|
||||
|
||||
escaped_name=$(printf '%s' "$fs_migration" | sed "s/'/''/g")
|
||||
mig_path="$MIGRATION_DIR/$fs_migration/down.sql"
|
||||
escaped_path=$(printf '%s' "$mig_path" | sed "s/'/''/g")
|
||||
|
||||
if [ ! -f "$mig_path" ]; then
|
||||
log error "migration file not found: ${WHITE}$mig_path${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "reverting migration ${WHITE}$fs_migration${NC} (down)"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args "$DB_URL" <<SQL
|
||||
BEGIN;
|
||||
\i '$escaped_path'
|
||||
DELETE FROM hectic.migration WHERE name = '$escaped_name';
|
||||
COMMIT;
|
||||
SQL
|
||||
then
|
||||
log error "migration rollback failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
i=$((i - 1))
|
||||
done
|
||||
|
||||
if [ "$target_idx" -eq 0 ]; then
|
||||
log notice "successfully migrated down to clean state"
|
||||
else
|
||||
log notice "successfully migrated down to ${WHITE}$target_migration${NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
form_psql_args() {
|
||||
psql_args="-d $DB_URL -v ON_ERROR_STOP=1"
|
||||
for var in ${VARIABLE_LIST:-}; do
|
||||
psql_args="$psql_args -v $var"
|
||||
done
|
||||
}
|
||||
|
||||
migrate_inner() {
|
||||
# depricated, rewrite
|
||||
printf '%s\n' "$fs_migrations" | while IFS= read -r fs_migration; do
|
||||
# skip already applied migrations
|
||||
printf '%s' "$db_migrations" | grep -qxF "$fs_migration" && continue
|
||||
@@ -386,6 +505,13 @@ SQL
|
||||
done
|
||||
}
|
||||
|
||||
form_psql_args() {
|
||||
psql_args="-d $DB_URL -v ON_ERROR_STOP=1"
|
||||
for var in ${VARIABLE_LIST:-}; do
|
||||
psql_args="$psql_args -v $var"
|
||||
done
|
||||
}
|
||||
|
||||
create() {
|
||||
local time_stamp name file_name file_path
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
config: ENC[AES256_GCM,data:7VLrHNSEA/ggR51z1CGK6w9X38d1Ny2fXaU7R3S/GOHR9JL5Jgxi42KEQhYfrti8iqasFXqfyqSvDxFCxz4RL97Npb1KFCLc3BJf6UUNJPV5I6skOR+S/WpzWrbabO9Jchcm1iZLiT6qBuB69ECYUck0X6UVLQrz0WmLBIK+P1tN3C0BywjjHcfuLz/+dZAuj51JpcFqlGGhPB4MOcLl/xgduBE+5yETVYpQfjSzQH6DHV1IaR0E8jL/7V8UV/LjUUEVHbSqZ08IkWUzwjTy+nX4u960vkWQlm4TAsJd72g5rNIfSLVcU4REOUK/7mr/2nAOrho0iWUIyq2PNIFbtnSLUdYDaZ6XD953ey5wPxGrUsSEfJyoOEPF98nByKQZkCtwQ2sax94SbfVaZQWw47XU+zzbd/+3zGfX0pHD4n74RIniwtGSuSXvRSRyKY+M8t9l2MoRp692vm3GO1eEFLZ0kjIi+iFkcB32Ud+3Pa4ThOlNQs/fFPBQsPg17oJ0W6bzZRS6flFo+aIUAX0+as8Vg1MbJYifQiWiDR5xyVE/dyZ+o74QdASgvhqsTVll8Thn5doVXw37hfgYLcnpBq18GrMt/Hg94vElyINOvXDBoXyJOV6pm8QwyPRihF+DGwtGrgbNu1WGJqpp0LB7AlRs4wE1F9BRV//evB0dp0N7Cy4PVMWTbFdKyMpPHs1HvN/6Ju9Y4DQGywZD819EbnupeE/ONT8QZBO99GqQPDyh1QKVAwDgyEgvCXDSfljtMFodrwxOT5X7vUmWsTRjogJ7OcUZXKospD1Kv1LUKRk3SydVsKIeZLXWHHRHTsKVjAHioihOwmVPJUg885+fcIO3uM5pujGbgFlCgllEEuNeiDTJHDFHVIje5fs6C6UYG/C45TNqftMWhojQeBpYHoKUcBTy3ZJxovUl04tkRyGf7DqYqFAVGntdFLbct7k6BofZUEpowcBChi2Rz4EgLd5mOBU1BVbQyPQ683eRYJ2QilSDSMD6l0r9G8keS7v8su1oqUs6JJZQXUYsV9Pdrz9N6z829lpLcqNs3ZWTgT0fCTmBrcMqft8qm/Vjl61wcc6+yC9S0jupVanaEOoTFwWi0u+ZYlp/EUG786iH2lOabMMwCvgLOM76E8z5dVOFzF1kpvwCOVW8ZJfj3XCqWgRQovHtmb257+sOS7fPRCc9xhmtu8GzbLsKnTXGFhDH3iPsvhyI1qIhJyQSm3QBFVPOWo8k4/A3IMGKMmF8Mu+EGhN2cEk3pcB+8vu4mHf4GZjH7P/hYH3oPCKHP5ST9wvmb6j4LzRE6sjxBig3ZuttV5sC/ZzzrYWf0jNFZYHTarx8Xs10AtLcb9zVUtQ4VSRNS7zrEbaW1dY8UFVwiECgR1J6/h7C97ge9PVM2D6Vgu3c5cQQmK1O+7t/ZXX263OymG7Oi8zJsQfaT3lW+YewuWQtM3+6qBzyi+mi9h9Fl3mx+XZECbqAaGDk3cYcZscDeHMvBkBOO4zsynoD3jWE7hnQkiOquGHdP9s7PYokGRtZ7XvpH1HzXxgVWuXO0ZtM0LertgV32vtv1s8zbi+Q5gjeCEEaS9h+C20AnCVXzViU88D8T0+umhZz76YK2WV5Io/jDGkI6RU+aohxY6Fn2pD1T0wRfQ0PzYCRpaBS5R6b0vw1JQhe70OZLVRGXfwQ/YV2hc0LRYvMUBpQ1eCEwH0snJ570Hdy3Ipe36QcyNxmAm1rGUT0fUKXbK/Nk/zq3fD8qyqlnD94URh7m8oNDU/KtU507QPAhsLgDa2bB477Nv4DCPb1oOOmVMd6RZ5QvpU2l3ZEeD5Xajh+9Mc/C91+uODNGxO06HsfKgYmG8FP8/5KGvFYmaXNjXVo64vNnQWniCu+dTGfA4lefFYtwoEQ2wKQYVmyN4XUIRKNT8us8IwcG+cUj4otQy8sDVlV3OsOIxZJkzqKLb/Qo/al+TmHxrDQAN38T95bGTCnr2ox5hRvWjhT1t8TZCJHAhVwDYggXqvQYDsK9sYrBImYh5HMHFDM/XTDorNWxPwcF21ALqaiMvnoUiWmoGze0HTT4y8TgQ20EKZpJWCKooisqTtkkTm895T+WXpJ+wnNTSq49GldWCz7fzSJZqnxBKx0OUoiW2XIaj9OdgYsnM3xr7HSY9jtGtmKQ9TWcqov5VHD0VL5F0yewfDOZKR+Wqfo4yumAsJoYv9JHwKUjBY3GMA3bAZFNTkHWqHKLlapo6JBiAVTvpNzhzuvyZKeYkyKmB3uwhpDz40GGXwj1e1ojXQTzPWa0OlE47QyVefsFQAGMvFXGrml7WJN5xbvd8yolMmiVmbNGdosCnSV4sOjoMKJdXtcnm/FnqxSVhVldI7df7m7tFmrUDHo22/KCrb3bM309c2Kiqj/QbCdAm9IJ1p3XlKZiENAv2rONzDcSuFekNA5WY60CtekxiIWoZiZEo+K589A078OU7p8pRTN9J+wrImYjvGocIJ62xgTjsnYGny6kJT5+wHPrmopK8prTEyzWeFNzFy/DyIRq63PpyEX8fxskFg/fElfZsL1kEx8UJ+eVUt9QnYhF1sArRnR351zC2h2B91GiidaCRYxvmzhQshrb8zOzWxeD04BgYVDlDKw+Cm39wIlLwniHhrRrvXzmPd1JfXLegQAdY1FfgLCpVMsCt+QWAS212k3Pul/2XyHqdBzOfSj6WxUTiO7mUAc6LzawxE9K6AbRprqsbF8cSHeEegg1zcR7mh1dQwb3UYpGR6f4Iuje1ybzw5fSniBLOuI67WdVu0JKo272hnuaHgiozh+wEmCyJ5uzezXk/LBLOCBg/nP2hXWdoQQNMu1/OoSyXGzNbcQYQSMPnqw+NL0iTUEA3XwMOrZO5/vdoRK4pCbwDrM8kBd0bLjs45Kgi7E0rWkGvsganXyGoFieUbdk//xfT6giqwFvXNp56E3OrylU2dgwwoeT5se7Mv/GEe73+e35ERicNaKNUK8OQOpH9uBQ7S5OP0UaSTcm0EWs6Wm6N8VSUVojPhsBfWlR4YVJUtqOby167JioVS6ngE6lbwZvVxSp/nhlnaxypZx/NeDbWYrhNStKqvAQeWNnMgaA1ajvFYCKdoB4yYyhUM3HW40Tt5L7LxXEjpkeruh1sEy8ogn0VXq+0dafoNnPmQ9daUK+f+IEG4BpNt5gTj/3cloFcfwvD3sQxqBVjCdUV8DDsv0dHGtVskjDi3AT2UNCBcp9fiL7UFxjzr/JcCogmpaYpu3zEYkBZ/4oyMazbDrPNghtqq4KWCN9YG2lqD+eHMwqXAOgScagHwwNDsy/Hdkco2lMrIPk/v2OfRFwZ19mJ5DwvWMVjjDAp/waanuKVVZZnLOiMvlteE1MjX33Ml0W6xjIenjHxNhGpPyk2bzYVxk+YwUbjgJQwNlw5UM7UPSlROJ12UytfG5CynwFdHYQ6z5SA5MojlOyaKxcjJ4H6m2bXzA67dAHi1Twso+9muRF/M9JJQsLAJQIof244tusQcmKuQ64UgiLTVf92t8wvWaDJ7ruhiXPg5Y2/TrOnDWcFWxt3akn5iJDcgcmsm3Ws4poCpZF3n5BSZpRAmWVhTRPbCwBXEwl6ObR8D/E4geGgFfrAHRNcoAzf5jumT/YgjI51VgABm/PF64WQ7f6kB1sCnfmu746vn513nHLugUFBMcvH1CHuUt173KuBr/aPiX5w0iuejaMV4sDUeFNjseVftwKuC9DG6EE6xgT2D8FRB3X51z3kTEqwOoQa6avm1UIZrSJgKeEClb1JIw8BW9nDyD2eIl6syvhYlj03PGUm7EW32dwZW6blLrPickubSYgc1Mq2jodhcIb7RvJY8m6JRF/Zx+nzClPamIl+8iAnxajMIx/Y5iPFTN8RE9f8cIcGahvuwaZaAHOZGAqZZF7m4xqZ77eKYj8GKvEYmofGgYbj1uVDWGtCfy6vGefilQcK4LjY/R5WM/+8xJEhPgWkE5Del82L7ZK1Wjsouk7ZzKcKviWw==,iv:CA7shi2L2Q3gzX5Wr8+X8IKdz/s02oSiROwxdtFmTPs=,tag:M96/xsztbbq+u4pUDYyqMw==,type:str]
|
||||
config: ENC[AES256_GCM,data:DUWHlnkNo+6Z0frTeVXhCYWQqMr6TbdK9Xy9iOb8y3LNOdh53p3d0b4FC5mlpsahJY74cDjZzMc6IU70CGpUtEuXUnfnTsjwhQoQVmZtEsTbplNVp7ND0KUH9X6dAuaO6cV/1lb8QsWBrcqDZVrjL9IXVmfKSuWxjk4lCzpcsIxAFTLP7dT5DQ8mmLOBFZ5bwZ1YuEJCPxkiN3Qf9L2zGij/dxBkcc3LmJP4K/zQDiACgFMfTo1FludRKQZdyiLoTS27wLGJEhSw9Xx8fWJ4VaEi0iByTzeaP7rq4HBiZ/caTKU5nVDGgcMG5Jivl7tybt4kLpVWbC9WHPVFmr3tEo/JnipxikE7VQ9zqUl52LZwvYWuPaPpLBkRK9OkqIob2S8BTC6B2fFuSkgQ+oVnot4nykIzqiU5/IPoBUlzFKy8XV6u3EALsW28oxoACq/VgM2vl+Lp2RLhliTkP6Opyn6hJcl7XbWyCKUaHN+VPltUqtCo5ckOBp8ddBOUScBHnkHIB2jaVvs0faeH7UE3TFu+7MPGEzinIAgiIZFDs2gWPYYOdIcfTVBlTTNfrSrXqhuUvsrrLgWHOgoZrw5JSM4cgh05N+ETvMTwZbBOtwxfo3NnMBzv8fg8725/MI2H0ekiZOCoY8Dz+kAQ6BJx3KeiaYJHePbvBhAWKvF/J3V4kA+SGuuROg2Tqs9JkRezPiUEBpMzW6r2IcqsKw/29efqr9+AMs3DTl8upi7vQBhVBCVK4JHSjHUyUvKaqtxTrNSh9yEhXSohwa/9LI0IB3CwlsHrfRhdMm6w2wr8IAZQizwD7MBDAZey5VUytkveFT7YBdYW0vzI6COnsoZQFxA6aS/O5biGXv3vsCtzVn8FmDUXR2lIu3++1Aa+/2EAb3HtdYpvFLiBZUCbzbSkJaEcQ85ZxK/uE2ylhfQrxxd4P7mAjSuq8nF1oLP81bRKYz8EO8rNX0aMskcdUQpQHIsjdjnL0H14SMohzognUBUD/c5fKc9EDeRcx93GZ6awp6OF1d+FqDhcGnY6UGPcbIqbU6FKvKHiIs5pSXsSxrDdLgQ41jUmiTbqHUWKL8aeHMqw0ngfDaM/bWk5WQ/XU8XNnWRZNGIDs6AFCxmISix9oeIyrwynt/slV65HVc62kTfvkheFs+UAIZLFHtXZyoaAq9xXVVCfhnQcX+LTJxLLBgdlcFdwNZtV3ywdOZV83OplcnXZw1Pgv5uZn0CKBxWFpBDW0hEmghjCE9e4fdgzbTcO9wCGEgW71FyX0hYi4V5EWWDWYLob/o9wcpAXz/N6I7yPgJob+D/VeO/Bk43VT98cUHPFHOGwxYETzuM+Z86ljGinS2LR5tHQ2+3wzVVG6XnoFCNySiSw9cqw9Lq3fDgNwhWJA+WlT3X+OkzRUMkWTKU91UgepCNXdyMs0M3Ti4AqVFHG8DyXThLiAz9lZWLlA369TZov+xPvlWI4RvbEOXbCJPEhaZR8ZL8wHbhKkyMSwRvteuo5nKj/jdy3XvqN9XPr94JKsDpEVEOhmxMQyW31pJsxFyS+wdlM3M49hGUrZRDKeaWwWOl27Q19otNh6YRFkt7TGJ6fGSiWbxRlwB+HZ7ezZXQj+VKQPnQa8/gLzVLTVyJY9P1n81/vetwMi4Sw7AevccakpERM1EyslK93K3K4/cKjEJGZ2+EznLalQAPSibsl0nQbx8mFcbFEFXY7OjOBIiGdL8IEet+GmAnx0u8i1lsgzAz/9rC71q3B5pW1E6ahKaP03qMQHXf0/6bJlYKGtQAPs3+ORSf/dGiiqJ0B8SWMNIfZVp28I9zFddd6vQ0oOuIDauvIraHZJWPzxwaIJCjMdmJub/7DTxF1AsmNpmexQX/jMgHvdrJKAaxg4Rwo52uCgL+lPlcSMZMFTfinymzj5CohzwG/711no2nAjZmBaOjQkdZqMUr29N4rJ19YAFYyYgCVMOyKMRKuOCjoDeodvC7zFMcMFhnS+3fna9cnlM98ks0bY1G9ZF530GuP8yjLSUBvpXSrkrJIkGeUDWQFMDguUNwf1U67t1olxNdjaqMqIfkHUAQaEvdj7aD13JqrTLpEAT7E2mxAaaDv7fyrcU1dR6/g0S2zClW3XkxxfelU/n2kPA9r410L47coUZIJpQUzfIdpLxFIXuwkOuiCdei0Dk/G9JMn09RMUz/M+QpLKptn55Mjqdx+WUWtCjvxURiXHAZudjWRehSMKmdFzJ/3OPsWZ6Mny9bGre1rnBYolRFgz8OYYjEkS8et7wAuVVYwpkK+XZ1vbzwgA3j9S+43Au0+bgMxw1OKeAczwIp7FkqPPnVNUCJeeWDpDdjUeZuBjwAA3NQPPxzaduGyoSmmIyhApFhfr1Xz61gprKkXRQKTsjQGtida3uTFhbuAX7nZITl0ElBprN4K2NhF6am1fsNteDx8rSKBzEkcduQ/yxNLB3PI9ign4cU604WT0hLwqvnm3uFVnhSSsJxL8nOCKvrLRYa0wj+oUY5HOVBeXnxs62MAE+g0oMd68oT2eFHUPvpjeJg3k/Q8npDxRCBmDGlpwG2yql+uWuSviwcbvB5u7gGFKlrJ3pX9aR6+IVmHr4vcypsIhIX90vMFyGy+h372PTBJwcQf6o1DzfCKpz4YJmr7Z6SIt/+kBIat8x5vnAWjdaiXob4K+IFNFXEjrlVYC279583KhOKdMcE0Y0Ky3LR2lzl/JmEx6cexOsin0B+p7Z8E7k+7fIT/YSlkY75jqGPvjQHEDNBwYsbhguzr9gmcEYhJz+LVOrJ/4fEHEuYqeOcC7V9DqrZIC7GH0xbqI6blxKeJFll2A9eTLEKEgkXfJVv3iMJeTz2J3sq4gme9z0MKK4aksztq0gXJXUFjOr/XZ0OGmPjQ0CdeW2h/OdP9Y8y1HEi+yI4o+pdt88hSqeUdgCAKh2toInVcAnODVj1r+UqP0h0RMr10ZEQsx0SF24Sj9YfCYsCiS5hxXUzG45UR6I2+WlgioObU3sdjapF8p+wyGL07tFvymkwAd2T0KH3E4pqEwum4X32ZRxAVKMkyW0DB1LNoFRs/Zwxrl7Q8gJTELSP5YzG3TYaPVj4onKHcTE467duZ9hfTQDQMU5I5crePxYh7+F7sigxxiascWsNKO61pT7Rfixg7q/63bFDM35iFK/OZ4/3E+raJ67KBAwRjUxYBIlaerobJ4rYdxe9tpEIB9S/CMZWHNd5ixegkR/5CYsl2VNWhavJ9o9drBx2TnxOcifralYD8OFZcYVWvIJB+T263iUiIp/QKItjusWjliIddwQtQkTKjxO7QxnHt1/pquwf/in2a8xgRfj0q2gi8weo0xQb0fT3Q99i7L+LLGklnfyVfJWB5gbd0Yrr0Fwgz+qx9hNiK1UqyjfpIdiFy+Ga2rzvbOSQaSip8+H6siSy55v5ntRT3n5WgSsbEOLSRWQyX2XFNG0JPPcDm75qICV82eMFgcYCtVhw4PpOAgqxSNKoqUVLIp6rArN1ZRacs5G0nnZER9IBOwzZ4bFt6YtnxUXHDIFjEVFtJqw3d95lTyV4ewnlZGPBvDfj2sEsb6rRYqjNDMR+e4fzGhJuY4ZE/bhpkejCYcjVrhqCL7dG+Rxhm/Iy4vpCDBuGWWCrfClHNJ0inGU6oOVI19qFSBESThtsG8JD753h7DfRTTeE+oWi7RXrDEvpcsYB8nuLhSFG/YM4Jv2mPRTQbd8MD6IRo7iabH7DeJavSaL1q9nW5bBoZNYP6pS+komZVZnp1Njd4Wff7Cx4iF6p0y5PwAP1dxEdO/arD4/X2j7gouVUCogpKx0ikS/vJOAd69oZvxnSvj59f2EBb9UrzY7zindvM3wyf+MVbQ9W+jypviwRlL4LkueCTbi+VlfZMcbpCmHyZ2l4hDx3w6TGMi5bUfwmyLz08cXNTDRhufzuwSvTXt2dRvF/9tIJBuQwOkG5VWnNiRfAA34myVsvqEeyCddWH4S7mHxfPVp8QqvbjxrtJA5VSQDWZeBf4ZLvM9RhqBIqUTnseKnFRl1sklnadj/iDOwqGMRxg9fOJKB33Zj8wlnB4nh0tx3L8aNl5qx1MKyy2zCrqhaHkXCdN4xUGbsKkIodK60/WbH39B46IWRIQxeq4U5rYG6UQGBjH6H8EfM9qw0YakNjMIQFXnz5SmiONzQ==,iv:kTSoTUSIk3epShN5zcMLn2LaD7i9uIcf/cOymnNezH4=,tag:hef7zLN7gosc8LzxuroZJg==,type:str]
|
||||
init-postgresql: ENC[AES256_GCM,data:4RGUfJLnYd0C0rGwa11DdreQFly1bcmAv728hv4QGzRRxcrka9GkOubPZFLZCZ8icoydotOckHK7caXK3Hg=,iv:T66gCmgEn94ydApfAs6eK/5FWlzXs3QmOYUQKbhllWI=,tag:9ZcOB/e0MGjjhXWbEpKGYg==,type:str]
|
||||
matrix:
|
||||
secrets: ENC[AES256_GCM,data:fLct3DBfxjebuuYv4IVhGXWx/G6EjQdQNBzFSq2ek8TRfv5XzpknwNeMXBE6eeNgqJeJT9K1MAHrxeChOlkAe0U4hwP/jv3yPo+cFMn9oA7fqXIjYtquRW7oMypHMjpO4X8B1DFkCyVZ0G8p/++UASmiG5bpQcWmUGSrgvJv6WZw+UiFUj0LE2MdZ2MHRv+S3wWtsUqmUCXWGSxuJXOmoLmkYLWBiX5+AkZ8kPTzhVUC7Gq72UngQTNW69uvnoZs/EgrtLSVhRe8YnC/0qu6gRtRXO55Unk9ETv1wDi+GsF4orJgPhLXw2fObomnzgNx4Vd8w+aqCxVUFQ8kwChKe+9/eMGg6lCHqV8i3bhZpTCHObN0rXvBnT9xUm6EoNCmD8V2YZdDBdLSQCuiUW0uSvmEn5yvv1AQYBX/t7Z78YuGrEzSz008akytjdSV8s7xNzks/wPa,iv:5OsDc68VovS1Er5YI7Q7m0K8yLoadkdS7B7pDrivsLI=,tag:jy8NIXaKC6VvJgxWJaeNXg==,type:str]
|
||||
@@ -32,7 +32,7 @@ sops:
|
||||
dFh2anpQTTBpVDdCd3hIYmJLMmpVM0kKvuWuryBpHTpsn9eq6MosafVH0m2KTmql
|
||||
xzxUibPr2BmeR4QAB+pYLqTBH1+N9atGYdLe5qe7GqEmcjq8IfJnBw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-12-07T12:47:45Z"
|
||||
mac: ENC[AES256_GCM,data:Pj5lXfZZq231LhRWyVUd9pI4AqNf8PsT1CJvncgBcN6ulwlwU62Ru5127zoo4KMWnkuD+gkY0/8XvcfK1OjMZy0KiKvldZ/AfNnf1jj6HoiqsfCQjUVDx4CkHwkzQmWIUQwyIgeCCimpT6PADti2I7AKKlHXcHVnDsXD+/U4yCw=,iv:CNIce+5KzMeRPS9lLdyb26R7qsrE4lL4eBUrxzob+T8=,tag:CSrJUGItRw6BQLRfrxMWBw==,type:str]
|
||||
lastmodified: "2025-12-16T11:55:38Z"
|
||||
mac: ENC[AES256_GCM,data:M10MqlFU2vwPMOHghGr6Ob29GvnKHUplAjaAniLEKiiCV54pZnVzL63rTQJW3hyHAHGWmGbXDFHzUdO3Z2xQhWRw7uWd7JMnmlm+uTsel6scvu9fovVJJA3Dr3IhZF8R3WIanS9z+0VfBzrG1xTngp0Jg219vnxgyLTRWRB0rEw=,iv:X3W4d1gT7eKp76aveiphMKbnHmADAtplDyGi4rbKGgQ=,tag:GXXVpUU7nintty/zfrFZEw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.10.2
|
||||
|
||||
@@ -25,7 +25,7 @@ json_diff() {
|
||||
}
|
||||
|
||||
# run test
|
||||
mkdir './test'
|
||||
cp -r "$test"/* './test/'
|
||||
cd './test'
|
||||
. './run.sh'
|
||||
#mkdir './test'
|
||||
#cp -r "$test"/* './test/'
|
||||
#cd './test'
|
||||
#. './run.sh'
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE tags DROP COLUMN name;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE tags ADD COLUMN name TEXT;
|
||||
|
||||
47
test/package/migrator/test/migrate-already-at-target/run.sh
Normal file
47
test/package/migrator/test/migrate-already-at-target/run.sh
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-already-at-target
|
||||
|
||||
log notice "test case: ${WHITE}migrate when already at target"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE tags (id INTEGER PRIMARY KEY)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply migration
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}first migrate up failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try to migrate to same position (should be no-op)
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate to 20250101000001-add-name; then
|
||||
log error "test failed: ${WHITE}migrate to same position failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify still only 1 migration
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try migrate up when no more migrations available
|
||||
set +e
|
||||
migrator --db-url "$DATABASE_URL" migrate up
|
||||
exit_code=$?
|
||||
set -e
|
||||
|
||||
if [ "$exit_code" = "0" ]; then
|
||||
log error "test failed: ${WHITE}should error when no migrations left"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders DROP COLUMN user_id;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders ADD COLUMN user_id INTEGER;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders DROP COLUMN total;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders ADD COLUMN total DECIMAL(10,2);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders DROP COLUMN status;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders ADD COLUMN status TEXT;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders DROP COLUMN created_at;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE orders ADD COLUMN created_at TIMESTAMPTZ;
|
||||
|
||||
53
test/package/migrator/test/migrate-down-multiple/run.sh
Normal file
53
test/package/migrator/test/migrate-down-multiple/run.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-down-multiple
|
||||
|
||||
log notice "test case: ${WHITE}migrate down multiple steps"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE orders (id INTEGER PRIMARY KEY)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply 4 migrations
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up 4; then
|
||||
log error "test failed: ${WHITE}migrate up failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify all columns exist
|
||||
if ! psql -Atc "SELECT user_id, total, status, created_at FROM orders LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}not all columns added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Migrate down 3 steps (should leave only first migration)
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate down 3; then
|
||||
log error "test failed: ${WHITE}migrate down 3 failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only 1 migration remains
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only user_id column remains
|
||||
if ! psql -Atc "SELECT user_id FROM orders LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}user_id should still exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if psql -Atc "SELECT total FROM orders LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}total column should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE products DROP COLUMN price;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE products ADD COLUMN price DECIMAL(10,2);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE products DROP COLUMN description;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE products ADD COLUMN description TEXT;
|
||||
|
||||
53
test/package/migrator/test/migrate-down-single/run.sh
Normal file
53
test/package/migrator/test/migrate-down-single/run.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-down-single
|
||||
|
||||
log notice "test case: ${WHITE}migrate down single step"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply 2 migrations
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up 2; then
|
||||
log error "test failed: ${WHITE}migrate up failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify both columns exist
|
||||
if ! psql -Atc "SELECT price, description FROM products LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}columns not added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Migrate down one step
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate down; then
|
||||
log error "test failed: ${WHITE}migrate down failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only 1 migration remains
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify description column was removed but price remains
|
||||
if psql -Atc "SELECT description FROM products LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}description column should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! psql -Atc "SELECT price FROM products LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}price column should still exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Remove transformed columns (original data preserved)
|
||||
ALTER TABLE products DROP COLUMN price_display;
|
||||
ALTER TABLE products DROP COLUMN price_dollars;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Add new columns and transform existing data
|
||||
ALTER TABLE products ADD COLUMN price_dollars DECIMAL(10,2);
|
||||
ALTER TABLE products ADD COLUMN price_display TEXT;
|
||||
|
||||
-- Transform existing data
|
||||
UPDATE products
|
||||
SET
|
||||
price_dollars = price_cents::DECIMAL / 100,
|
||||
price_display = '$' || to_char(price_cents::DECIMAL / 100, 'FM999999999.00');
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-existing-data-migration
|
||||
|
||||
log notice "test case: ${WHITE}data migration on existing populated table"
|
||||
|
||||
# Create existing table with data
|
||||
log info "creating existing table with data"
|
||||
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<SQL
|
||||
CREATE TABLE products (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
price_cents INTEGER NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO products (name, price_cents) VALUES
|
||||
('Widget', 1000),
|
||||
('Gadget', 2500),
|
||||
('Gizmo', 500);
|
||||
SQL
|
||||
|
||||
# Verify initial data
|
||||
product_count=$(psql -Atc "SELECT COUNT(*) FROM products" "$DATABASE_URL")
|
||||
if [ "$product_count" != "3" ]; then
|
||||
log error "test failed: ${WHITE}initial data not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply migration that does data transformation
|
||||
log info "applying data migration"
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}data migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify new columns exist
|
||||
if ! psql -Atc "SELECT price_dollars, price_display FROM products LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}new columns not added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify data was transformed correctly
|
||||
widget_price=$(psql -Atc "SELECT price_dollars FROM products WHERE name = 'Widget'" "$DATABASE_URL")
|
||||
widget_display=$(psql -Atc "SELECT price_display FROM products WHERE name = 'Widget'" "$DATABASE_URL")
|
||||
|
||||
if [ "$widget_price" != "10.00" ]; then
|
||||
log error "test failed: ${WHITE}price_dollars not calculated correctly, got: $widget_price"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$widget_display" != "\$10.00" ]; then
|
||||
log error "test failed: ${WHITE}price_display not formatted correctly, got: $widget_display"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "data transformation successful"
|
||||
|
||||
# Verify all products were transformed
|
||||
transformed_count=$(psql -Atc "SELECT COUNT(*) FROM products WHERE price_dollars IS NOT NULL" "$DATABASE_URL")
|
||||
if [ "$transformed_count" != "3" ]; then
|
||||
log error "test failed: ${WHITE}not all products transformed, got: $transformed_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test rollback of data migration
|
||||
log info "rolling back data migration"
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate down; then
|
||||
log error "test failed: ${WHITE}rollback failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify columns removed
|
||||
if psql -Atc "SELECT price_dollars FROM products LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}columns should be removed after rollback"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify original data still intact
|
||||
original_count=$(psql -Atc "SELECT COUNT(*) FROM products WHERE price_cents IS NOT NULL" "$DATABASE_URL")
|
||||
if [ "$original_count" != "3" ]; then
|
||||
log error "test failed: ${WHITE}original data corrupted after rollback"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
widget_original=$(psql -Atc "SELECT price_cents FROM products WHERE name = 'Widget'" "$DATABASE_URL")
|
||||
if [ "$widget_original" != "1000" ]; then
|
||||
log error "test failed: ${WHITE}original price data corrupted, got: $widget_original"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed: data migration works on existing populated tables"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Remove column from existing table
|
||||
ALTER TABLE users DROP COLUMN bio;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add column to existing table
|
||||
ALTER TABLE users ADD COLUMN bio TEXT;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Remove new table
|
||||
DROP TABLE comments;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Add new table that references existing tables
|
||||
CREATE TABLE comments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
151
test/package/migrator/test/migrate-existing-database/run.sh
Normal file
151
test/package/migrator/test/migrate-existing-database/run.sh
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-existing-database
|
||||
|
||||
log notice "test case: ${WHITE}add migrator to existing database with data"
|
||||
|
||||
# Simulate existing database with tables and data
|
||||
log info "creating existing database schema and data"
|
||||
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<SQL
|
||||
-- Existing tables from before migrator
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE posts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
title TEXT NOT NULL,
|
||||
content TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Insert existing data
|
||||
INSERT INTO users (username, email) VALUES
|
||||
('alice', 'alice@example.com'),
|
||||
('bob', 'bob@example.com'),
|
||||
('charlie', 'charlie@example.com');
|
||||
|
||||
INSERT INTO posts (user_id, title, content) VALUES
|
||||
(1, 'First Post', 'Hello World'),
|
||||
(1, 'Second Post', 'More content'),
|
||||
(2, 'Bob Post', 'Bob content');
|
||||
SQL
|
||||
|
||||
# Verify existing data
|
||||
user_count=$(psql -Atc "SELECT COUNT(*) FROM users" "$DATABASE_URL")
|
||||
post_count=$(psql -Atc "SELECT COUNT(*) FROM posts" "$DATABASE_URL")
|
||||
|
||||
if [ "$user_count" != "3" ] || [ "$post_count" != "3" ]; then
|
||||
log error "test failed: ${WHITE}existing data not created properly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "existing database has $user_count users and $post_count posts"
|
||||
|
||||
# NOW initialize migrator on existing database
|
||||
log info "initializing migrator on existing database"
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed on existing database"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify migrator schema was created
|
||||
if ! psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}hectic.migration table not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify existing data is still intact
|
||||
user_count_after=$(psql -Atc "SELECT COUNT(*) FROM users" "$DATABASE_URL")
|
||||
post_count_after=$(psql -Atc "SELECT COUNT(*) FROM posts" "$DATABASE_URL")
|
||||
|
||||
if [ "$user_count_after" != "$user_count" ] || [ "$post_count_after" != "$post_count" ]; then
|
||||
log error "test failed: ${WHITE}existing data was affected by migrator init"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "existing data preserved: $user_count_after users, $post_count_after posts"
|
||||
|
||||
# Apply a migration that modifies existing table
|
||||
log info "applying migration to existing table"
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}migration on existing table failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify migration was applied
|
||||
if ! psql -Atc "SELECT bio FROM users LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}bio column not added to existing table"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify existing data still intact with NULL in new column
|
||||
alice_bio=$(psql -Atc "SELECT bio FROM users WHERE username = 'alice'" "$DATABASE_URL")
|
||||
if [ "$alice_bio" != "" ]; then
|
||||
log error "test failed: ${WHITE}new column should be NULL for existing rows, got: $alice_bio"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we can still query existing data
|
||||
alice_email=$(psql -Atc "SELECT email FROM users WHERE username = 'alice'" "$DATABASE_URL")
|
||||
if [ "$alice_email" != "alice@example.com" ]; then
|
||||
log error "test failed: ${WHITE}existing data corrupted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "migration applied successfully to existing table"
|
||||
|
||||
# Apply second migration that adds a new table
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}second migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify new table exists
|
||||
if ! psql -Atc "SELECT COUNT(*) FROM comments" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}comments table not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we can add data that references existing data
|
||||
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<SQL
|
||||
INSERT INTO comments (post_id, user_id, content)
|
||||
VALUES (1, 2, 'Nice post!');
|
||||
SQL
|
||||
|
||||
comment_count=$(psql -Atc "SELECT COUNT(*) FROM comments" "$DATABASE_URL")
|
||||
if [ "$comment_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}could not insert into new table with foreign keys to existing data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "new table works with existing data relationships"
|
||||
|
||||
# Test migration rollback with existing database
|
||||
log info "testing rollback on database with pre-existing tables"
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate down; then
|
||||
log error "test failed: ${WHITE}migration down failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify comments table was removed
|
||||
if psql -Atc "SELECT COUNT(*) FROM comments" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}comments table should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify existing tables still intact
|
||||
final_user_count=$(psql -Atc "SELECT COUNT(*) FROM users" "$DATABASE_URL")
|
||||
final_post_count=$(psql -Atc "SELECT COUNT(*) FROM posts" "$DATABASE_URL")
|
||||
|
||||
if [ "$final_user_count" != "$user_count" ] || [ "$final_post_count" != "$post_count" ]; then
|
||||
log error "test failed: ${WHITE}existing data affected by rollback"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed: migrator works correctly with existing database"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Remove test table
|
||||
DROP TABLE hectic.test_table;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Add a table to hectic schema alongside user's existing tables
|
||||
CREATE TABLE hectic.test_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-existing-with-conflicts
|
||||
|
||||
log notice "test case: ${WHITE}migrator with conflicting existing schema"
|
||||
|
||||
# Create a database that already has a 'hectic' schema (potential conflict)
|
||||
log info "creating existing database with hectic schema"
|
||||
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<SQL
|
||||
-- User already has something in hectic schema
|
||||
CREATE SCHEMA hectic;
|
||||
|
||||
CREATE TABLE hectic.user_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
data TEXT
|
||||
);
|
||||
|
||||
INSERT INTO hectic.user_data (data) VALUES ('important data');
|
||||
SQL
|
||||
|
||||
# Verify existing hectic data
|
||||
existing_data=$(psql -Atc "SELECT data FROM hectic.user_data" "$DATABASE_URL")
|
||||
if [ "$existing_data" != "important data" ]; then
|
||||
log error "test failed: ${WHITE}existing hectic data not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "existing hectic schema contains user_data table"
|
||||
|
||||
# Initialize migrator (should handle existing hectic schema gracefully)
|
||||
log info "initializing migrator with existing hectic schema"
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed with existing hectic schema"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify migrator tables were created
|
||||
if ! psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}hectic.migration not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! psql -Atc "SELECT COUNT(*) FROM hectic.version" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}hectic.version not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify existing user data still intact
|
||||
existing_data_after=$(psql -Atc "SELECT data FROM hectic.user_data" "$DATABASE_URL")
|
||||
if [ "$existing_data_after" != "$existing_data" ]; then
|
||||
log error "test failed: ${WHITE}existing hectic.user_data was corrupted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "existing hectic.user_data preserved"
|
||||
|
||||
# Apply a migration
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify both user table and migrator tables coexist
|
||||
tables_in_hectic=$(psql -Atc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'hectic'" "$DATABASE_URL")
|
||||
if [ "$tables_in_hectic" -lt 4 ]; then
|
||||
log error "test failed: ${WHITE}expected at least 4 tables in hectic schema (user_data, migration, version, test_table), got $tables_in_hectic"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "hectic schema contains $tables_in_hectic tables (user + migrator tables)"
|
||||
|
||||
log notice "test passed: migrator coexists with existing hectic schema"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions DROP COLUMN user_id;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions ADD COLUMN user_id INTEGER;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions DROP COLUMN token;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions ADD COLUMN token TEXT;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions DROP COLUMN expires_at;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions ADD COLUMN expires_at TIMESTAMPTZ;
|
||||
|
||||
53
test/package/migrator/test/migrate-to-backward/run.sh
Normal file
53
test/package/migrator/test/migrate-to-backward/run.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-to-backward
|
||||
|
||||
log notice "test case: ${WHITE}migrate to (backward) specific migration"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE sessions (id INTEGER PRIMARY KEY)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply all 3 migrations
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up 3; then
|
||||
log error "test failed: ${WHITE}migrate up failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify all 3 columns exist
|
||||
if ! psql -Atc "SELECT user_id, token, expires_at FROM sessions LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}not all columns added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Migrate back to first migration
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate to 20250101000001-add-user-id; then
|
||||
log error "test failed: ${WHITE}migrate to (backward) failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only 1 migration remains
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only user_id exists
|
||||
if ! psql -Atc "SELECT user_id FROM sessions LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}user_id should exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if psql -Atc "SELECT token FROM sessions LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}token should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE comments DROP COLUMN content;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE comments ADD COLUMN content TEXT;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE comments DROP COLUMN user_id;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE comments ADD COLUMN user_id INTEGER;
|
||||
|
||||
36
test/package/migrator/test/migrate-to-forward/run.sh
Normal file
36
test/package/migrator/test/migrate-to-forward/run.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-to-forward
|
||||
|
||||
log notice "test case: ${WHITE}migrate to (forward) specific migration"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE comments (id INTEGER PRIMARY KEY)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Migrate to second migration (skipping intermediate)
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate to 20250101000002-add-user-id; then
|
||||
log error "test failed: ${WHITE}migrate to failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify 2 migrations were applied
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "2" ]; then
|
||||
log error "test failed: ${WHITE}expected 2 migrations, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify both columns exist
|
||||
if ! psql -Atc "SELECT content, user_id FROM comments LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}columns not added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts DROP COLUMN content;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts ADD COLUMN content TEXT;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts DROP COLUMN author;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts ADD COLUMN author TEXT;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts DROP COLUMN published_at;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE posts ADD COLUMN published_at TIMESTAMPTZ;
|
||||
|
||||
36
test/package/migrator/test/migrate-up-multiple/run.sh
Normal file
36
test/package/migrator/test/migrate-up-multiple/run.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-up-multiple
|
||||
|
||||
log notice "test case: ${WHITE}migrate up multiple steps"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply 3 migrations at once
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up 3; then
|
||||
log error "test failed: ${WHITE}migrate up 3 failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify all 3 migrations were applied
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "3" ]; then
|
||||
log error "test failed: ${WHITE}expected 3 migrations, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify all columns were added
|
||||
if ! psql -Atc "SELECT content, author, published_at FROM posts LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}not all columns were added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE users DROP COLUMN email;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE users ADD COLUMN email TEXT;
|
||||
|
||||
36
test/package/migrator/test/migrate-up-single/run.sh
Normal file
36
test/package/migrator/test/migrate-up-single/run.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migrate-up-single
|
||||
|
||||
log notice "test case: ${WHITE}migrate up single step"
|
||||
|
||||
# Create initial schema
|
||||
psql "$DATABASE_URL" -c 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)'
|
||||
|
||||
# Initialize migrator
|
||||
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply first migration
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}migrate up failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify migration was applied
|
||||
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||
if [ "$applied_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $applied_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify column was added
|
||||
if ! psql -Atc "SELECT email FROM users LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}email column not added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE profile ADD COLUMN info TEXT;
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-migration-list
|
||||
|
||||
|
||||
|
||||
log notice "test case: ${WHITE}migration up"
|
||||
psql "$DATABASE_URL" 'CREATE TABLE profile (
|
||||
id INTEGER,
|
||||
username TEXT
|
||||
)'
|
||||
|
||||
if ! migrator --db-url "$DATABASE_URL" migrate to 20251104192425-add-info-to-profile; then
|
||||
log error "test failed: ${WHITE}error on migration up"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "$(columns profile)"
|
||||
|
||||
|
||||
exit 1
|
||||
Reference in New Issue
Block a user