Skip to content

Commit c2c414f

Browse files
committed
ci: add backend linting workflow
- Introduced `lint-backend.yml` GitHub workflow to run lint checks on all backend changes. - Upgraded `setup-python` and `setup-uv` versions for compatibility and reliability. - Configured UV dependency caching in lint workflow to improve efficiency.
1 parent 1a26fc2 commit c2c414f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+509
-542
lines changed

.github/workflows/lint-backend.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Lint Backend
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- "backend/**"
9+
pull_request:
10+
branches:
11+
- master
12+
paths:
13+
- "backend/**"
14+
15+
jobs:
16+
lint:
17+
runs-on: ubuntu-latest
18+
defaults:
19+
run:
20+
working-directory: ./backend
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v5
25+
26+
- name: Set up Python
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: '3.13'
30+
31+
- name: Install uv
32+
uses: astral-sh/setup-uv@v5
33+
with:
34+
version: "0.9.18"
35+
36+
- name: Cache uv dependencies
37+
uses: actions/cache@v4
38+
with:
39+
path: ~/.cache/uv
40+
key: uv-${{ runner.os }}-${{ hashFiles('backend/uv.lock') }}
41+
restore-keys: uv-${{ runner.os }}-
42+
43+
- name: Install dependencies
44+
run: uv sync
45+
46+
- name: Run ruff check
47+
run: uv run ruff check --output-format=github .
48+
49+
- name: Run ruff format check
50+
run: uv run ruff format --check --diff .

.github/workflows/test-backend.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- name: Install uv
4242
uses: astral-sh/setup-uv@v5
4343
with:
44-
version: "0.5.16"
44+
version: "0.9.18"
4545

4646
- name: Cache uv dependencies
4747
uses: actions/cache@v4

backend/alembic/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sqlalchemy import engine_from_config, pool
55

66
from alembic import context
7-
from app.db.models import *
7+
from app.db.models import Base # noqa: F401 - Required for Alembic autogenerate
88

99
# this is the Alembic Config object, which provides
1010
# access to the values within the .ini file in use.

backend/alembic/versions/08393764144d_add_delete_edit_tokens_and_deleted_at.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,35 @@
55
Create Date: 2025-12-17 22:00:02.736745
66
77
"""
8+
89
from collections.abc import Sequence
910

1011
import sqlalchemy as sa
1112

1213
from alembic import op
1314

1415
# revision identifiers, used by Alembic.
15-
revision: str = '08393764144d'
16-
down_revision: str | Sequence[str] | None = '7c45e2617d61'
16+
revision: str = "08393764144d"
17+
down_revision: str | Sequence[str] | None = "7c45e2617d61"
1718
branch_labels: str | Sequence[str] | None = None
1819
depends_on: str | Sequence[str] | None = None
1920

2021

2122
def upgrade() -> None:
2223
"""Upgrade schema."""
2324
# ### commands auto generated by Alembic - please adjust! ###
24-
op.add_column('pastes', sa.Column('edit_token', sa.String(), nullable=True))
25-
op.add_column('pastes', sa.Column('last_updated_at', sa.TIMESTAMP(timezone=True), nullable=True))
26-
op.add_column('pastes', sa.Column('delete_token', sa.String(), nullable=True))
27-
op.add_column('pastes', sa.Column('deleted_at', sa.TIMESTAMP(timezone=True), nullable=True))
25+
op.add_column("pastes", sa.Column("edit_token", sa.String(), nullable=True))
26+
op.add_column("pastes", sa.Column("last_updated_at", sa.TIMESTAMP(timezone=True), nullable=True))
27+
op.add_column("pastes", sa.Column("delete_token", sa.String(), nullable=True))
28+
op.add_column("pastes", sa.Column("deleted_at", sa.TIMESTAMP(timezone=True), nullable=True))
2829
# ### end Alembic commands ###
2930

3031

3132
def downgrade() -> None:
3233
"""Downgrade schema."""
3334
# ### commands auto generated by Alembic - please adjust! ###
34-
op.drop_column('pastes', 'deleted_at')
35-
op.drop_column('pastes', 'delete_token')
36-
op.drop_column('pastes', 'last_updated_at')
37-
op.drop_column('pastes', 'edit_token')
35+
op.drop_column("pastes", "deleted_at")
36+
op.drop_column("pastes", "delete_token")
37+
op.drop_column("pastes", "last_updated_at")
38+
op.drop_column("pastes", "edit_token")
3839
# ### end Alembic commands ###

backend/alembic/versions/4e57d32ab2ac_add_paste_indexes.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,29 @@
55
Create Date: 2025-12-25 20:15:09.567249
66
77
"""
8+
89
from collections.abc import Sequence
910

1011
from alembic import op
1112

1213
# revision identifiers, used by Alembic.
13-
revision: str = '4e57d32ab2ac'
14-
down_revision: str | Sequence[str] | None = '6c5a2c764fb8'
14+
revision: str = "4e57d32ab2ac"
15+
down_revision: str | Sequence[str] | None = "6c5a2c764fb8"
1516
branch_labels: str | Sequence[str] | None = None
1617
depends_on: str | Sequence[str] | None = None
1718

1819

1920
def upgrade() -> None:
2021
"""Upgrade schema."""
2122
# Add indexes to optimize paste queries
22-
op.create_index('idx_pastes_expires_at', 'pastes', ['expires_at'])
23-
op.create_index('idx_pastes_deleted_at', 'pastes', ['deleted_at'])
24-
op.create_index('idx_pastes_created_at', 'pastes', ['created_at'])
23+
op.create_index("idx_pastes_expires_at", "pastes", ["expires_at"])
24+
op.create_index("idx_pastes_deleted_at", "pastes", ["deleted_at"])
25+
op.create_index("idx_pastes_created_at", "pastes", ["created_at"])
2526

2627

2728
def downgrade() -> None:
2829
"""Downgrade schema."""
2930
# Remove indexes
30-
op.drop_index('idx_pastes_created_at', 'pastes')
31-
op.drop_index('idx_pastes_deleted_at', 'pastes')
32-
op.drop_index('idx_pastes_expires_at', 'pastes')
31+
op.drop_index("idx_pastes_created_at", "pastes")
32+
op.drop_index("idx_pastes_deleted_at", "pastes")
33+
op.drop_index("idx_pastes_expires_at", "pastes")

backend/alembic/versions/6c5a2c764fb8_add_compression_fields.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,31 @@
55
Create Date: 2025-12-25 13:23:44.911166
66
77
"""
8+
89
from collections.abc import Sequence
910

1011
import sqlalchemy as sa
1112

1213
from alembic import op
1314

1415
# revision identifiers, used by Alembic.
15-
revision: str = '6c5a2c764fb8'
16-
down_revision: str | Sequence[str] | None = '0ed6c1042110'
16+
revision: str = "6c5a2c764fb8"
17+
down_revision: str | Sequence[str] | None = "0ed6c1042110"
1718
branch_labels: str | Sequence[str] | None = None
1819
depends_on: str | Sequence[str] | None = None
1920

2021

2122
def upgrade() -> None:
2223
"""Upgrade schema."""
2324
# ### commands auto generated by Alembic - please adjust! ###
24-
op.add_column('pastes', sa.Column('is_compressed', sa.Boolean(), server_default='false', nullable=False))
25-
op.add_column('pastes', sa.Column('original_size', sa.Integer(), nullable=True))
25+
op.add_column("pastes", sa.Column("is_compressed", sa.Boolean(), server_default="false", nullable=False))
26+
op.add_column("pastes", sa.Column("original_size", sa.Integer(), nullable=True))
2627
# ### end Alembic commands ###
2728

2829

2930
def downgrade() -> None:
3031
"""Downgrade schema."""
3132
# ### commands auto generated by Alembic - please adjust! ###
32-
op.drop_column('pastes', 'original_size')
33-
op.drop_column('pastes', 'is_compressed')
33+
op.drop_column("pastes", "original_size")
34+
op.drop_column("pastes", "is_compressed")
3435
# ### end Alembic commands ###

backend/alembic/versions/9e8ceaeba260_initial_migration.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
Create Date: 2025-12-08 19:06:52.326528
66
77
"""
8+
89
from collections.abc import Sequence
910

1011
import sqlalchemy as sa
1112

1213
from alembic import op
1314

1415
# revision identifiers, used by Alembic.
15-
revision: str = '9e8ceaeba260'
16+
revision: str = "9e8ceaeba260"
1617
down_revision: str | Sequence[str] | None = None
1718
branch_labels: str | Sequence[str] | None = None
1819
depends_on: str | Sequence[str] | None = None
@@ -21,23 +22,24 @@
2122
def upgrade() -> None:
2223
"""Upgrade schema."""
2324
# ### commands auto generated by Alembic - please adjust! ###
24-
op.create_table('pastes',
25-
sa.Column('id', sa.UUID(), server_default=sa.text('gen_random_uuid()'), nullable=False),
26-
sa.Column('title', sa.String(length=255), nullable=False),
27-
sa.Column('content_path', sa.String(), nullable=False),
28-
sa.Column('content_language', sa.String(), server_default='plain_text', nullable=False),
29-
sa.Column('expires_at', sa.DateTime(), nullable=True),
30-
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
31-
sa.Column('content_size', sa.Integer(), nullable=False),
32-
sa.Column('creator_ip', sa.String(), nullable=True),
33-
sa.Column('creator_user_agent', sa.String(), nullable=True),
34-
sa.PrimaryKeyConstraint('id')
35-
)
25+
op.create_table(
26+
"pastes",
27+
sa.Column("id", sa.UUID(), server_default=sa.text("gen_random_uuid()"), nullable=False),
28+
sa.Column("title", sa.String(length=255), nullable=False),
29+
sa.Column("content_path", sa.String(), nullable=False),
30+
sa.Column("content_language", sa.String(), server_default="plain_text", nullable=False),
31+
sa.Column("expires_at", sa.DateTime(), nullable=True),
32+
sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
33+
sa.Column("content_size", sa.Integer(), nullable=False),
34+
sa.Column("creator_ip", sa.String(), nullable=True),
35+
sa.Column("creator_user_agent", sa.String(), nullable=True),
36+
sa.PrimaryKeyConstraint("id"),
37+
)
3638
# ### end Alembic commands ###
3739

3840

3941
def downgrade() -> None:
4042
"""Downgrade schema."""
4143
# ### commands auto generated by Alembic - please adjust! ###
42-
op.drop_table('pastes')
44+
op.drop_table("pastes")
4345
# ### end Alembic commands ###

backend/app/api/subroutes/well-known.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from fastapi import APIRouter
22

3-
well_known_route = APIRouter(
4-
prefix="/.well-known",
5-
tags=[".well-known"]
6-
)
3+
well_known_route = APIRouter(prefix="/.well-known", tags=[".well-known"])
74

85

96
@well_known_route.get("/security.txt")

backend/app/config.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
def validate_rate_limit(value: str) -> str:
1616
"""Validate rate limit format (e.g., '10/minute', '100/hour')."""
1717
if not RATE_LIMIT_PATTERN.match(value):
18-
raise ValueError(
19-
f"Invalid rate limit format: '{value}'. " "Expected format: '<number>/<second|minute|hour|day>'"
20-
)
18+
raise ValueError(f"Invalid rate limit format: '{value}'. Expected format: '<number>/<second|minute|hour|day>'")
2119
return value
2220

2321

@@ -34,7 +32,7 @@ class Config(BaseSettings):
3432
)
3533

3634
PORT: int = Field(default=8000, validation_alias="APP_PORT")
37-
HOST: str = Field(default="0.0.0.0", validation_alias="APP_HOST")
35+
HOST: str = Field(default="0.0.0.0", validation_alias="APP_HOST") # noqa: S104 - Bind to all interfaces for container deployment
3836

3937
# DB
4038
DATABASE_URL: str = Field(

backend/app/containers.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919

2020

2121
@asynccontextmanager
22-
async def _engine_resource(
23-
db_url: str, echo: bool = False
24-
) -> AsyncIterator[AsyncEngine]:
22+
async def _engine_resource(db_url: str, echo: bool = False) -> AsyncIterator[AsyncEngine]:
2523
engine = create_async_engine(db_url, echo=echo, future=True)
2624
try:
2725
yield engine
@@ -215,6 +213,4 @@ class Container(containers.DeclarativeContainer):
215213
distributed_lock,
216214
)
217215

218-
paste_service = providers.Factory(
219-
PasteService, session_factory, cleanup_service, storage_client
220-
)
216+
paste_service = providers.Factory(PasteService, session_factory, cleanup_service, storage_client)

0 commit comments

Comments
 (0)