feat: add astro dev local for Docker-free local development#2012
feat: add astro dev local for Docker-free local development#2012
astro dev local for Docker-free local development#2012Conversation
Pull Request Test Coverage Report for Build e9f025c7-7a3b-4d40-bb7a-fb9f2bd0045eDetails
💛 - Coveralls |
…pment Add a new `astro dev standalone` command that runs Airflow locally without Docker, using `airflow standalone` and `uv` for dependency management. This provides a dramatically faster dev loop for Airflow 3 projects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d95fbe6 to
1e42a1e
Compare
for more information, see https://pre-commit.ci
Standalone mode now works with both Airflow 2 (runtime 4.0.0+) and Airflow 3 (runtime 3.x). The health check endpoint, image registry, and settings version are determined dynamically based on the detected Airflow major version. Changes: - Accept airflowMajor "2" or "3" (was "3" only) - Health check: /health + webserver (AF2) vs /api/v2/monitor/health + api-server (AF3) - Image registry: quay.io/astronomer/astro-runtime (AF2) vs astrocrpublic.azurecr.io/runtime (AF3) - Settings version passed dynamically (was hardcoded 3) - Kill/reset cleans up both AF2 and AF3 credential files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
Default `astro dev standalone` to background mode — the CLI starts the airflow process, writes a PID file, waits for the health check, prints status, and returns. A `--foreground` flag preserves the previous stream-to-terminal behaviour. New subcommands: - `astro dev standalone stop` — SIGTERM the process group, clean up PID file - `astro dev standalone logs [-f]` — dump or tail the log file Also wires `reset` to stop a running process before cleaning up files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9683aac to
2593b32
Compare
|
@jlaneve I know this mirrors the airflow standalone command but would an shorter alias be useful too? |
airflow/standalone.go
Outdated
| standaloneLogFile = "airflow.log" | ||
| defaultStandalonePort = "8080" | ||
| standaloneIndexURL = "https://pip.astronomer.io/v2/" | ||
| standalonePythonVer = "3.12" |
There was a problem hiding this comment.
You'd want to capture the python version from the image they have in their dockerfile as well.
| requirementsPath := filepath.Join(s.airflowHome, "requirements.txt") | ||
| installArgs := []string{ | ||
| "pip", "install", | ||
| fmt.Sprintf("apache-airflow==%s", airflowVersion), |
There was a problem hiding this comment.
This alone isn't enough - you'd need to capture the full set of "stuff" we install by default. And that varies a bit.
Airflow 2 has the concept of a slim and "full" image. The latter has a number of extra providers installed by default. Both have a set of providers. From memory, it also varies a bit based on the Runtime version as well, so you likely can't have a static list across the board here.
Airflow 3 is only slim, but there is a bit of risk here that changes in the default set would cause environment drift. Ideally we aren't hard coding those there to get stale.
…install - Remove airflowMajor field and AF2 code paths (standalone is AF3-only) - Replace Docker-based constraint extraction with HTTP fetch from pip.astronomer.io/runtime-constraints - Implement 2-step install: first install airflow with full constraints, then install user requirements with only airflow/task-sdk version locks - Add parsePackageVersionFromConstraints helper for task-sdk version - Remove runtimeImageName, execDockerRun, constraintsFileInImage - Simplify healthEndpoint to always return AF3 endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b1a21b5 to
e91a7ad
Compare
…an project root - Fix CDN base URLs: constraintsBaseURL → cdn.astronomer.io/runtime-constraints - Add freezeBaseURL (cdn.astronomer.io/runtime-freeze) for full 191-package list - getConstraints() now fetches and caches both constraints + freeze files; returns freeze path for use as pip -c arg in step 1 install - Move "already running" check to top of Start() before any install work - Add ensureCredentials() to seed passwords file with admin:admin on first run - Add readCredentials() to display username/password in startup output - Redirect AIRFLOW_HOME → .astro/standalone/ so airflow.cfg, airflow.db, and logs/ all live there instead of cluttering the project root - Set AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_PASSWORDS_FILE to .astro/standalone/ - Update Kill() to clean up .venv/ and .astro/standalone/ only - Update tests: freeze file routing in fetch mock, AIRFLOW_HOME assertions, new TestStandaloneEnsureCredentials, TestStandaloneReadCredentials tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Standalone.Build() stub (errStandaloneNotSupported) to satisfy the updated ContainerHandler interface which gained a Build method in main - Resolve conflict in cmd/airflow_test.go: keep both TestAirflowStandalone and new TestAirflowBuild from main, preserving all subtests from both Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename the command from 'standalone' to 'local' for a more intuitive UX. The internal Go types (Standalone, StandaloneHandlerInit) are unchanged. - cmd/airflow.go: Use:"standalone" → Use:"local", all function/var names - cmd/airflow_hooks.go: EnsureStandaloneRuntime → EnsureLocalRuntime - cmd/airflow_test.go: update all test names and assertions - airflow/standalone.go: add Build() stub (satisfies updated ContainerHandler) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
astro dev standalone for Docker-free local developmentastro dev local for Docker-free local development
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parse the optional -python-X.Y suffix from runtime image tags (e.g. 3.1-12-python-3.11) instead of hardcoding Python 3.12. Falls back to 3.12 (the default for all Runtime 3.x images) when no suffix is present. Cache filenames now include the Python version to avoid stale lookups when switching between Python versions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem
Local Airflow development with
astro dev startrequires Docker Desktop and spins up 6 containers (Postgres, db-migration, api-server, scheduler, triggerer, dag-processor). This takes ~70 seconds for a cold start and consumes significant memory/CPU. For iterating on DAGs during development, this is slow and heavyweight.Solution
Add a new
astro dev localcommand that runs Airflow natively on the host without Docker, using:airflow standalone— Airflow's built-in single-process mode (SQLite, all components in one process)uv— fast Python package manager for venv creation and dependency installationDockerfileandrequirements.txt, zero migration neededStartup times
astro dev start(Docker)astro dev localCommands
How it works
Dockerfileto extract the runtime image taguvis installedcdn.astronomer.io(cached in.astro/standalone/):cdn.astronomer.io/runtime-freeze) — full ~191-package list used aspip -cconstraints for reproducible installscdn.astronomer.io/runtime-constraints) — 3-line file for extractingairflowandtask-sdkversion pinsapache-airflowwith the full freeze file as constraints (reproduces the exact runtime environment)requirements.txtwith onlyapache-airflowandapache-airflow-task-sdkversion locks (gives the resolver freedom to satisfy user dependencies without the full freeze blocking)simple_auth_manager_passwords.json.generatedfile withadmin:admincredentials (no-op if file already exists, preserving any custom credentials)airflow_settings.yaml(connections, variables, pools)airflow standalone, redirects output toairflow.log, writes a PID file, runs health check, prints status with credentials, and returns--foreground): Streams output to terminal, blocks on process, Ctrl+C sends SIGTERM to process groupFlags
--env/-e.envfile (default:.env)--settings-file/-sairflow_settings.yaml)--wait1m)--foreground/-f--workspace-id/-w--deployment-id/-dState files
All generated files are kept inside
.astro/standalone/— the project root stays clean (no strayairflow.cfg,airflow.db,logs/, or passwords files):Authentication
Standalone mode uses Airflow 3's
SimpleAuthManager. On first run,admin:admincredentials are seeded automatically. Credentials are displayed in the startup output:The passwords file is preserved across restarts — credentials can be changed by editing
.astro/standalone/simple_auth_manager_passwords.json.generated.Prerequisites
uvmust be installed (installation guide)Implementation
New files
airflow/standalone.go—Standalonestruct implementingContainerHandlerinterfaceairflow/standalone_test.go— unit tests (background, foreground, stop, logs, PS, edge cases)Modified files
cmd/airflow.go—localcommand withstop,logs,resetsubcommands +--foregroundflagcmd/airflow_hooks.go—EnsureLocalRuntimepre-run hook (skips Docker init)cmd/airflow_test.go— command-layer tests for all subcommandsairflow/container.go—StandaloneHandlerInitfactory functionsettings/settings.go—SetExecAirflowCommandto allow standalone settings injectionTest plan
go test ./airflow/ ./cmd/ ./settings/)go test -race ./airflow/)astro dev localbackgrounds, PID file written, health check passes, CLI returnsastro dev localwhile running → "already running" error (check happens before install)astro dev local logsprints log file contentastro dev local stopsends SIGTERM, process exits, PID file removedastro dev local stopwhen not running → graceful messageastro dev local --foregroundstreams output, exits on terminationastro dev local resetstops running process then cleans up all filesrequirements.txtwith extra providers (e.g.apache-airflow-providers-http) installs correctly.astro/standalone/, project root stays cleanKnown limitations / future work
🤖 Generated with Claude Code