From 182f9e1a71d263a9fb1baf49f23e72c393b9dae4 Mon Sep 17 00:00:00 2001 From: marius-mather Date: Thu, 3 Jul 2025 12:59:51 +1000 Subject: [PATCH 1/5] Update generate_migrations script to show schema --- generate_migrations.py | 38 +++++++++++++---- .../101f45395233_add_group_membership.py | 41 ------------------- .../versions/e55434038b96_status_enum.py | 37 ----------------- 3 files changed, 29 insertions(+), 87 deletions(-) delete mode 100644 migrations/versions/101f45395233_add_group_membership.py delete mode 100644 migrations/versions/e55434038b96_status_enum.py diff --git a/generate_migrations.py b/generate_migrations.py index 7bd1c8d0..9b58f0c7 100644 --- a/generate_migrations.py +++ b/generate_migrations.py @@ -16,11 +16,29 @@ def run(cmd, env=None): subprocess.run(cmd, shell=True, check=True, env=env or os.environ) +def print_db_schema(): + print("๐Ÿ“ฆ Printing database schema...") + query = """ + SELECT table_name, column_name, data_type + FROM information_schema.columns + WHERE table_schema = 'public' + ORDER BY table_name, ordinal_position; + """ + psql_cmd = ( + f'psql -h localhost -p {DEFAULT_PORT} -U {POSTGRES_USER} ' + f'-c "{query.strip()}"' + ) + env = os.environ.copy() + env["PGPASSWORD"] = POSTGRES_PASSWORD + run(psql_cmd, env=env) + + @click.command() @click.option('--revision-message', '-m', required=False, help="Message for Alembic revision.") @click.option('--check', is_flag=True, help="Only run 'alembic check' after DB container is up.") -def generate_migrations(revision_message, check): - """Spin up a temp Postgres DB, apply migrations or run alembic check.""" +@click.option('--print-schema', is_flag=True, help="Print the schema of the database after upgrade/check.") +def generate_migrations(revision_message, check, print_schema): + """Spin up a temp Postgres DB, apply migrations or run alembic check, optionally print schema.""" if not check and not revision_message: raise click.UsageError("Missing option '-m' / '--revision-message'. Required unless using --check.") @@ -39,19 +57,21 @@ def generate_migrations(revision_message, check): ) print("โณ Waiting for database to be ready...") - time.sleep(5) # Could be enhanced with pg_isready + time.sleep(5) + + print("๐Ÿงฑ Applying existing Alembic migrations...") + run("alembic upgrade head") if check: - print("๐Ÿงฑ Applying existing Alembic migrations...") - run("alembic upgrade head") print("๐Ÿ” Running 'alembic check'...") run("alembic check") - else: - print("๐Ÿงฑ Applying existing Alembic migrations...") - run("alembic upgrade head") + elif revision_message: print("๐Ÿ“ Generating new Alembic revision...") - run(f"alembic revision --autogenerate -m \"{revision_message}\"") + run(f'alembic revision --autogenerate -m "{revision_message}"') + + if print_schema: + print_db_schema() finally: print("๐Ÿงน Cleaning up: stopping container...") diff --git a/migrations/versions/101f45395233_add_group_membership.py b/migrations/versions/101f45395233_add_group_membership.py deleted file mode 100644 index 6d302439..00000000 --- a/migrations/versions/101f45395233_add_group_membership.py +++ /dev/null @@ -1,41 +0,0 @@ -"""add_group_membership - -Revision ID: 101f45395233 -Revises: -Create Date: 2025-07-01 16:29:48.072722 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import sqlmodel - - -# revision identifiers, used by Alembic. -revision: str = '101f45395233' -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('groupmembership', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('group', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('user_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('user_email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('approval_status', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('updated_at', sa.DateTime(), nullable=False), - sa.Column('updated_by_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('updated_by_email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('groupmembership') - # ### end Alembic commands ### diff --git a/migrations/versions/e55434038b96_status_enum.py b/migrations/versions/e55434038b96_status_enum.py deleted file mode 100644 index b81eb7f5..00000000 --- a/migrations/versions/e55434038b96_status_enum.py +++ /dev/null @@ -1,37 +0,0 @@ -"""status_enum - -Revision ID: e55434038b96 -Revises: 101f45395233 -Create Date: 2025-07-03 09:41:04.657535 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import sqlmodel - - -# revision identifiers, used by Alembic. -revision: str = 'e55434038b96' -down_revision: Union[str, None] = '101f45395233' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('groupmembership', 'approval_status', - existing_type=sa.VARCHAR(), - type_=sa.Enum('APPROVED', 'PENDING', 'REVOKED', name='ApprovalStatusEnum'), - existing_nullable=False) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('groupmembership', 'approval_status', - existing_type=sa.Enum('APPROVED', 'PENDING', 'REVOKED', name='ApprovalStatusEnum'), - type_=sa.VARCHAR(), - existing_nullable=False) - # ### end Alembic commands ### From b29eed30e519e46373e07aa4d96d964679f3056d Mon Sep 17 00:00:00 2001 From: marius-mather Date: Thu, 3 Jul 2025 13:00:34 +1000 Subject: [PATCH 2/5] Replace migrations: alembic only handles Enums automatically when creating a new table --- .../versions/07161e26f537_group_membership.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 migrations/versions/07161e26f537_group_membership.py diff --git a/migrations/versions/07161e26f537_group_membership.py b/migrations/versions/07161e26f537_group_membership.py new file mode 100644 index 00000000..a0a50b6a --- /dev/null +++ b/migrations/versions/07161e26f537_group_membership.py @@ -0,0 +1,41 @@ +"""group_membership + +Revision ID: 07161e26f537 +Revises: +Create Date: 2025-07-03 11:48:44.293471 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = '07161e26f537' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('groupmembership', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('group', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('user_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('user_email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('approval_status', sa.Enum('APPROVED', 'PENDING', 'REVOKED', name='ApprovalStatusEnum'), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=False), + sa.Column('updated_by_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('updated_by_email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('groupmembership') + # ### end Alembic commands ### From 9502c39597daadc63b92138924d53888370586f8 Mon Sep 17 00:00:00 2001 From: marius-mather Date: Thu, 3 Jul 2025 13:02:04 +1000 Subject: [PATCH 3/5] Add a GitHub Action to check migrations --- .github/workflows/check-migrations.yml | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/check-migrations.yml diff --git a/.github/workflows/check-migrations.yml b/.github/workflows/check-migrations.yml new file mode 100644 index 00000000..a3313774 --- /dev/null +++ b/.github/workflows/check-migrations.yml @@ -0,0 +1,37 @@ +name: check-migrations.yml +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.13 + + - name: Install uv + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + + - name: Create virtual environment and install dependencies + run: | + uv venv + uv sync --extra dev + source .venv/bin/activate + + - name: Run migration checks + run: | + python generate_migrations.py --check From 4fcfcb1909e6cde33533c6e9552dee58607444bd Mon Sep 17 00:00:00 2001 From: marius-mather Date: Thu, 3 Jul 2025 13:06:45 +1000 Subject: [PATCH 4/5] Rename GitHub Action for migration checks --- .github/workflows/check-migrations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-migrations.yml b/.github/workflows/check-migrations.yml index a3313774..1ed25a94 100644 --- a/.github/workflows/check-migrations.yml +++ b/.github/workflows/check-migrations.yml @@ -1,4 +1,4 @@ -name: check-migrations.yml +name: Check DB migrations on: push: branches: From 1d0c020a1b8593a4a113d415da1bb986d1719e1d Mon Sep 17 00:00:00 2001 From: marius-mather Date: Thu, 3 Jul 2025 13:08:16 +1000 Subject: [PATCH 5/5] Fix activating venv in GitHub Action --- .github/workflows/check-migrations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-migrations.yml b/.github/workflows/check-migrations.yml index 1ed25a94..b2b6728f 100644 --- a/.github/workflows/check-migrations.yml +++ b/.github/workflows/check-migrations.yml @@ -30,8 +30,8 @@ jobs: run: | uv venv uv sync --extra dev - source .venv/bin/activate - name: Run migration checks run: | + source .venv/bin/activate python generate_migrations.py --check