feat(package): migrator: mvp

This commit is contained in:
2025-12-16 17:28:36 +00:00
parent 13fdfac2ef
commit bb2ae34758
55 changed files with 890 additions and 46 deletions

View File

@@ -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"
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
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

View File

@@ -1,4 +1,4 @@
config: ENC[AES256_GCM,data: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,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

View File

@@ -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'

View File

@@ -0,0 +1,2 @@
ALTER TABLE tags DROP COLUMN name;

View File

@@ -0,0 +1,2 @@
ALTER TABLE tags ADD COLUMN name TEXT;

View 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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders DROP COLUMN user_id;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders ADD COLUMN user_id INTEGER;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders DROP COLUMN total;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders ADD COLUMN total DECIMAL(10,2);

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders DROP COLUMN status;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders ADD COLUMN status TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders DROP COLUMN created_at;

View File

@@ -0,0 +1,2 @@
ALTER TABLE orders ADD COLUMN created_at TIMESTAMPTZ;

View 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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE products DROP COLUMN price;

View File

@@ -0,0 +1,2 @@
ALTER TABLE products ADD COLUMN price DECIMAL(10,2);

View File

@@ -0,0 +1,2 @@
ALTER TABLE products DROP COLUMN description;

View File

@@ -0,0 +1,2 @@
ALTER TABLE products ADD COLUMN description TEXT;

View 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"

View File

@@ -0,0 +1,4 @@
-- Remove transformed columns (original data preserved)
ALTER TABLE products DROP COLUMN price_display;
ALTER TABLE products DROP COLUMN price_dollars;

View File

@@ -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');

View File

@@ -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"

View File

@@ -0,0 +1,3 @@
-- Remove column from existing table
ALTER TABLE users DROP COLUMN bio;

View File

@@ -0,0 +1,3 @@
-- Add column to existing table
ALTER TABLE users ADD COLUMN bio TEXT;

View File

@@ -0,0 +1,3 @@
-- Remove new table
DROP TABLE comments;

View File

@@ -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()
);

View 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"

View File

@@ -0,0 +1,3 @@
-- Remove test table
DROP TABLE hectic.test_table;

View File

@@ -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
);

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions DROP COLUMN user_id;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions ADD COLUMN user_id INTEGER;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions DROP COLUMN token;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions ADD COLUMN token TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions DROP COLUMN expires_at;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sessions ADD COLUMN expires_at TIMESTAMPTZ;

View 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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE comments DROP COLUMN content;

View File

@@ -0,0 +1,2 @@
ALTER TABLE comments ADD COLUMN content TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE comments DROP COLUMN user_id;

View File

@@ -0,0 +1,2 @@
ALTER TABLE comments ADD COLUMN user_id INTEGER;

View 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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts DROP COLUMN content;

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts ADD COLUMN content TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts DROP COLUMN author;

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts ADD COLUMN author TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts DROP COLUMN published_at;

View File

@@ -0,0 +1,2 @@
ALTER TABLE posts ADD COLUMN published_at TIMESTAMPTZ;

View 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"

View File

@@ -0,0 +1,2 @@
ALTER TABLE users DROP COLUMN email;

View File

@@ -0,0 +1,2 @@
ALTER TABLE users ADD COLUMN email TEXT;

View 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"

View File

@@ -1 +0,0 @@
ALTER TABLE profile ADD COLUMN info TEXT;

View File

@@ -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