diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24b88229..a793aa49 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,7 @@ third-party Actions alike. **Rationale.** Mutable tags (including immutable-looking version tags like `@v6.0.1`) can be retroactively moved by an attacker who gains write access to the upstream repository. The -[`tj-actions/changed-files` compromise (CVE-2025-30066, March 2025)](https://www.cisa.gov/news-events/alerts/2025/03/18/supply-chain-compromise-third-party-tj-actionschanged-files-cve-2025-30066) +[`tj-actions/changed-files` compromise (CVE-2025-30066, March 2025)](https://github.com/advisories/GHSA-mrrh-fwg8-r2c3) demonstrated this exact failure mode: ~23,000 repositories had their CI secrets exfiltrated, and only workflows that pinned to a commit SHA were safe. GitHub's own guidance now identifies SHA pinning as diff --git a/DESIGN.md b/DESIGN.md index bc85c26d..50bd060d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -79,8 +79,8 @@ Note: `render_swagger_ui()` does not embed or generate the OpenAPI spec. It retu ## See Also -- [azure-functions-validation — Architecture](https://github.com/yeongseon/azure-functions-validation) — Request/response validation pipeline -- [azure-functions-logging — Architecture](https://github.com/yeongseon/azure-functions-logging) — Structured logging with contextvars -- [azure-functions-doctor — Architecture](https://github.com/yeongseon/azure-functions-doctor) — Pre-deploy diagnostic CLI -- [azure-functions-scaffold — Architecture](https://github.com/yeongseon/azure-functions-scaffold) — Project scaffolding CLI -- [azure-functions-langgraph — Architecture](https://github.com/yeongseon/azure-functions-langgraph) — LangGraph agent deployment +- [azure-functions-validation — Architecture](https://github.com/yeongseon/azure-functions-validation-python) — Request/response validation pipeline +- [azure-functions-logging — Architecture](https://github.com/yeongseon/azure-functions-logging-python) — Structured logging with contextvars +- [azure-functions-doctor — Architecture](https://github.com/yeongseon/azure-functions-doctor-python) — Pre-deploy diagnostic CLI +- [azure-functions-scaffold — Architecture](https://github.com/yeongseon/azure-functions-scaffold-python) — Project scaffolding CLI +- [azure-functions-langgraph — Architecture](https://github.com/yeongseon/azure-functions-langgraph-python) — LangGraph agent deployment diff --git a/SUPPORT.md b/SUPPORT.md index 86c8bd14..801a6d93 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -2,7 +2,7 @@ ## Supported Environment -* Python: 3.11 and above +* Python: 3.11-3.14 (`>=3.11,<3.15`) * Runtime: Azure Functions (Python) ## Support Scope diff --git a/docs/architecture.md b/docs/architecture.md index c835d59d..48fcea2a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -219,7 +219,7 @@ The `_openapi_registry` is protected by `threading.RLock`, ensuring safe concurr The function accepts a similar core metadata shape to `@openapi(...)` (path, method, operation_id, summary, request/response schemas, etc.) and writes directly to the shared `_openapi_registry`. Once registered, routes appear in the generated spec alongside decorator-registered routes. -**Reference consumer:** [`azure-functions-langgraph`](https://github.com/yeongseon/azure-functions-langgraph) uses its bridge module (`azure_functions_langgraph.openapi.register_with_openapi`) to read route metadata from its `get_app_metadata()` API and forward it to `register_openapi_metadata()`. This pattern demonstrates how any Azure Functions package can contribute routes to the OpenAPI spec without depending on the `@openapi` decorator. +**Reference consumer:** [`azure-functions-langgraph`](https://github.com/yeongseon/azure-functions-langgraph-python) uses its bridge module (`azure_functions_langgraph.openapi.register_with_openapi`) to read route metadata from its `get_app_metadata()` API and forward it to `register_openapi_metadata()`. This pattern demonstrates how any Azure Functions package can contribute routes to the OpenAPI spec without depending on the `@openapi` decorator. The architecture intentionally keeps bridge implementation in the *consumer* package (langgraph), not here. This package defines the contract; consumers decide when and how to call it. @@ -260,8 +260,8 @@ The architecture intentionally keeps bridge implementation in the *consumer* pac ## See Also -- [azure-functions-langgraph — Architecture](https://github.com/yeongseon/azure-functions-langgraph) — LangGraph deployment adapter (reference consumer of `register_openapi_metadata()`) -- [azure-functions-validation — Architecture](https://github.com/yeongseon/azure-functions-validation) — Request/response validation pipeline -- [azure-functions-logging — Architecture](https://github.com/yeongseon/azure-functions-logging) — Structured logging with contextvars -- [azure-functions-doctor — Architecture](https://github.com/yeongseon/azure-functions-doctor) — Pre-deploy diagnostic CLI -- [azure-functions-scaffold — Architecture](https://github.com/yeongseon/azure-functions-scaffold) — Project scaffolding CLI +- [azure-functions-langgraph — Architecture](https://github.com/yeongseon/azure-functions-langgraph-python) — LangGraph deployment adapter (reference consumer of `register_openapi_metadata()`) +- [azure-functions-validation — Architecture](https://github.com/yeongseon/azure-functions-validation-python) — Request/response validation pipeline +- [azure-functions-logging — Architecture](https://github.com/yeongseon/azure-functions-logging-python) — Structured logging with contextvars +- [azure-functions-doctor — Architecture](https://github.com/yeongseon/azure-functions-doctor-python) — Pre-deploy diagnostic CLI +- [azure-functions-scaffold — Architecture](https://github.com/yeongseon/azure-functions-scaffold-python) — Project scaffolding CLI diff --git a/docs/assets/screenshots.yml b/docs/assets/screenshots.yml index e867f8fe..147d4d41 100644 --- a/docs/assets/screenshots.yml +++ b/docs/assets/screenshots.yml @@ -1,3 +1,11 @@ +# Screenshot provenance manifest. +# +# STATUS: every capture below is HISTORICAL. All images were taken at +# package version 0.21.2 (2026-08-15) and have not been re-captured since. +# They still illustrate the documented behaviour, but chrome, labels, and +# minor spec details may differ from the current release. Re-capture and run +# `python scripts/check_screenshots.py --update` before relying on them as a +# pixel-accurate reference for the latest version. schema_version: 1 screenshots: - id: deployment_report_jobs_openapi_json @@ -7,6 +15,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/report_jobs/function_app.py @@ -20,6 +29,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/report_jobs/function_app.py @@ -33,6 +43,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/notification_request/function_app.py @@ -46,6 +57,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/partner_import_bridge/function_app.py @@ -59,6 +71,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/webhook_receiver/function_app.py @@ -72,6 +85,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/webhook_receiver/function_app.py @@ -85,6 +99,7 @@ screenshots: git_sha: 8310039aa73e91d45cc8389b356428071225eb5b date: '2026-08-15' method: manual + status: historical source: inputs: - examples/webhook_receiver/function_app.py diff --git a/docs/contributing.md b/docs/contributing.md index f6618045..4306e181 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -72,7 +72,7 @@ Titles for issues, pull requests, and commits follow the **Title Convention** in ### Before submitting - Run `make check-all` and verify it passes. -- Ensure new code has test coverage (target: 85%+). +- Ensure new code has test coverage. The suite is gated at 95% (`fail_under = 95` in `pyproject.toml`). - Update documentation if the change affects public API or behavior. - Keep the PR focused on a single concern. @@ -82,7 +82,7 @@ Describe what changed and why. Reference related issues with `Fixes #N` or `Clos ### Review process -- At least one approval is required before merging. +- Branch protection does not require a review approval; the required CI checks are the merge gate. Maintainers still review anything beyond a trivial change. - CI must pass on all Python versions (3.11 -- 3.14). - Merge with "Squash and merge" to keep the commit history clean. @@ -90,9 +90,8 @@ Describe what changed and why. Reference related issues with `Fixes #N` or `Clos ### Formatting -- **Black** for code formatting (line length: default). -- **Ruff** for linting and import sorting. -- Both run as pre-commit hooks. +- **Ruff** does everything: formatting, linting, and import sorting. There is no Black in this project. +- `ruff format` and `ruff check` both run as pre-commit hooks. ### Type annotations @@ -115,22 +114,31 @@ Describe what changed and why. Reference related issues with `Fixes #N` or `Clos | Target | Description | | --- | --- | -| `make format` | Format code with Ruff and Black | -| `make lint` | Run Ruff linter | +| `make format` | Format code with `ruff format` | +| `make format-check` | Check formatting without writing | +| `make style` | `ruff check` + `ruff format --check` | +| `make lint` | Run Ruff + mypy | | `make typecheck` | Run mypy type checking | | `make security` | Run Bandit security scan | | `make test` | Run pytest | -| `make cov` | Run tests with coverage | -| `make check-all` | Run lint + typecheck + test (full gate) | +| `make cov` | Run tests with coverage (gated at 95%) | +| `make lint-workflows` | Lint release workflows, action pins, Hatch matrix | +| `make check` | Run lint + typecheck | +| `make check-all` | Run lint-workflows + check + test + security (full gate) | +| `make docs` | Build the MkDocs site | + +See [Development Guide](development.md#makefile-targets) for the complete list. ### Pre-commit hooks -| Tool | Version | Purpose | -| --- | --- | --- | -| Black | 26.1.0 | Code formatter | -| Ruff | v0.14.13 | Linter and import sorter | -| mypy | v1.19.1 | Static type checker | -| Bandit | 1.9.3 | Security scanner (src/ only) | +| Tool | Purpose | +| --- | --- | +| Ruff | Formatter, linter, and import sorter | +| mypy | Static type checker | +| Bandit | Security scanner (`src/` only) | + +Pinned versions live in `.pre-commit-config.yaml` (hook `rev`) and +`pyproject.toml` (the `dev` dependency pins), so they are not duplicated here. Run all hooks manually: diff --git a/docs/deployment.md b/docs/deployment.md index 0ecd500f..5ff55b70 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -389,7 +389,7 @@ From either example directory: ```bash azure-functions-openapi generate \ - --app-path ./function_app.py \ + --app function_app:app \ --format json \ --output ./openapi.json ``` @@ -398,7 +398,7 @@ Generate YAML instead: ```bash azure-functions-openapi generate \ - --app-path ./function_app.py \ + --app function_app:app \ --format yaml \ --output ./openapi.yaml ``` @@ -535,8 +535,8 @@ az group list --query "[?starts_with(name, 'rg-openapi')]" -o table - [Choose an Azure Functions Hosting Plan](choose-a-plan.md) — Plan selection guide with decision tree - [CLI guide](./cli.md) -- [`azure-functions-scaffold`](https://github.com/yeongseon/azure-functions-scaffold) -- [`azure-functions-validation`](https://github.com/yeongseon/azure-functions-validation) -- [`azure-functions-doctor`](https://github.com/yeongseon/azure-functions-doctor) -- [`azure-functions-logging`](https://github.com/yeongseon/azure-functions-logging) -- [`azure-functions-langgraph`](https://github.com/yeongseon/azure-functions-langgraph) +- [`azure-functions-scaffold`](https://github.com/yeongseon/azure-functions-scaffold-python) +- [`azure-functions-validation`](https://github.com/yeongseon/azure-functions-validation-python) +- [`azure-functions-doctor`](https://github.com/yeongseon/azure-functions-doctor-python) +- [`azure-functions-logging`](https://github.com/yeongseon/azure-functions-logging-python) +- [`azure-functions-langgraph`](https://github.com/yeongseon/azure-functions-langgraph-python) diff --git a/docs/development.md b/docs/development.md index 2869b289..074d2e02 100644 --- a/docs/development.md +++ b/docs/development.md @@ -71,11 +71,19 @@ azure-functions-openapi/ This project uses pre-commit to ensure consistent code quality across formatting, linting, typing, and security. -| Tool | Version | Purpose | -|--------|----------|--------------------------------| -| ruff | v0.15.5 | Formatter + linter + import sorter | -| mypy | v1.19.1 | Static type checker | -| bandit | 1.9.4 | Security checker on `src/` only | +There is no Black in this project. Ruff handles both formatting and linting. + +| Tool | Purpose | +|--------|------------------------------------| +| ruff | Formatter + linter + import sorter | +| mypy | Static type checker | +| bandit | Security checker on `src/` only | + +Pinned versions live in two places and are the source of truth, so this table +deliberately does not repeat them: + +- `.pre-commit-config.yaml` — the `rev` of each hook +- `pyproject.toml` — the `dev` optional-dependency pins used by `make` targets ### Bandit Configuration @@ -121,22 +129,37 @@ Use these as the **golden commands** for local validation and CI parity. Prefer | Target | Description | |--------|-------------| | `make install` | Create Hatch env and install pre-commit hooks | -| `make format` | Format code (ruff) | +| `make shell` | Open a shell inside the Hatch env | +| `make format` | Format code (ruff format) | +| `make format-check` | Check formatting without writing (ruff format --check) | +| `make style` | Run `ruff check` + `ruff format --check` | | `make lint` | Run linter (ruff + mypy) | | `make typecheck` | Run mypy type checking | | `make security` | Run Bandit security scan | | `make test` | Run pytest | -| `make cov` | Run tests with coverage | +| `make cov` | Run tests with coverage (gate: `fail_under = 95`) | +| `make e2e-local` | Run e2e tests against local Azurite on `:7071` | +| `make e2e-azure` | Run e2e tests against Azure (`E2E_BASE_URL` required) | +| `make lint-workflows` | Lint release workflows, action pins, and the Hatch matrix | | `make check` | Run lint + typecheck | -| `make check-all` | Run lint + typecheck + test | +| `make check-all` | Run lint-workflows + check + test + security | +| `make docs` | Build the MkDocs site | +| `make docs-serve` | Serve the docs locally with live reload | +| `make demo` | Run the Swagger UI and example demos | | `make build` | Build package | | `make version` | Show the current version | | `make publish-test` | Publish to TestPyPI | | `make precommit` | Run all pre-commit hooks | | `make precommit-install` | Install pre-commit hooks | | `make doctor` | Show environment diagnostic info | +| `make reset` | Deep clean, then reinstall the env | +| `make hatch-clean` | Remove the Hatch environment | | `make clean` | Remove build artifacts | | `make clean-all` | Deep clean (caches, coverage, venv) | +| `make help` | List available targets | + +Coverage is enforced at **95%** (`fail_under = 95` in `pyproject.toml`); a drop +below that fails the build. > For the full release workflow, see [Release Process](release_process.md). diff --git a/docs/installation.md b/docs/installation.md index 2ab670b8..ea816014 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -123,6 +123,13 @@ RUN pip install -r /home/site/wwwroot/requirements.txt COPY . /home/site/wwwroot ``` +`4-python3.12` is just an example: `4` is the Functions runtime major version +and `python3.12` is the interpreter baked into the image. Swap it for whichever +supported interpreter your app targets. This package supports 3.11 through +3.14, and `mcr.microsoft.com/azure-functions/python` publishes `4-python3.11`, +`4-python3.12`, `4-python3.13`, and `4-python3.14`. Pick the tag that matches +the version you test against, and keep it in sync with your CI matrix. + ## Upgrading Upgrade to the latest release: diff --git a/docs/testing.md b/docs/testing.md index 3597cc1c..f05700c6 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,7 +1,8 @@ # Testing Guide -This project targets **85%+ total coverage** for core modules. -Latest measured result (CI): **87%** (2026-02-09). +Total coverage is **enforced at 95%**: `fail_under = 95` in `pyproject.toml` +fails the build when the suite drops below it. Run `make cov` for the current +measured number and an HTML report. ## Test Structure @@ -57,15 +58,12 @@ hatch run pytest -k "security" -v - Coverage report is generated as `coverage.xml`. - Codecov upload runs in CI for the Python 3.11 job. -### Coverage thresholds +### Coverage threshold -| Module | Target | -| --- | --- | -| `decorator.py` | 90%+ | -| `openapi.py` | 85%+ | -| `swagger_ui.py` | 85%+ | -| `cli.py` | 80%+ | -| `utils.py` | 90%+ | +There is one threshold and it applies to the whole package: **95%**, configured +as `fail_under` under `[tool.coverage.report]` in `pyproject.toml`. Per-module +targets are not tracked separately; use `make cov` and the term-missing report +to find uncovered lines. ## Test Categories diff --git a/docs/usage.md b/docs/usage.md index 88a453f5..d2a2370e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -644,9 +644,9 @@ spec = generate_openapi_spec(openapi_version=OPENAPI_VERSION_3_2) Under 3.0/3.1 there is no `query` path-item field, so the operation is dropped with a warning (and raises under `strict=True`). - ### Non-standard HTTP methods (OpenAPI 3.2) +### Non-standard HTTP methods (OpenAPI 3.2) - OpenAPI 3.0/3.1 path items only accept the fixed verb set (`get`, `put`, +OpenAPI 3.0/3.1 path items only accept the fixed verb set (`get`, `put`, `post`, `delete`, `options`, `head`, `patch`, `trace`). OpenAPI 3.2 adds an `additionalOperations` map for methods outside that set (for example `PURGE` or `LINK`). When you document a handler bound to a non-standard method and diff --git a/llms-full.txt b/llms-full.txt index 025d60d8..ddd18ada 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -5,7 +5,7 @@ ## Package Info - PyPI: `pip install azure-functions-openapi` -- Version: 0.27.0 +- Version: see PyPI (https://pypi.org/project/azure-functions-openapi/) for the current release - Python: >=3.11, <3.15 - License: MIT - Docs: https://yeongseon.dev/azure-functions-python/openapi/ diff --git a/llms.txt b/llms.txt index 434b9b49..450e981f 100644 --- a/llms.txt +++ b/llms.txt @@ -5,7 +5,7 @@ ## Package Info - PyPI: `pip install azure-functions-openapi` -- Version: 0.27.0 +- Version: see PyPI (https://pypi.org/project/azure-functions-openapi/) for the current release - Python: >=3.11, <3.15 - License: MIT - Docs: https://yeongseon.dev/azure-functions-python/openapi/