diff --git a/Dockerfile b/Dockerfile index d43a75b..35e9516 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,25 @@ FROM python:3.14-alpine COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +# Set working directory WORKDIR /code + +# Enable bytecode compilation and suppress uv cache warning +ENV UV_COMPILE_BYTECODE=1 +ENV UV_LINK_MODE=copy + +# Install dependencies first for better layer caching +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + uv sync --frozen --no-install-project --no-dev + +# Copy the rest of the application COPY . . -RUN uv sync -ENTRYPOINT ["uv", "run", "app"] + +# Install the project itself +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev + +# Use the direct path to the executable to bypass 'uv run' overhead (~100ms) +ENTRYPOINT ["/code/.venv/bin/app"] diff --git a/mise.toml b/mise.toml index 450814a..9a6e4d0 100644 --- a/mise.toml +++ b/mise.toml @@ -52,20 +52,12 @@ run = "uv lock --upgrade" [tasks.lint] description = "Lint and format code" alias = "l" -run = [ - "uv run ruff check --fix", - "uv run ruff format", - # "uv run pyright", -] +run = "uv run sh -c 'ruff check --fix && ruff format'" [tasks.test] description = "Run tests with coverage" alias = "t" -run = [ - "uv run coverage run -m pytest .", - "uv run coverage report -m", - "uv run coverage xml", -] +run = "uv run sh -c 'coverage run -m pytest . && coverage report -m && coverage xml'" [tasks.all] description = "Full setup from scratch" diff --git a/scripts/rename.py b/scripts/rename.py index 4388ac2..ed0ad91 100644 --- a/scripts/rename.py +++ b/scripts/rename.py @@ -1,6 +1,7 @@ import os import re import shutil +from collections import defaultdict from pathlib import Path from click import ClickException, UsageError, command, echo, option @@ -30,7 +31,7 @@ def main(name: str, description: str, author: str, email: str, github: str): raise ClickException(f"Error: Neither 'project' nor '{source}' directory found.") # 2. File modifications - replacements = [ + replacements_list = [ ("docs/reference/app.md", r"^::: project\.app", f"::: {source}.app"), ("mkdocs.yml", r"^repo_name: .*", f"repo_name: {github}/{name}"), ("mkdocs.yml", r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"), @@ -44,16 +45,25 @@ def main(name: str, description: str, author: str, email: str, github: str): (".github/FUNDING.yml", r"^github: \[.*\]", f"github: [{github}]"), ] - for filepath, pattern, replacement in replacements: + # Group replacements by file to minimize I/O + file_replacements = defaultdict(list) + for filepath, pattern, replacement in replacements_list: + file_replacements[filepath].append((pattern, replacement)) + + for filepath, patterns in file_replacements.items(): path = Path(filepath) if not path.exists(): echo(f"Warning: File {filepath} not found, skipping.") continue content = path.read_text() - new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE) - path.write_text(new_content) - echo(f"Updated {filepath}") + new_content = content + for pattern, replacement in patterns: + new_content = re.sub(pattern, replacement, new_content, flags=re.MULTILINE) + + if new_content != content: + path.write_text(new_content) + echo(f"Updated {filepath}") echo("Project initialization complete.")