Skip to content

Add pyproject.toml as project definition format#2055

Open
kaxil wants to merge 1 commit intomainfrom
kaxilnaik/ai-77-pyprojecttoml-as-project-definition
Open

Add pyproject.toml as project definition format#2055
kaxil wants to merge 1 commit intomainfrom
kaxilnaik/ai-77-pyprojecttoml-as-project-definition

Conversation

@kaxil
Copy link
Copy Markdown
Contributor

@kaxil kaxil commented Mar 25, 2026

Summary

Replaces the Dockerfile-centric project definition with pyproject.toml (PEP 621) for Airflow 3 projects. An Airflow project is a Python project — this aligns with the ecosystem, enables the uv-based standalone runtime, and lays the groundwork for multi-target compilation.

pyproject.toml parser and project format detection

New airflow/project.go with ReadProject(), IsPyProject(), DetectProjectFormat(), and PinRuntimeVersion(). Parses [project] and [tool.astro] sections. Detection requires the [tool.astro] key — a pyproject.toml from ruff or pytest won't trigger new behavior.

astro dev init --format pyproject

New --format flag: pyproject (AF3 only) or dockerfile (default, unchanged). Scaffolds a clean project with pyproject.toml, dags/, tests/, .env, airflow_settings.yaml. Resolves airflow-version and runtime-version from the API. Includes packages.txt and requirements.txt stubs needed by the runtime ONBUILD for Docker mode.

Standalone mode reads pyproject.toml

astro dev start --standalone detects pyproject format and reads versions from pyproject.toml instead of parsing the Dockerfile. User dependencies from [project.dependencies] installed via uv pip install. Legacy Dockerfile path completely unchanged.

Docker mode generates Dockerfile from pyproject.toml

astro dev start --docker (or astro dev start with mode = "docker" in pyproject.toml) generates a Dockerfile at .astro/Dockerfile.pyproject from the project definition. Includes FROM line from runtime-version, USER root / apt-get install / USER astro for system packages. Generated on every build so pyproject.toml edits are always picked up. System package names validated against Debian naming rules.

Runtime version resolution

runtime-version is optional in pyproject.toml. On first dev start or docker build, the CLI resolves the latest runtime for the given airflow-version, pins it back to pyproject.toml, and warns that multiple runtimes may exist per Airflow version (to prevent accidental upgrades on deploy). Mismatches between airflow-version and runtime-version produce a warning.

Mode from pyproject.toml

[tool.astro].mode ("standalone" or "docker") is respected by astro dev start without needing --standalone or --docker flags. CLI flags still take precedence.

Hand-written Dockerfile escape hatch

If a Dockerfile exists in the project root alongside pyproject.toml, the CLI uses it instead of generating one. This supports users who need custom Docker steps beyond what pyproject.toml can express.

Schema

[project]
name = "my-project"
requires-python = ">=3.12"
dependencies = ["pandas>=2.0", "requests"]

[tool.astro]
airflow-version = "3.0.1"
mode = "docker"               # or "standalone"

[tool.astro.docker]
system-packages = ["gcc", "libpq-dev"]

runtime-version is auto-resolved and pinned on first run. Users only need to specify airflow-version.

Design rationale

  • Why [tool.astro] detection, not just pyproject.toml existence? Projects often have pyproject.toml for ruff, pytest, or mypy config. We only activate new behavior when [tool.astro] is explicitly present.
  • Why no [[tool.uv.index]] in the template? The Astronomer index is already controlled by the CLI via --index-url in standalone mode and baked into the Docker image via /etc/uv/uv.toml. Users who need private indexes can add their own [[tool.uv.index]] entries.
  • Why pin runtime-version automatically? One airflow-version can map to multiple runtime versions. Without pinning, dev start could silently resolve to a different runtime between runs, causing inconsistencies between local dev and deployed environments.
  • Why respect hand-written Dockerfile? Power users who need multi-stage builds, custom entrypoints, or non-standard base images can "eject" from generation by placing a Dockerfile in the project root. The CLI detects it and skips generation.
  • Backwards compatibility: No existing project is affected. The new behavior is strictly opt-in via --format pyproject on init or manual creation of pyproject.toml with [tool.astro].

Linear: AI-77

@coveralls-official
Copy link
Copy Markdown

coveralls-official bot commented Mar 25, 2026

Pull Request Test Coverage Report for Build 7fbd087c-0308-4fa9-af77-861aba9664f4

Details

  • 261 of 368 (70.92%) changed or added relevant lines in 8 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+0.2%) to 36.24%

Changes Missing Coverage Covered Lines Changed/Added Lines %
airflow/docker_image.go 4 6 66.67%
airflow/airflow.go 19 23 82.61%
airflow/docker.go 7 11 63.64%
airflow/project.go 85 93 91.4%
cmd/airflow.go 26 37 70.27%
airflow/dockerfile_gen.go 51 70 72.86%
airflow_versions/airflow_versions.go 0 26 0.0%
airflow/standalone.go 69 102 67.65%
Files with Coverage Reduction New Missed Lines %
cmd/airflow.go 1 91.39%
Totals Coverage Status
Change from base Build 144547f3-b79e-4dbb-a890-8df0ad38b23c: 0.2%
Covered Lines: 24708
Relevant Lines: 68179

💛 - Coveralls

@kaxil kaxil force-pushed the kaxilnaik/ai-77-pyprojecttoml-as-project-definition branch 12 times, most recently from e7a8b5c to 362196a Compare March 26, 2026 02:25
Replace the Dockerfile-centric project definition with pyproject.toml
(PEP 621) for Airflow 3 projects. This aligns with the Python ecosystem,
enables the uv-based standalone runtime, and lays the groundwork for
multi-target compilation.

Parser and detection (airflow/project.go):
- ReadProject(), IsPyProject(), DetectProjectFormat(), PinRuntimeVersion()
- Detection requires [tool.astro] to avoid false positives from
  ruff/pytest pyproject.toml files

astro dev init --format pyproject:
- Scaffolds pyproject.toml projects with no Dockerfile or requirements.txt
- Resolves airflow-version and runtime-version from the API
- Validates: AF3 only, mutual exclusion with --from-template

Standalone mode reads pyproject.toml:
- Reads versions and dependencies from [tool.astro] and [project]
- Falls back to Dockerfile parsing for legacy projects

Docker mode generates Dockerfile from pyproject.toml:
- GenerateDockerfile() produces FROM + optional apt-get for system packages
- EnsureDockerfile() called at build time so pyproject.toml edits are
  always picked up
- Generated file written to .astro/Dockerfile.pyproject (gitignored)
- System package names validated against Debian naming rules

Runtime version resolution:
- runtime-version is optional in pyproject.toml — resolved from
  airflow-version on first start/build and pinned back to pyproject.toml
- Mismatch between airflow-version and runtime-version triggers a warning
- Warns users that multiple runtimes exist per Airflow version to prevent
  accidental upgrades on deploy
@kaxil kaxil force-pushed the kaxilnaik/ai-77-pyprojecttoml-as-project-definition branch from 362196a to 06d03a2 Compare March 26, 2026 02:33
@kaxil kaxil marked this pull request as ready for review March 26, 2026 02:35
@kaxil kaxil requested a review from a team as a code owner March 26, 2026 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant