The platform's side of D6: git runs on the host, in a gitdir that is never mounted into a container, so repository credentials never reach the model. - adapters/git_workspace.py: LocalGitWorkspaceAdapter implementing RepoWorkspacePort. Bare shared gitdir + a dev work tree mounted at /workspace + one linked worktree per task under work/.worktrees/<id> (excluded via the gitdir's info/exclude, not the project's .gitignore). Explicit --git-dir/--work-tree, core.hooksPath=/dev/null, fsmonitor off, quotePath off, GIT_CONFIG_NOSYSTEM, a private HOME per project, prompts off, one asyncio.Lock per project. Token via GIT_ASKPASS reading a 0600 file; SSH key as a 0600 temp file with a pinned known_hosts. Everything deleted in a finally; only a *path* ever travels through the environment. - The bug this phase existed to find: --git-dir=<shared> does not address a linked worktree, so `git commit` in a task worktree committed straight to `dev`. Operations now address <gitdir>/worktrees/<task_id> explicitly — and never read the worktree's .git file, which a repository can rewrite. - domain/platform/rules.py: is_blocked_address (pure) + assert_remote_host_allowed (resolves, refuses if any address is loopback/private/link-local/metadata). Shared by the git remote test and, later, every deploy target. - ProjectExternalGit domain model + repository; container_delivery wires the real adapter behind GIT_WORKSPACE=local with a resolver that joins the project, its repo and its (decrypted) credentials. Tests: 394 API (+48) against real git and a local bare repo; ruff and mypy clean. Report: docs/plan/reports/agent-3-phase-2.md
76 lines
2.1 KiB
TOML
76 lines
2.1 KiB
TOML
[project]
|
|
name = "smart-org-api"
|
|
version = "0.1.0"
|
|
description = "Smart Organization backend — FastAPI + SQLAlchemy, Clean/Hexagonal architecture."
|
|
requires-python = ">=3.12"
|
|
dependencies = [
|
|
"fastapi>=0.115.0",
|
|
"uvicorn[standard]>=0.34.0",
|
|
"sqlalchemy[asyncio]>=2.0.36",
|
|
"psycopg[binary]>=3.2.3",
|
|
"aiosqlite>=0.20.0",
|
|
"alembic>=1.14.0",
|
|
"pydantic>=2.10.0",
|
|
"pydantic-settings>=2.7.0",
|
|
"email-validator>=2.2.0",
|
|
"PyJWT>=2.10.0",
|
|
"argon2-cffi>=23.1.0",
|
|
"cryptography>=44.0.0",
|
|
"redis>=5.2.0",
|
|
"httpx>=0.28.0",
|
|
"python-multipart>=0.0.20",
|
|
"asyncssh>=2.17.0",
|
|
"aioftp>=0.23.0",
|
|
"mcp>=1.2.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
# Remote sandbox providers. Kept optional: these SDKs are large and move fast,
|
|
# and a default install must run with SANDBOX_PROVIDER=mock and zero credentials.
|
|
# Enable with: pip install -e ".[sandbox-remote]"
|
|
sandbox-remote = [
|
|
"daytona>=0.100",
|
|
"e2b>=1.0",
|
|
]
|
|
dev = [
|
|
"pytest>=8.3.0",
|
|
"pytest-asyncio>=0.25.0",
|
|
"ruff>=0.9.0",
|
|
"mypy>=1.14.0",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["setuptools>=75.0"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[tool.setuptools.packages.find]
|
|
include = ["app*"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py312"
|
|
# Alembic's autogenerated revisions are machine-written: their long lines and
|
|
# import style are not ours to fix, and editing them by hand invites mistakes.
|
|
exclude = ["alembic/versions"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "UP", "B", "C4", "SIM"]
|
|
ignore = ["B008"]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
filterwarnings = ["ignore::DeprecationWarning"]
|
|
# `docker` marks tests that need a real container runtime, `deploy_it` those
|
|
# that need a live sshd/ftp server. Both are skipped unless the matching
|
|
# environment flag is set, so the default run needs no infrastructure.
|
|
markers = [
|
|
"docker: needs a running docker/podman (set SANDBOX_DOCKER_IT=1)",
|
|
"deploy_it: needs a live ssh/ftp test server (set DEPLOY_IT=1)",
|
|
]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
ignore_missing_imports = true
|
|
warn_unused_ignores = false
|