diff --git a/src/fastapi_cloud_cli/commands/deploy/archive.py b/src/fastapi_cloud_cli/commands/deploy/archive.py index 795dcf0..a102e53 100644 --- a/src/fastapi_cloud_cli/commands/deploy/archive.py +++ b/src/fastapi_cloud_cli/commands/deploy/archive.py @@ -1,3 +1,4 @@ +import importlib import logging import re from pathlib import Path, PurePosixPath @@ -43,8 +44,38 @@ def validate_app_directory(v: str | None) -> str | None: AppDirectory = Annotated[str | None, AfterValidator(validate_app_directory)] +def _project_name_from_pyproject(path: Path) -> str: + try: + tomllib = importlib.import_module("tomllib") + except ImportError: + return "" + + if not path.exists(): + return "" + + try: + with path.open("rb") as f: + data = tomllib.load(f) + + project_name = data.get("project", {}).get("name") + if project_name: + return str(project_name) + + poetry_name = data.get("tool", {}).get("poetry", {}).get("name") + if poetry_name: + return str(poetry_name) + + except Exception: + return "" + + return "" + + def _get_app_name(path: Path) -> str: - # TODO: use pyproject.toml to get the app name + pyproject_path = path / "pyproject.toml" + name = _project_name_from_pyproject(pyproject_path) + if name: + return name return path.name diff --git a/tests/test_archive.py b/tests/test_archive.py index cbb8ec5..b87b279 100644 --- a/tests/test_archive.py +++ b/tests/test_archive.py @@ -1,9 +1,10 @@ +import sys from pathlib import Path import fastar import pytest -from fastapi_cloud_cli.commands.deploy.archive import archive +from fastapi_cloud_cli.commands.deploy.archive import _get_app_name, archive @pytest.fixture @@ -163,3 +164,28 @@ def test_archive_includes_hidden_files_but_excludes_env( dst_path / ".config", dst_path / ".config" / "settings.json", } + + +def test_get_app_name_uses_directory_name_when_pyproject_is_missing( + tmp_path: Path, +) -> None: + assert _get_app_name(tmp_path) == tmp_path.name + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_get_app_name_uses_project_name_from_pyproject( + tmp_path: Path, +) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + """ +[project] +name = "my-app" +""", + encoding="utf-8", + ) + + assert _get_app_name(tmp_path) == "my-app" diff --git a/tests/test_cli_deploy.py b/tests/test_cli_deploy.py index 1dbf4a5..f0f4b33 100644 --- a/tests/test_cli_deploy.py +++ b/tests/test_cli_deploy.py @@ -2,6 +2,7 @@ import random import re import string +import sys from datetime import timedelta from pathlib import Path from typing import TypedDict @@ -18,6 +19,7 @@ from typer.testing import CliRunner from fastapi_cloud_cli.cli import app +from fastapi_cloud_cli.commands.deploy.archive import _project_name_from_pyproject from fastapi_cloud_cli.config import Settings from fastapi_cloud_cli.utils.api import StreamLogError, TooManyRetriesError from tests.conftest import ConfiguredApp @@ -2494,3 +2496,91 @@ def test_invalid_large_file_threshold( assert result.exit_code == 2 assert "Invalid value for '--large-file-threshold'" in result.output + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_project_name_from_pyproject_project_section(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + """ +[project] +name = "my-app" +""", + encoding="utf-8", + ) + + result = _project_name_from_pyproject(pyproject) + + assert result == "my-app" + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_project_name_from_pyproject_poetry_section(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + """ +[tool.poetry] +name = "my-poetry-app" +""", + encoding="utf-8", + ) + + result = _project_name_from_pyproject(pyproject) + + assert result == "my-poetry-app" + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_project_name_from_pyproject_missing_file(tmp_path: Path) -> None: + pyproject = tmp_path / "missing.toml" + + result = _project_name_from_pyproject(pyproject) + + assert result == "" + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_project_name_from_pyproject_missing_name(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + """ +[project] +version = "0.1.0" +""", + encoding="utf-8", + ) + + result = _project_name_from_pyproject(pyproject) + + assert result == "" + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="tomllib is only available on Python 3.11+", +) +def test_project_name_from_pyproject_invalid_toml(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + """ +[project +name = "invalid" +""", + encoding="utf-8", + ) + + result = _project_name_from_pyproject(pyproject) + + assert result == ""