chore: sync current workspace changes
Desktop CI Build / Build macOS ARM64 DMG (push) Has been cancelled
Desktop CI Build / Build Windows x64 (push) Has been cancelled
Desktop CI Build / Build macOS ARM64 DMG (push) Has been cancelled
Desktop CI Build / Build Windows x64 (push) Has been cancelled
This commit is contained in:
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PGRUST_REPOSITORY="${PGRUST_REPOSITORY:-https://github.com/malisper/pgrust.git}"
|
||||
PGRUST_REF="${PGRUST_REF:-c9aecbd05348e52a8ba21c688eec6258e8e8282f}"
|
||||
PGRUST_SOURCE_DIR="${PGRUST_SOURCE_DIR:-$ROOT_DIR/target/pgrust-src}"
|
||||
PGRUST_TARGET_DIR="${PGRUST_TARGET_DIR:-$ROOT_DIR/target/pgrust-build}"
|
||||
PGRUST_OUTPUT_DIR="${PGRUST_OUTPUT_DIR:-$ROOT_DIR/dist/pgrust}"
|
||||
PGRUST_SIZE_OPTIMIZED="${PGRUST_SIZE_OPTIMIZED:-0}"
|
||||
|
||||
for command in cargo git pkg-config; do
|
||||
command -v "$command" >/dev/null || {
|
||||
echo "missing required command: $command" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
for package in icu-uc openssl libxml-2.0; do
|
||||
pkg-config --exists "$package" || {
|
||||
echo "missing native development package for $package" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [ ! -d "$PGRUST_SOURCE_DIR/.git" ]; then
|
||||
rm -rf "$PGRUST_SOURCE_DIR"
|
||||
git clone --no-checkout "$PGRUST_REPOSITORY" "$PGRUST_SOURCE_DIR"
|
||||
fi
|
||||
|
||||
minimal_auth_patch="$ROOT_DIR/patches/pgrust-minimal-auth.patch"
|
||||
if git -C "$PGRUST_SOURCE_DIR" apply --check "$minimal_auth_patch" 2>/dev/null; then
|
||||
git -C "$PGRUST_SOURCE_DIR" apply "$minimal_auth_patch"
|
||||
elif ! git -C "$PGRUST_SOURCE_DIR" apply --reverse --check "$minimal_auth_patch" 2>/dev/null; then
|
||||
echo "pgrust minimal auth patch does not apply cleanly to $PGRUST_REF" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(git -C "$PGRUST_SOURCE_DIR" rev-parse HEAD 2>/dev/null || true)" != "$PGRUST_REF" ]; then
|
||||
git -C "$PGRUST_SOURCE_DIR" fetch --depth 1 origin "$PGRUST_REF"
|
||||
git -C "$PGRUST_SOURCE_DIR" checkout --detach FETCH_HEAD
|
||||
fi
|
||||
|
||||
if rustc -vV | grep -q 'host: .*musl'; then
|
||||
patch_file="$ROOT_DIR/patches/pgrust-musl-sem.patch"
|
||||
if git -C "$PGRUST_SOURCE_DIR" apply --check "$patch_file" 2>/dev/null; then
|
||||
git -C "$PGRUST_SOURCE_DIR" apply "$patch_file"
|
||||
elif ! git -C "$PGRUST_SOURCE_DIR" apply --reverse --check "$patch_file" 2>/dev/null; then
|
||||
echo "pgrust musl patch does not apply cleanly to $PGRUST_REF" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
share_source="$PGRUST_SOURCE_DIR/vendor/postgres-18.3/share"
|
||||
test -f "$share_source/postgres.bki" || {
|
||||
echo "pgrust PostgreSQL 18.3 share tree is incomplete" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$PGRUST_SIZE_OPTIMIZED" = "1" ]; then
|
||||
export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
|
||||
export CARGO_PROFILE_RELEASE_LTO=thin
|
||||
fi
|
||||
|
||||
PGRUST_PGBINDIR=/opt/boosapi/pgrust/bin \
|
||||
PGRUST_PGSHAREDIR=/opt/boosapi/pgrust/share \
|
||||
CARGO_TARGET_DIR="$PGRUST_TARGET_DIR" \
|
||||
cargo build \
|
||||
--manifest-path "$PGRUST_SOURCE_DIR/Cargo.toml" \
|
||||
--release --locked --bin postgres
|
||||
|
||||
staging="$PGRUST_OUTPUT_DIR.tmp-$$"
|
||||
rm -rf "$staging"
|
||||
mkdir -p "$staging/bin" "$staging/share"
|
||||
cp "$PGRUST_TARGET_DIR/release/postgres" "$staging/bin/postgres"
|
||||
cp -a "$share_source/." "$staging/share/"
|
||||
cp "$PGRUST_SOURCE_DIR/LICENSE" "$staging/LICENSE.pgrust-AGPL-3.0"
|
||||
printf '%s\n' \
|
||||
"Repository: $PGRUST_REPOSITORY" \
|
||||
"Commit: $PGRUST_REF" \
|
||||
"License: AGPL-3.0" \
|
||||
>"$staging/SOURCE"
|
||||
chmod 755 "$staging/bin/postgres"
|
||||
find "$staging/share" -type d -exec chmod 755 {} +
|
||||
find "$staging/share" -type f -exec chmod 644 {} +
|
||||
|
||||
rm -rf "$PGRUST_OUTPUT_DIR"
|
||||
mv "$staging" "$PGRUST_OUTPUT_DIR"
|
||||
echo "pgrust runtime built at $PGRUST_OUTPUT_DIR"
|
||||
Executable
+203
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
source_url="${1:-postgresql:///boosapi_migration_test}"
|
||||
output="${2:-migrations_mysql/20260516000001_initial.sql}"
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
psql_cmd=(psql -X -qAt -v ON_ERROR_STOP=1 "$source_url")
|
||||
|
||||
"${psql_cmd[@]}" -F '|' -c "
|
||||
SELECT DISTINCT c.relname, a.attname
|
||||
FROM pg_index i
|
||||
JOIN pg_class c ON c.oid = i.indrelid
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
JOIN LATERAL unnest(i.indkey) AS key(attnum) ON true
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = key.attnum
|
||||
WHERE n.nspname = 'public'
|
||||
UNION
|
||||
SELECT DISTINCT c.relname, a.attname
|
||||
FROM pg_constraint con
|
||||
JOIN pg_class c ON c.oid = con.conrelid
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
JOIN LATERAL unnest(con.conkey) AS key(attnum) ON true
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = key.attnum
|
||||
WHERE n.nspname = 'public' AND con.contype = 'f';
|
||||
" > "$tmp_dir/indexed"
|
||||
|
||||
map_type() {
|
||||
local table="$1" column="$2" data_type="$3" default="$4"
|
||||
case "$data_type" in
|
||||
bigint) printf 'BIGINT' ;;
|
||||
boolean) printf 'TINYINT(1)' ;;
|
||||
'double precision') printf 'DOUBLE' ;;
|
||||
bytea) printf 'LONGBLOB' ;;
|
||||
text)
|
||||
if grep -Fxq "$table|$column" "$tmp_dir/indexed"; then
|
||||
printf 'VARCHAR(128)'
|
||||
else
|
||||
printf 'LONGTEXT'
|
||||
fi
|
||||
;;
|
||||
*) printf 'LONGTEXT' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
map_default() {
|
||||
local default="$1" data_type="$2"
|
||||
[[ -n "$default" ]] || return 0
|
||||
if [[ "$default" == nextval\(* ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$default" in
|
||||
'(CURRENT_TIMESTAMP)::text'|'CURRENT_TIMESTAMP'|'now()')
|
||||
printf ' DEFAULT (CAST(UTC_TIMESTAMP() AS CHAR))'
|
||||
;;
|
||||
true) printf ' DEFAULT 1' ;;
|
||||
false) printf ' DEFAULT 0' ;;
|
||||
*"::text")
|
||||
local literal="${default%::text}"
|
||||
if [[ "$data_type" == text && "$literal" == "''" ]]; then
|
||||
printf " DEFAULT (CAST('' AS CHAR))"
|
||||
elif [[ "$data_type" == text && "$literal" == "'"* ]]; then
|
||||
printf ' DEFAULT (%s)' "$literal"
|
||||
else
|
||||
printf ' DEFAULT %s' "$literal"
|
||||
fi
|
||||
;;
|
||||
*) printf ' DEFAULT %s' "$default" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
{
|
||||
echo '-- Generated from the canonical PostgreSQL schema by scripts/generate_mysql_schema.sh.'
|
||||
echo '-- Compatible with MySQL 8.0+ and MariaDB 10.6+.'
|
||||
echo 'SET NAMES utf8mb4;'
|
||||
echo 'SET FOREIGN_KEY_CHECKS = 0;'
|
||||
echo
|
||||
|
||||
while IFS= read -r table; do
|
||||
echo "CREATE TABLE \`$table\` ("
|
||||
mapfile -t columns < <("${psql_cmd[@]}" -F '|' -c "
|
||||
SELECT column_name, data_type, is_nullable, COALESCE(column_default, '')
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = '$table'
|
||||
ORDER BY ordinal_position;")
|
||||
definitions=()
|
||||
for row in "${columns[@]}"; do
|
||||
IFS='|' read -r column data_type nullable default <<< "$row"
|
||||
type="$(map_type "$table" "$column" "$data_type" "$default")"
|
||||
definition=" \`$column\` $type"
|
||||
[[ "$nullable" == YES ]] || definition+=' NOT NULL'
|
||||
if [[ "$default" == nextval\(* ]]; then
|
||||
definition+=' AUTO_INCREMENT'
|
||||
else
|
||||
definition+="$(map_default "$default" "$data_type")"
|
||||
fi
|
||||
definitions+=("$definition")
|
||||
done
|
||||
|
||||
while IFS='|' read -r constraint_name constraint_type column_list; do
|
||||
[[ -n "$constraint_name" ]] || continue
|
||||
if [[ "$constraint_type" == PRIMARY* ]]; then
|
||||
definitions+=(" PRIMARY KEY ($column_list)")
|
||||
else
|
||||
definitions+=(" CONSTRAINT \`$constraint_name\` UNIQUE ($column_list)")
|
||||
fi
|
||||
done < <("${psql_cmd[@]}" -F '|' -c "
|
||||
SELECT con.conname,
|
||||
CASE con.contype WHEN 'p' THEN 'PRIMARY' ELSE 'UNIQUE' END,
|
||||
string_agg(format('\\\`%s\\\`', att.attname), ', ' ORDER BY ord.n)
|
||||
FROM pg_constraint con
|
||||
JOIN pg_class rel ON rel.oid = con.conrelid
|
||||
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
||||
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY ord(attnum, n) ON true
|
||||
JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = ord.attnum
|
||||
WHERE ns.nspname = 'public' AND rel.relname = '$table' AND con.contype IN ('p', 'u')
|
||||
GROUP BY con.conname, con.contype
|
||||
ORDER BY con.contype, con.conname;")
|
||||
|
||||
case "$table" in
|
||||
api_request_leases)
|
||||
definitions+=(" \`active_user_id\` BIGINT GENERATED ALWAYS AS (CASE WHEN \`status\` = 'active' THEN \`user_id\` ELSE NULL END) VIRTUAL")
|
||||
definitions+=(" UNIQUE INDEX \`api_request_leases_one_active_per_user\` (\`active_user_id\`)")
|
||||
;;
|
||||
agent_runs)
|
||||
definitions+=(" \`active_session_id\` VARCHAR(128) GENERATED ALWAYS AS (CASE WHEN \`status\` IN ('starting','running','waiting_approval','waiting_input','cancelling') THEN \`session_id\` ELSE NULL END) VIRTUAL")
|
||||
definitions+=(" UNIQUE INDEX \`agent_runs_one_active_per_session\` (\`active_session_id\`)")
|
||||
;;
|
||||
payment_refund_requests)
|
||||
definitions+=(" \`active_payment_order_id\` BIGINT GENERATED ALWAYS AS (CASE WHEN \`status\` IN ('pending','approved','processing') THEN \`payment_order_id\` ELSE NULL END) VIRTUAL")
|
||||
definitions+=(" UNIQUE INDEX \`payment_refund_requests_one_active_per_order\` (\`active_payment_order_id\`)")
|
||||
;;
|
||||
esac
|
||||
|
||||
for ((i=0; i<${#definitions[@]}; i++)); do
|
||||
suffix=','
|
||||
(( i + 1 == ${#definitions[@]} )) && suffix=''
|
||||
printf '%s%s\n' "${definitions[$i]}" "$suffix"
|
||||
done
|
||||
echo ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;'
|
||||
echo
|
||||
done < <("${psql_cmd[@]}" -c "
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
||||
AND table_name <> '_sqlx_migrations'
|
||||
ORDER BY table_name;")
|
||||
|
||||
"${psql_cmd[@]}" -F '|' -c "
|
||||
SELECT format(
|
||||
'ALTER TABLE \\\`%s\\\` ADD CONSTRAINT \\\`%s\\\` %s;',
|
||||
rel.relname,
|
||||
con.conname,
|
||||
replace(pg_get_constraintdef(con.oid), 'public.', '')
|
||||
)
|
||||
FROM pg_constraint con
|
||||
JOIN pg_class rel ON rel.oid = con.conrelid
|
||||
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
||||
WHERE ns.nspname = 'public' AND con.contype = 'f'
|
||||
ORDER BY rel.relname, con.conname;"
|
||||
|
||||
echo
|
||||
"${psql_cmd[@]}" -F '|' -c "
|
||||
SELECT format(
|
||||
'CREATE INDEX \\\`%s\\\` ON \\\`%s\\\` (%s);',
|
||||
idx.relname,
|
||||
tbl.relname,
|
||||
string_agg(format('\\\`%s\\\`', att.attname), ', ' ORDER BY ord.n)
|
||||
)
|
||||
FROM pg_index i
|
||||
JOIN pg_class tbl ON tbl.oid = i.indrelid
|
||||
JOIN pg_class idx ON idx.oid = i.indexrelid
|
||||
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
|
||||
JOIN LATERAL unnest(i.indkey) WITH ORDINALITY ord(attnum, n) ON ord.attnum > 0
|
||||
JOIN pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = ord.attnum
|
||||
LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid
|
||||
WHERE ns.nspname = 'public' AND con.oid IS NULL
|
||||
AND NOT i.indisunique AND i.indpred IS NULL AND i.indexprs IS NULL
|
||||
GROUP BY idx.relname, tbl.relname
|
||||
ORDER BY tbl.relname, idx.relname;"
|
||||
|
||||
cat <<'SQL'
|
||||
|
||||
CREATE UNIQUE INDEX `payment_orders_provider_session_unique` ON `payment_orders` (`provider`, `provider_session_id`);
|
||||
CREATE UNIQUE INDEX `payment_order_provider_session_unique` ON `payment_order_provider_bindings` (`provider_instance_id`, `provider_session_id`);
|
||||
CREATE UNIQUE INDEX `payment_order_provider_payment_unique` ON `payment_order_provider_bindings` (`provider_instance_id`, `provider_payment_id`);
|
||||
CREATE UNIQUE INDEX `payment_refunds_provider_reference_unique` ON `payment_refunds` (`provider_instance_id`, `provider_refund_id`);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
SQL
|
||||
} > "$output"
|
||||
|
||||
sed -i 's/\\`/`/g' "$output"
|
||||
|
||||
for source_migration in migrations/*.sql; do
|
||||
name="$(basename "$source_migration")"
|
||||
[[ "$name" == "$(basename "$output")" ]] && continue
|
||||
[[ -e "migrations_mysql/$name" ]] && continue
|
||||
printf '%s\n' "-- Included in the consolidated MySQL/MariaDB initial schema." > "migrations_mysql/$name"
|
||||
done
|
||||
|
||||
echo "generated $output"
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SOURCE_RUNTIME_DIR="${PGRUST_RUNTIME_DIR:-$ROOT_DIR/dist/pgrust}"
|
||||
APP_PORT="${BOOSAPI_SMOKE_PORT:-4402}"
|
||||
SMOKE_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/boosapi-pgrust-smoke.XXXXXX")"
|
||||
DATA_DIR="$SMOKE_ROOT/data"
|
||||
RUNTIME_DIR="$SMOKE_ROOT/runtime"
|
||||
CONFIG_PATH="$SMOKE_ROOT/config.toml"
|
||||
APP_LOG="$SMOKE_ROOT/boosapi.log"
|
||||
APP_PID=""
|
||||
|
||||
cleanup() {
|
||||
local status=$?
|
||||
if [ -n "$APP_PID" ] && kill -0 "$APP_PID" 2>/dev/null; then
|
||||
kill -INT "$APP_PID" 2>/dev/null || true
|
||||
wait "$APP_PID" 2>/dev/null || true
|
||||
fi
|
||||
if pgrep -f -- "-D $DATA_DIR" >/dev/null 2>&1; then
|
||||
pkill -INT -f -- "-D $DATA_DIR" 2>/dev/null || true
|
||||
fi
|
||||
if [ "$status" -ne 0 ] && [ -s "$APP_LOG" ]; then
|
||||
cat "$APP_LOG" >&2
|
||||
fi
|
||||
rm -rf "$SMOKE_ROOT"
|
||||
return "$status"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
for command in cargo curl jq pgrep; do
|
||||
command -v "$command" >/dev/null || {
|
||||
echo "missing required command: $command" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
if [ ! -x "$SOURCE_RUNTIME_DIR/bin/postgres" ]; then
|
||||
scripts/build_pgrust_runtime.sh
|
||||
fi
|
||||
chmod 755 "$SMOKE_ROOT"
|
||||
cp -a "$SOURCE_RUNTIME_DIR" "$RUNTIME_DIR"
|
||||
chmod -R a+rX "$RUNTIME_DIR"
|
||||
cp config.toml "$CONFIG_PATH"
|
||||
|
||||
cargo rustc --bin boosapi -- -C linker=clang -C link-arg=-fuse-ld=mold
|
||||
|
||||
common_env=(
|
||||
"BOOSAPI_PGRUST__RUNTIME_DIR=$RUNTIME_DIR"
|
||||
"BOOSAPI_PGRUST__DATA_DIR=$DATA_DIR"
|
||||
"BOOSAPI_REDIS__ENABLED=false"
|
||||
)
|
||||
|
||||
env "${common_env[@]}" target/debug/boosapi --config "$CONFIG_PATH" --migrate-only
|
||||
test -f "$DATA_DIR/PG_VERSION"
|
||||
test ! -f "$DATA_DIR/postmaster.pid"
|
||||
|
||||
env "${common_env[@]}" \
|
||||
BOOSAPI_SERVER__PORT="$APP_PORT" \
|
||||
BOOSAPI_AUTH__AUTH_TOKEN=pgrust-smoke-admin-token \
|
||||
target/debug/boosapi --config "$CONFIG_PATH" >"$APP_LOG" 2>&1 &
|
||||
APP_PID=$!
|
||||
|
||||
for _ in $(seq 1 120); do
|
||||
if curl --fail --silent "http://127.0.0.1:${APP_PORT}/healthz" \
|
||||
| jq -e '.status == "ok" and .database == "ok"' >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$APP_PID" 2>/dev/null; then
|
||||
wait "$APP_PID" || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
|
||||
curl --fail --silent \
|
||||
-H 'Authorization: Bearer pgrust-smoke-admin-token' \
|
||||
"http://127.0.0.1:${APP_PORT}/api/admin/users" \
|
||||
| jq -e '(.users | type) == "array"' >/dev/null
|
||||
|
||||
pgrep -f -- "-D $DATA_DIR" >/dev/null
|
||||
kill -INT "$APP_PID"
|
||||
wait "$APP_PID"
|
||||
APP_PID=""
|
||||
|
||||
for _ in $(seq 1 40); do
|
||||
if ! pgrep -f -- "-D $DATA_DIR" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
if pgrep -f -- "-D $DATA_DIR" >/dev/null 2>&1; then
|
||||
echo "embedded pgrust process did not stop with BoosAPI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "embedded pgrust smoke test passed"
|
||||
Reference in New Issue
Block a user