+ "source": "#| export\ndef python_app(port=8000, cmd=None, im='python:3.13-slim', wd='/app', pkgs=None,\n vols=None, multistage=True, uv_image='ghcr.io/astral-sh/uv:python3.13-bookworm', healthcheck=None, req=False):\n 'Python app Dockerfile. multistage=True (default): uv-builder → slim. False: single-stage with uv binary copy. req=True: use requirements.txt instead of pyproject.toml (forces multistage=False).'\n if multistage:\n df = Dockerfile().with_uv(uv_image, im, wd).copy(wd, wd, from_='builder').env('PATH', f'{wd}/.venv/bin:$PATH').packages(*listify(pkgs))\n else: df = Dockerfile().from_(im).workdir(wd).packages(*listify(pkgs)).inst_uv(req=req, wd=wd).copy('.', '.')\n if vols: df = df.run('mkdir -p ' + ' '.join(listify(vols)))\n if healthcheck: df = df.healthcheck(f'curl -f http://localhost:{port}{healthcheck}', i='30s', t='5s', r='3')\n _cmd = cmd or ['python', 'main.py']\n return df.expose(port).cmd(_cmd if isinstance(_cmd, list) else _cmd.split())\n\ndef fastapi_react(port=8000, node_version='20', frontend_dir='frontend', build_dir='dist',\n image='python:3.13-slim', pkgs=None, healthcheck='/health', multistage=False,\n uv_image='ghcr.io/astral-sh/uv:python3.13-bookworm'):\n 'Two-stage (default) or three-stage (multistage=True) Dockerfile: Node.js frontend + Python/FastAPI backend'\n df = (Dockerfile().from_(f'node:{node_version}-slim', as_='frontend')\n .workdir('/build').copy(f'{frontend_dir}/package*.json', '.')\n .run('npm ci').copy(frontend_dir, '.').run('npm run build'))\n if multistage: df = df.with_uv(uv_image, image, '/app').copy('/app', '/app', from_='builder').env('PATH', '/app/.venv/bin:$PATH').packages(*listify(pkgs))\n else: df = df.from_(image).workdir('/app').packages(*listify(pkgs)).inst_uv(wd='/app').copy('.', '.')\n df = df.copy(f'/build/{build_dir}', '/app/static', from_='frontend')\n if healthcheck: df = df.healthcheck(f'curl -f http://localhost:{port}{healthcheck}', i='30s', t='5s', r='3')\n return df.expose(port).cmd(['uvicorn', 'main:app', '--host', '0.0.0.0', f'--port={port}'])\n\ndef go_app(port=8080, go_version='1.22', binary='app', runtime='gcr.io/distroless/static', cmd=None, cgo=False):\n 'Two-stage Go Dockerfile: go compiler + go mod cache → distroless runtime'\n df = (Dockerfile().from_(f'golang:{go_version}-alpine', as_='builder')\n .workdir('/src').copy('go.mod', '.').copy('go.sum', '.')\n .run_mount('go mod download', target='/go/pkg/mod')\n .copy('.', '.').env('CGO_ENABLED', '0' if not cgo else '1')\n .run(f'go build -ldflags=\"-s -w\" -o /{binary} .').from_(runtime)\n .copy(f'/{binary}', f'/{binary}', from_='builder').expose(port))\n return df.cmd(cmd or [f'/{binary}'])\n\ndef rust_app(port=8080, rust_version='1', binary='app', runtime='gcr.io/distroless/static', features=None):\n 'Two-stage Rust Dockerfile: cargo build --release → distroless runtime'\n build_cmd = 'cargo build --release' + (f' --features {features}' if features else '')\n df = (Dockerfile().from_(f'rust:{rust_version}-slim-bookworm', as_='builder').workdir('/src')\n .copy('.', '.').run_mount(build_cmd, target='/usr/local/cargo/registry').from_(runtime)\n .copy(f'/src/target/release/{binary}', f'/{binary}', from_='builder').expose(port))\n return df.cmd([f'/{binary}'])\n\ndef node_app(port=3000, node_version='20', cmd=None, build_cmd='npm run build', static=False):\n 'Node.js Dockerfile. static=True does two-stage build → serve dist/; False runs node directly.'\n df = (Dockerfile().from_(f'node:{node_version}-slim', as_='builder' if static else None)\n .workdir('/app').copy('package*.json', '.').run('npm ci').copy('.', '.'))\n if static:\n return (df.run(build_cmd).from_(f'node:{node_version}-slim').workdir('/app')\n .run('npm install -g serve').copy('/app/dist', '.', from_='builder')\n .expose(port).cmd(cmd or ['serve', '-s', '.', '-l', str(port)]))\n return df.expose(port).cmd(cmd or ['node', 'index.js'])\n\nfasthtml_app = bind(python_app, port=5001, cmd=['python', 'app.py'], multistage=False)\n\ndef detect_app(path='.', multistage=True, use_railpack=False, **kw):\n 'Detect project type and return the appropriate Dockerfile. use_railpack=True falls back to rp_build() for unsupported stacks (PHP, Java, Ruby, .NET, Elixir, Deno, C++ etc.) — returns tag str instead of Dockerfile in that case. For **kw see each app builder.'\n p = Path(path)\n has = lambda f: (p/f).exists()\n read = lambda f: (p/f).read_text().lower() if has(f) else ''\n pyp, req= has('pyproject.toml'), has('requirements.txt')\n has_py = pyp or req\n if has('Cargo.toml'): return rust_app(**kw)\n if has('go.mod'): return go_app(**kw)\n if has('package.json') and has_py: return fastapi_react(multistage=multistage, **kw)\n if has('package.json'): return node_app(**kw)\n if pyp and 'python-fasthtml' in read('pyproject.toml'): return fasthtml_app(multistage=multistage, **kw)\n if pyp: return python_app(multistage=multistage, **kw)\n if req: return python_app(multistage=False, req=True, **kw)\n if use_railpack: return rp_build(path=path, **kw)\n raise ValueError(f'Cannot detect project type in {path!r}. Try use_railpack=True for PHP, Java, Ruby, .NET, Elixir, Deno etc.')"
0 commit comments