fix(alembic): bridge ghost migration 2026_05_28_add_tool_definition_manifests
The production database was stamped with a migration that no longer exists in the codebase (created on another branch, applied, then removed). This adds a no-op bridge migration so Alembic can reconcile the DB state. - Create bridge migration 2026_05_28_add_tool_definition_manifests (no-op) - Re-chain terminal_sessions migration to depend on the bridge - Fixes startup failure: Can't locate revision identified by ...
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -6,16 +6,16 @@ Create Date: 2026-05-28
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "2026_05_28_add_terminal_sessions"
|
revision: str = "2026_05_28_add_terminal_sessions"
|
||||||
down_revision: Union[str, None] = "20260527_160017_add_pi_agent"
|
down_revision: str | None = "2026_05_28_add_tool_definition_manifests"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
"""bridge: add_tool_definition_manifests (ghost migration)
|
||||||
|
|
||||||
|
Revision ID: 2026_05_28_add_tool_definition_manifests
|
||||||
|
Revises: 20260527_160017_add_pi_agent
|
||||||
|
Create Date: 2026-05-28
|
||||||
|
|
||||||
|
This is a no-op bridge migration. The original migration was created on
|
||||||
|
another branch and applied to the production database, but the file was
|
||||||
|
subsequently removed. This bridge allows Alembic to reconcile the DB
|
||||||
|
state with the local revision history.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "2026_05_28_add_tool_definition_manifests"
|
||||||
|
down_revision: str | None = "20260527_160017_add_pi_agent"
|
||||||
|
branch_labels: str | Sequence[str] | None = None
|
||||||
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# No-op: this migration was already applied on the target database.
|
||||||
|
# If the schema was actually changed by the original migration,
|
||||||
|
# those changes must be preserved manually or via a separate migration.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# No-op: cannot safely reverse a migration whose original operations
|
||||||
|
# are unknown.
|
||||||
|
pass
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -2907,7 +2907,9 @@ a.nav-item,
|
|||||||
/* Mobile auto-hide header and tabs */
|
/* Mobile auto-hide header and tabs */
|
||||||
.terminal-page.mobile .terminal-page-header,
|
.terminal-page.mobile .terminal-page-header,
|
||||||
.mobile-tabs-container {
|
.mobile-tabs-container {
|
||||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
transition:
|
||||||
|
transform 0.3s ease,
|
||||||
|
opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-page.mobile .terminal-page-header.hidden,
|
.terminal-page.mobile .terminal-page-header.hidden,
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Fix ghost migration on production server
|
||||||
|
# Run this INSIDE the api container or on the server
|
||||||
|
|
||||||
|
echo "=== Checking for ghost migration file ==="
|
||||||
|
find /app/alembic/versions -name "*tool_definition*" 2>/dev/null
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Current alembic_version in DB ==="
|
||||||
|
psql "$DATABASE_URL" -c "SELECT * FROM alembic_version;"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Checking if tool_types has definition_manifests column ==="
|
||||||
|
psql "$DATABASE_URL" -c "\d tool_types" | grep -i manifest
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Our known migration heads ==="
|
||||||
|
grep -r "revision.*=" /app/alembic/versions/*.py | grep "2026_05_28"
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Securely update the PostgreSQL password for the headquarter database user.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python scripts/update_db_password.py
|
||||||
|
|
||||||
|
The script will prompt for the new password (no echo) and update it via the
|
||||||
|
running hq-postgres Docker container.
|
||||||
|
|
||||||
|
After running, remember to update your .env file:
|
||||||
|
POSTGRES_PASSWORD=<your-new-password>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import getpass
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
# Prompt for new password securely (no echo to terminal)
|
||||||
|
new_password = getpass.getpass("Enter new password for 'headquarter' user: ")
|
||||||
|
if not new_password:
|
||||||
|
print("Error: password cannot be empty.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
confirm = getpass.getpass("Confirm new password: ")
|
||||||
|
if new_password != confirm:
|
||||||
|
print("Error: passwords do not match.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Use psql inside the running postgres container to avoid exposing
|
||||||
|
# the password in host shell history.
|
||||||
|
sql = f"ALTER ROLE headquarter WITH PASSWORD '{new_password}';"
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"docker",
|
||||||
|
"exec",
|
||||||
|
"-i",
|
||||||
|
"hq-postgres",
|
||||||
|
"psql",
|
||||||
|
"-U",
|
||||||
|
"headquarter",
|
||||||
|
"-d",
|
||||||
|
"headquarter",
|
||||||
|
"-c",
|
||||||
|
sql,
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
print(result.stdout.strip())
|
||||||
|
print("\n✅ Password updated successfully.")
|
||||||
|
print("\n⚠️ IMPORTANT: Update your .env file:")
|
||||||
|
print(f" POSTGRES_PASSWORD={new_password}")
|
||||||
|
print("\n⚠️ Then restart the application containers:")
|
||||||
|
print(" docker compose up -d")
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
print(f"Error: {exc.stderr or exc.stdout}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(
|
||||||
|
"Error: 'docker' command not found. Is Docker installed and running?",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user