Skip to content

Commit 7b2fa14

Browse files
authored
Merge pull request #2 from flamingo-run/feature/first-version
First Ever Version
2 parents 586e69b + 03aeff7 commit 7b2fa14

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3617
-2
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Setup FastAPI Cloudflow Environment
2+
description: Common environment setup for all FastAPI Cloudflow jobs
3+
4+
inputs:
5+
github-token:
6+
description: 'GitHub token for authentication'
7+
required: false
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Install uv
14+
uses: astral-sh/setup-uv@v4
15+
- name: Install Task
16+
uses: arduino/setup-task@v2
17+
with:
18+
version: 3.x
19+
repo-token: ${{ inputs.github-token }}
20+
- name: Install dependencies
21+
run: task install-dev
22+
shell: bash

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
lint:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: ./.github/actions/setup-env
14+
with:
15+
github-token: ${{ secrets.GITHUB_TOKEN }}
16+
- name: Lint with Ruff and Ty
17+
run: task lint
18+
19+
test:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: ./.github/actions/setup-env
24+
with:
25+
github-token: ${{ secrets.GITHUB_TOKEN }}
26+
- name: Run tests
27+
run: task test
28+
- name: Report Coverage
29+
run: uv run coverage lcov
30+
- uses: qltysh/qlty-action/coverage@v1
31+
with:
32+
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
33+
files: coverage.lcov
34+
35+
build:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- uses: actions/checkout@v4
39+
- uses: ./.github/actions/setup-env
40+
with:
41+
github-token: ${{ secrets.GITHUB_TOKEN }}
42+
- name: Build package
43+
run: task build

.github/workflows/publish.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Tag & Release Package
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
id-token: write
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: ./.github/actions/setup-env
17+
with:
18+
github-token: ${{ secrets.GITHUB_TOKEN }}
19+
- name: Detect version upgrade
20+
id: versioning
21+
run: |
22+
package_version=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
23+
echo "package_version=$package_version" >> $GITHUB_OUTPUT
24+
upgraded=$(git tag --list | grep -q "${package_version}$" && echo "false" || echo "true")
25+
echo "upgraded=$upgraded" >> $GITHUB_OUTPUT
26+
pre_release=$([[ $package_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "false" || echo "true")
27+
echo "pre_release=$pre_release" >> $GITHUB_OUTPUT
28+
main_branch_release=$([[ $pre_release == "false" && $GITHUB_REF_NAME == "main" ]] && echo "true" || echo "false")
29+
alternative_branch_release=$([[ $pre_release == "true" && $GITHUB_REF_NAME != "main" ]] && echo "true" || echo "false")
30+
should_release=$([[ $upgraded == "true" && ($main_branch_release == "true" || $alternative_branch_release == "true") ]] && echo "true" || echo "false")
31+
echo "should_release=$should_release" >> $GITHUB_OUTPUT
32+
echo "upgraded=$upgraded"
33+
echo "pre_release=$pre_release"
34+
echo "git_ref=$GITHUB_REF_NAME"
35+
echo "main_branch_release=$main_branch_release"
36+
echo "alternative_branch_release=$alternative_branch_release"
37+
echo "should_release=$should_release"
38+
- name: Create Release
39+
if: ${{ steps.versioning.outputs.should_release == 'true' }}
40+
run: gh release create ${{ steps.versioning.outputs.package_version }} --generate-notes
41+
env:
42+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
- name: Build & Publish package
44+
if: ${{ steps.versioning.outputs.should_release == 'true' }}
45+
run: |
46+
task build
47+
uv publish

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

.vscode/settings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"[python]": {
3+
"editor.formatOnSave": true,
4+
"editor.codeActionsOnSave": {
5+
"source.fixAll.ruff": "always",
6+
"source.organizeImports.ruff": "always"
7+
},
8+
"editor.detectIndentation": false,
9+
"editor.tabSize": 4,
10+
"editor.defaultFormatter": "charliermarsh.ruff",
11+
"editor.rulers": [
12+
120
13+
],
14+
},
15+
"python.analysis.autoImportCompletions": true,
16+
"python.formatting.provider": "none",
17+
"python.languageServer": "None",
18+
"python.testing.unittestEnabled": false,
19+
"python.testing.pytestEnabled": true,
20+
"python.defaultInterpreterPath": "${workspaceRoot}/.venv/bin/python",
21+
"debug.allowBreakpointsEverywhere": true,
22+
"ruff.nativeServer": "on"
23+
}

CONTRIBUTING.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## Contributing
2+
3+
Thanks for helping improve fastapi-cloudflow! This guide explains how to develop locally, run tests, and the project structure expectations so contributions stay consistent.
4+
5+
### Prerequisites
6+
- Python 3.13
7+
- [uv](https://github.com/astral-sh/uv) for env/install
8+
9+
### Setup
10+
```
11+
uv sync
12+
```
13+
14+
### Commands (local)
15+
- Lint: `uv run ruff check --fix .`
16+
- Types: `uv run ty check`
17+
- Unit tests: `uv run pytest -q tests/unit`
18+
- Codegen tests (snapshots): `uv run pytest -q tests/codegen`
19+
- First run creates snapshots under `tests/codegen/fixtures/yaml` if missing; commit them
20+
- Full test suite: `uv run pytest -q`
21+
22+
### Smoke tests
23+
Run end-to-end against deployed Workflows (requires Google Cloud project and a deployed example app):
24+
```
25+
export GOOGLE_CLOUD_PROJECT=<your-project>
26+
uv run -q python tests/smoke/run_smoke.py --region us-central1
27+
```
28+
29+
### Project structure
30+
- `src/fastapi_cloudflow/`
31+
- The reusable library (FastAPI-first) and CLI. Includes a FastAPI runtime (`attach_to_fastapi`) plus codegen/CLI. Keep it typed and well-tested
32+
33+
- `examples/app/`
34+
- A realistic FastAPI app showcasing multi-step, playful workflows
35+
- Naming: files should reflect the workflow domain (e.g., `echo_name.py`, `post_story.py`, `jokes.py`)
36+
- Every workflow is multi-step and demonstrates transformations; avoid duplicates covering the exact same feature
37+
38+
- `tests/`
39+
- `unit/`: FastAPI TestClient calls each step endpoint (`/steps/<name>`). Scope: step contracts (Pydantic I/O), route registration, and runtime error paths (422s for malformed/missing body, wrapper support)
40+
- `codegen/`: Runs the CLI to generate YAML and compares full-file snapshots under `tests/codegen/fixtures/yaml`. Scope: codegen stability and header/auth/timeout/ArgExpr emission
41+
- `smoke/`: Python runner that deploys Workflows and invokes them via gcloud, asserting final outputs. Scope: end-to-end validation of the example app + generated Workflows

README.md

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,124 @@
1-
# fastapi-google-workflows
2-
Framework to create Google Cloud Workflows using FastAPI server
1+
[![Code Coverage](https://qlty.sh/gh/flamingo-run/projects/fastapi-google-workflows/coverage.svg)](https://qlty.sh/gh/flamingo-run/projects/fastapi-google-workflows)
2+
[![Maintainability](https://qlty.sh/gh/flamingo-run/projects/fastapi-google-workflows/maintainability.svg)](https://qlty.sh/gh/flamingo-run/projects/fastapi-google-workflows)
3+
[![CI](https://github.com/flamingo-run/fastapi-cloud-workflows/actions/workflows/ci.yml/badge.svg)](https://github.com/flamingo-run/fastapi-cloud-workflows/actions/workflows/ci.yml)
4+
5+
6+
# FastAPI Cloudflow
7+
8+
Typed, ergonomic Google Cloud Workflows backed by FastAPI apps.
9+
10+
Write typed steps as normal async functions, compose them with `>>`, and generate/deploy Cloud Workflows while the framework exposes first-class FastAPI step endpoints for you.
11+
12+
## Why it’s useful
13+
- Define workflows in pure, typed Python (Pydantic IO models)
14+
- Reuse your FastAPI app: framework attaches `/steps/<name>` endpoints automatically
15+
- Generate stable Cloud Workflows YAML from your Python composition
16+
- Works great with CI (codegen snapshots) and smoke tests against real GCP
17+
18+
## Tiny example
19+
```python
20+
from pydantic import BaseModel
21+
from fastapi_cloudflow import Context, step, workflow
22+
23+
class CreateOrder(BaseModel):
24+
account_id: int
25+
sku: str
26+
qty: int
27+
28+
class OrderDraft(BaseModel):
29+
order_id: str
30+
status: str
31+
32+
class OrderPlaced(BaseModel):
33+
order_id: str
34+
status: str
35+
36+
@step(name="price-order")
37+
async def price_order(ctx: Context, data: CreateOrder) -> OrderDraft:
38+
return OrderDraft(order_id="o-123", status="priced")
39+
40+
@step(name="confirm-order")
41+
async def confirm_order(ctx: Context, data: OrderDraft) -> OrderPlaced:
42+
return OrderPlaced(order_id=data.order_id, status="approved")
43+
44+
ORDER_FLOW = (workflow("order-flow") >> price_order >> confirm_order).build()
45+
```
46+
- Run `fastapi-cloudflow build` and you’ll get `order-flow.yaml`
47+
- Attach to your FastAPI app and the framework exposes `POST /steps/price-order` with typed IO
48+
49+
## How it works (high-level)
50+
```mermaid
51+
graph TD
52+
%% GCP runtime
53+
subgraph GCP
54+
subgraph CloudRun
55+
subgraph "FastAPI Server"
56+
subgraph "steps endpoints"
57+
StepCode["steps code"]
58+
end
59+
Other["other endpoints"]
60+
end
61+
end
62+
subgraph CloudWorkflows
63+
subgraph Workflow
64+
WFStep["Step"]
65+
end
66+
end
67+
end
68+
69+
%% CD pipeline
70+
subgraph "Continuous Delivery"
71+
subgraph Codegen
72+
YAML["YAML"]
73+
end
74+
Deploy["Deployment"]
75+
end
76+
77+
Codebase["Codebase"] --> Codegen
78+
Codebase --> Deploy
79+
Codegen --> YAML
80+
YAML --> Workflow
81+
WFStep -->|http.post| StepCode
82+
StepCode -->|JSON| WFStep
83+
Deploy --> CloudRun
84+
```
85+
86+
- You write typed steps and compose with `workflow(...) >> step_a >> step_b`
87+
- The CLI emits Cloud Workflows YAML that uses `http.post` to call the attached FastAPI step endpoints
88+
- The framework injects a workflow context into each request (headers include name/run-id), and returns typed JSON bodies
89+
90+
## Try the examples
91+
- See `examples/app/flows`: playful multi-step workflows
92+
- `echo_name.py` → echo-name-flow: echo → extract → shout
93+
- `post_story.py` → post-story-flow: build story → POST external → summarize
94+
- `jokes.py` → joke-flow: fetch → split → rate
95+
96+
## Codegen & tests
97+
- Codegen snapshots: `uv run -q pytest -q tests/codegen` (full-file YAML equality)
98+
- Unit tests: `uv run -q pytest -q tests/unit` (hits `/steps/<name>` endpoints with TestClient)
99+
- Smoke tests: `uv run -q python tests/smoke/run_smoke.py --region us-central1` (requires GCP & deployed example)
100+
101+
## Supported features (Cloud Workflows)
102+
103+
| Feature | Status | Notes |
104+
| --- | --- | --- |
105+
| HTTP calls (http.get/post/…) || `HttpStep`; custom headers, method |
106+
| Auth to Cloud Run (OIDC audience) || Python steps bake OIDC with `audience=BASE_URL` |
107+
| Step timeouts || `HttpStep(timeout=…)` emits seconds |
108+
| Variables / assignment || payload propagation + adapters/typed steps |
109+
| Expressions (concat/env/params) || `Arg.env/param/ctx`, string concat and path join |
110+
| Sequential composition || `workflow(...) >> step_a >> step_b` |
111+
| Workflow input/output || single `payload` param; final `return: ${payload}` |
112+
| Error surfacing || HTTP errors propagate; FastAPI returns typed 4xx/5xx |
113+
| Retries || planned `RetryPolicy` emission |
114+
| Try/catch || not yet |
115+
| Conditionals / switch || not yet |
116+
| Loops || not yet |
117+
| Parallel branches / join || not yet |
118+
| Subworkflows / call other workflows || not yet |
119+
| GCP connectors / direct service calls || not yet |
120+
121+
## Next steps
122+
- Open CONTRIBUTING.md for local setup, structure, and contribution checklist
123+
- Use descriptive names for workflows/steps and prefer multi-step workflows that show transformations
124+

Taskfile.yml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
version: '3'
2+
3+
tasks:
4+
install:
5+
desc: Install production dependencies
6+
cmds:
7+
- uv sync --no-dev
8+
9+
install-dev:
10+
desc: Install development dependencies
11+
cmds:
12+
- uv sync --group docs --group dev
13+
- uv pip install -e .
14+
15+
install-docs:
16+
desc: Install documentation dependencies
17+
cmds:
18+
- uv sync --group docs
19+
20+
update:
21+
desc: Update dependencies
22+
cmds:
23+
- uv lock --upgrade
24+
25+
test:
26+
desc: Run tests
27+
deps: [install-dev]
28+
cmds:
29+
- uv run pytest
30+
31+
lint:
32+
desc: Run linting
33+
cmds:
34+
- uv run ruff check .
35+
- uv run ruff format --check .
36+
- uv run ty check
37+
38+
format:
39+
desc: Format code
40+
cmds:
41+
- uv run ruff format .
42+
- uv run ruff check --fix --unsafe-fixes .
43+
44+
clean:
45+
desc: Clean build artifacts
46+
cmds:
47+
- rm -rf build/
48+
- rm -rf dist/
49+
- rm -rf src/*.egg-info/
50+
- rm -rf .coverage
51+
- rm -rf htmlcov/
52+
53+
build:
54+
desc: Build package
55+
deps: [clean]
56+
cmds:
57+
- uv build
58+
59+
publish:
60+
desc: Publish to PyPI
61+
deps: [build]
62+
cmds:
63+
- uv publish --token $(PYPI_API_TOKEN)
64+
65+
docs-build:
66+
desc: Build the documentation
67+
deps: [install-docs]
68+
cmds:
69+
- uv run mkdocs build --config-file ./mkdocs.yml
70+
71+
docs-serve:
72+
desc: Serve the documentation locally
73+
deps: [install-docs]
74+
cmds:
75+
- uv run mkdocs serve --config-file ./mkdocs.yml
76+
77+
docs-deploy:
78+
desc: Deploy documentation to GitHub Pages
79+
deps: [install-docs]
80+
cmds:
81+
- uv run mkdocs gh-deploy --config-file ./mkdocs.yml

examples/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)