diff --git a/CHANGELOG.md b/CHANGELOG.md index cd09926..cd97b7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ Collect fragments into this file with: scriv collect --version X.Y.Z + + +## 0.15.0 (2025-01-15) + +### Backwards-incompatible changes + +- Migrate the database to use `TEXT` column types where previously we used `VARCHAR` columns with a (now unnecessary) length limit. **This change requires a database migration on deployment**. In Postgres there is no functional or performance difference between `VARCHAR` and `TEXT` columns. This change simplifies the database schema and reduce the risk of future issues with column length limits. + +### Bug fixes + +- In the `cli` tox environment, fix the name of the executable to be `times-square` rather than `timessquare`. + +### Other changes + +- Improved the developer documentation for database migration to concretely provide copy-and-paste-able commands for preparing and running database migrations. + ## 0.14.0 (2025-01-13) diff --git a/alembic/versions/20250114_1620_2a3bb5a5933b_adopt_text_column_types_where_useful.py b/alembic/versions/20250114_1620_2a3bb5a5933b_adopt_text_column_types_where_useful.py new file mode 100644 index 0000000..ac17b67 --- /dev/null +++ b/alembic/versions/20250114_1620_2a3bb5a5933b_adopt_text_column_types_where_useful.py @@ -0,0 +1,139 @@ +"""Adopt TEXT column types where useful. + +In Postgres there is not functional or performance difference between VARCHAR +and TEXT columns. Therefore we're removing arbitrary constraints on many +string columns by migrating to TEXT column types. + +Revision ID: 2a3bb5a5933b +Revises: 487195933fee +Create Date: 2025-01-14 16:20:45.553562+00:00 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "2a3bb5a5933b" +down_revision: str | None = "487195933fee" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + op.alter_column( + "pages", + "uploader_username", + existing_type=sa.VARCHAR(length=64), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "github_owner", + existing_type=sa.VARCHAR(length=255), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "github_repo", + existing_type=sa.VARCHAR(length=255), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_path_prefix", + existing_type=sa.VARCHAR(length=2048), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_display_path_prefix", + existing_type=sa.VARCHAR(length=2048), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_path_stem", + existing_type=sa.VARCHAR(length=255), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_source_extension", + existing_type=sa.VARCHAR(length=255), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_sidecar_extension", + existing_type=sa.VARCHAR(length=255), + type_=sa.UnicodeText(), + existing_nullable=True, + ) + + +def downgrade() -> None: + op.alter_column( + "pages", + "repository_sidecar_extension", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_source_extension", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_path_stem", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_display_path_prefix", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=2048), + existing_nullable=True, + ) + op.alter_column( + "pages", + "repository_path_prefix", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=2048), + existing_nullable=True, + ) + op.alter_column( + "pages", + "github_repo", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "pages", + "github_owner", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "pages", + "uploader_username", + existing_type=sa.UnicodeText(), + type_=sa.VARCHAR(length=64), + existing_nullable=True, + ) diff --git a/docs/dev/development.rst b/docs/dev/development.rst index f86e294..97b2282 100644 --- a/docs/dev/development.rst +++ b/docs/dev/development.rst @@ -70,7 +70,44 @@ Database migrations Times Square uses Alembic_ for database migrations. If your work involves changing the database schema (in :file:`/src/timessquare/dbschema`) you will need to prepare an Alembic migration in the same PR. -This process is outlined in the `Safir documentation `__. +To create the migration, you will need to run a local postgres database for Alembic to compare the current schema to the new schema: + +1. With your existing codebase (before your changes; switch branches or stash changes if necessary), start up the database: + + .. code-block:: sh + + docker-compose -f docker-compose.yaml up + +2. Initialize the database: + + .. code-block:: sh + + tox run -e cli -- init + +3. Apply code changes to the database schema in :file:`/src/timessquare/dbschema`. + +4. Generate the Alembic migration: + + .. code-block:: sh + + tox run -e alembic -- revision --autogenerate -m "Your migration message." + +5. Review the migration in :file:`alembic/versions/` and make any necessary changes. + In particular, enums require special handling. See the `Safir documentation `__ for more information. + +6. Apply the migration to the running database: + + .. code-block:: sh + + tox run -e cli -- update-db-schema --alembic-config-path alembic.ini + +7. Shut down the database: + + .. code-block:: sh + + docker-compose -f docker-compose.yaml down + +For more general information about preparing Alembic migrations, see the `Safir documentation `__. Note that in Times Square the :file:`docker-compose.yaml` is hosted in the root of the repository rather than in the :file:`alembic` directory. Building documentation diff --git a/src/timessquare/dbschema/page.py b/src/timessquare/dbschema/page.py index 4927874..b0c7230 100644 --- a/src/timessquare/dbschema/page.py +++ b/src/timessquare/dbschema/page.py @@ -60,7 +60,7 @@ class SqlPage(Base): """Tags (keywords) assigned to this page.""" uploader_username: Mapped[str | None] = mapped_column( - Unicode(64), nullable=True + UnicodeText, nullable=True ) """Username of the uploader, if this page is uploaded without GitHub backing. @@ -79,12 +79,12 @@ class SqlPage(Base): indefinitely. """ - github_owner: Mapped[str | None] = mapped_column(Unicode(255)) + github_owner: Mapped[str | None] = mapped_column(UnicodeText) """The GitHub repository owner (username or organization name) for GitHub-backed pages. """ - github_repo: Mapped[str | None] = mapped_column(Unicode(255)) + github_repo: Mapped[str | None] = mapped_column(UnicodeText) """The GitHub repository name for GitHub-backed pages.""" github_commit: Mapped[str | None] = mapped_column(Unicode(40)) @@ -92,17 +92,17 @@ class SqlPage(Base): associated with a GitHub Check Run. """ - repository_path_prefix: Mapped[str | None] = mapped_column(Unicode(2048)) + repository_path_prefix: Mapped[str | None] = mapped_column(UnicodeText) """The repository path prefix, relative to the root of the directory.""" repository_display_path_prefix: Mapped[str | None] = mapped_column( - Unicode(2048) + UnicodeText ) """The repository path prefix, relative to the configured root of Times Square notebooks in a repository. """ - repository_path_stem: Mapped[str | None] = mapped_column(Unicode(255)) + repository_path_stem: Mapped[str | None] = mapped_column(UnicodeText) """The filename stem (without prefix and without extension) of the source file in the GitHub repository for GitHub-backed pages. @@ -112,7 +112,7 @@ class SqlPage(Base): """ repository_source_extension: Mapped[str | None] = mapped_column( - Unicode(255) + UnicodeText ) """The filename extension of the source file in the GitHub repository for GitHub-backed pages. @@ -121,7 +121,7 @@ class SqlPage(Base): """ repository_sidecar_extension: Mapped[str | None] = mapped_column( - Unicode(255) + UnicodeText ) """The filename extension of the sidecar YAML file in the GitHub repository for GitHub-backed pages. diff --git a/tox.ini b/tox.ini index 420692d..faea677 100644 --- a/tox.ini +++ b/tox.ini @@ -104,7 +104,7 @@ commands = [testenv:cli] description = Run command-line tool against a test database commands = - timessquare {posargs} + times-square {posargs} setenv = TS_ENVIRONMENT_URL = https://test.example.com TS_PATH_PREFIX = /times-square/api