From 3232e2f0641be854a4b08bd799606884f04d639e Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 7 Jan 2026 00:43:12 +0000 Subject: [PATCH 1/4] Add schema-docs auto-generation with pre-commit and CI --- .github/workflows/schema-docs.yaml | 74 ++++++++++++++++++++++++++ .pre-commit-config.yaml | 6 +++ docs/supported_formats.md | 26 +++++++++ scripts/build_schema_docs.py | 85 +++++++++++++++++++++++------- tox.ini | 7 +++ 5 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/schema-docs.yaml diff --git a/.github/workflows/schema-docs.yaml b/.github/workflows/schema-docs.yaml new file mode 100644 index 000000000..7a4e76fb7 --- /dev/null +++ b/.github/workflows/schema-docs.yaml @@ -0,0 +1,74 @@ +name: Update Schema Docs + +on: + push: + branches: [main] + paths: + - 'src/datamodel_code_generator/parser/schema_version.py' + - 'scripts/build_schema_docs.py' + - 'docs/supported_formats.md' + pull_request: + branches: [main] + paths: + - 'src/datamodel_code_generator/parser/schema_version.py' + - 'scripts/build_schema_docs.py' + - 'docs/supported_formats.md' + pull_request_target: + types: [labeled] + paths: + - 'src/datamodel_code_generator/parser/schema_version.py' + - 'scripts/build_schema_docs.py' + - 'docs/supported_formats.md' + +permissions: + contents: write + +jobs: + update-schema-docs: + if: | + github.event_name == 'push' || + !github.event.pull_request.head.repo.fork || + github.actor == 'koxudaxi' || + github.actor == 'gaborbernat' || + github.actor == 'ilovelinux' || + (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-fix' && + (github.event.sender.login == 'koxudaxi' || + github.event.sender.login == 'gaborbernat' || + github.event.sender.login == 'ilovelinux')) + runs-on: ubuntu-latest + steps: + # Checkout for forks (no PAT available) + - uses: actions/checkout@v4 + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + # Checkout for same-repo PRs, pushes, and pull_request_target + - uses: actions/checkout@v4 + if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref || github.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + token: ${{ secrets.PAT }} + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v5 + - name: Install tox + run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv + - name: Setup environment + run: tox run -vv --notest --skip-missing-interpreters false -e schema-docs + env: + UV_PYTHON_PREFERENCE: "only-managed" + - name: Build schema docs + run: .tox/schema-docs/bin/python scripts/build_schema_docs.py + - name: Commit and push if changed + if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add docs/supported_formats.md + git diff --staged --quiet || git commit -m "docs: update schema feature documentation + + 🤖 Generated by GitHub Actions" + git push diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f09051eb..5da9d8bdd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,3 +43,9 @@ repos: language: system files: ^src/datamodel_code_generator/config\.py$ pass_filenames: false + - id: schema-docs + name: Update schema feature documentation + entry: bash -c '[ -x .tox/schema-docs/bin/python ] && PYTHON=.tox/schema-docs/bin/python || PYTHON=.tox/schema-docs/Scripts/python; test -x "$PYTHON" || tox run -e schema-docs --notest -qq; "$PYTHON" scripts/build_schema_docs.py' + language: system + files: ^(src/datamodel_code_generator/parser/schema_version\.py|scripts/build_schema_docs\.py|docs/supported_formats\.md)$ + pass_filenames: false diff --git a/docs/supported_formats.md b/docs/supported_formats.md index c6941cae4..ed8e6b039 100644 --- a/docs/supported_formats.md +++ b/docs/supported_formats.md @@ -90,6 +90,32 @@ datamodel-code-generator detects the OpenAPI version from the `openapi` field: - `openapi: "3.1.x"` -> OpenAPI 3.1 - No `openapi` field -> Fallback to OpenAPI 3.1 + +### Supported Features (from code) + +The following features are tracked in the codebase with their implementation status: + +#### JSON Schema Features + +| Feature | Introduced | Status | Description | +|---------|------------|--------|-------------| +| `Null in type array` | 2020-12 | ✅ Supported | Allows `type: ['string', 'null']` syntax for nullable types | +| `$defs` | 2019-09 | ✅ Supported | Uses `$defs` instead of `definitions` for schema definitions | +| `prefixItems` | 2020-12 | ✅ Supported | Tuple validation using `prefixItems` keyword | +| `Boolean schemas` | Draft 6 | ✅ Supported | Allows `true` and `false` as valid schemas | +| `$id` | Draft 6 | ✅ Supported | Schema identifier field (`id` in Draft 4, `$id` in Draft 6+) | +| `definitions/$defs` | Draft 4 | ✅ Supported | Key for reusable schema definitions | +| `exclusiveMinimum/Maximum as number` | Draft 6 | ✅ Supported | Numeric `exclusiveMinimum`/`exclusiveMaximum` (boolean in Draft 4) | +| `readOnly/writeOnly` | Draft 7 | ✅ Supported | Field visibility hints for read-only and write-only properties | + +#### OpenAPI-Specific Features + +| Feature | Introduced | Status | Description | +|---------|------------|--------|-------------| +| `nullable` | OAS 3.0 | ✅ Supported | Uses `nullable: true` for nullable types (deprecated in 3.1) | +| `discriminator` | OAS 3.0 | ✅ Supported | Polymorphism support via `discriminator` keyword | + + ## Data Format Support ### Common Formats (JSON Schema + OpenAPI) diff --git a/scripts/build_schema_docs.py b/scripts/build_schema_docs.py index 67b79ca53..9433a8ce2 100644 --- a/scripts/build_schema_docs.py +++ b/scripts/build_schema_docs.py @@ -11,6 +11,7 @@ from __future__ import annotations import argparse +import re import sys from dataclasses import fields from pathlib import Path @@ -26,6 +27,10 @@ DOCS_PATH = Path(__file__).parent.parent / "docs" / "supported_formats.md" +# Markers for auto-generated content +BEGIN_MARKER = "" +END_MARKER = "" + # Status emoji mapping STATUS_EMOJI = { "supported": "✅", @@ -114,10 +119,9 @@ def print_features_summary() -> None: print() -def generate_supported_features_table() -> str: - """Generate the supported features table for documentation.""" +def generate_supported_features_content() -> str: + """Generate the supported features content for documentation (without markers).""" lines = [ - "", "", "### Supported Features (from code)", "", @@ -136,11 +140,68 @@ def generate_supported_features_table() -> str: openapi_features = [ (name, meta) for name, meta in get_feature_metadata(OpenAPISchemaFeatures) if name not in json_field_names ] - lines.extend((generate_feature_table(openapi_features), "", "")) + lines.extend((generate_feature_table(openapi_features), "")) return "\n".join(lines) +def generate_supported_features_table() -> str: + """Generate the supported features table with markers for documentation.""" + return f"{BEGIN_MARKER}{generate_supported_features_content()}{END_MARKER}" + + +def update_docs_file(*, check: bool = False) -> int: + """Update docs/supported_formats.md with auto-generated content. + + Args: + check: If True, only check if content would change (returns 1 if different). + + Returns: + 0 on success, 1 on failure or if check mode detects changes. + """ + if not DOCS_PATH.exists(): + print(f"Error: {DOCS_PATH} does not exist", file=sys.stderr) + return 1 + + current_content = DOCS_PATH.read_text(encoding="utf-8") + new_generated = generate_supported_features_content() + + # Pattern to match content between markers (including markers) + pattern = re.compile( + rf"{re.escape(BEGIN_MARKER)}.*?{re.escape(END_MARKER)}", + re.DOTALL, + ) + + if pattern.search(current_content): + # Replace existing auto-generated section + new_content = pattern.sub( + f"{BEGIN_MARKER}{new_generated}{END_MARKER}", + current_content, + ) + else: + print(f"Warning: Markers not found in {DOCS_PATH}", file=sys.stderr) + print("Please add the following markers to the file:", file=sys.stderr) + print(f" {BEGIN_MARKER}", file=sys.stderr) + print(f" {END_MARKER}", file=sys.stderr) + return 1 + + if check: + if current_content != new_content: + print(f"Schema docs are out of date: {DOCS_PATH}", file=sys.stderr) + print("Run 'python scripts/build_schema_docs.py' to update.", file=sys.stderr) + return 1 + print("Schema docs are up to date.") + return 0 + + if current_content == new_content: + print("Schema docs are already up to date.") + return 0 + + DOCS_PATH.write_text(new_content, encoding="utf-8") + print(f"Updated {DOCS_PATH}") + return 0 + + def main() -> int: """Parse arguments and build documentation.""" parser = argparse.ArgumentParser(description="Build schema documentation from code metadata") @@ -160,21 +221,7 @@ def main() -> int: print_features_summary() return 0 - print("Schema Documentation Builder") - print("-" * 40) - - # For now, just print the generated table - print("\nGenerated Supported Features Table:") - print() - print(generate_supported_features_table()) - - if args.check: - print("\n[Check mode] No files modified.") - else: - print("\n[Info] This script currently outputs to stdout.") - print(" Future versions will update docs/supported_formats.md directly.") - - return 0 + return update_docs_file(check=args.check) if __name__ == "__main__": diff --git a/tox.ini b/tox.ini index f66722076..c3e902f65 100644 --- a/tox.ini +++ b/tox.ini @@ -111,6 +111,13 @@ commands = check-wheel-contents --no-config {env_tmp_dir} dependency_groups = pkg-meta +[testenv:schema-docs] +description = Build schema feature documentation (use --check to validate only) +commands = + python scripts/build_schema_docs.py {posargs} +dependency_groups = +no_default_groups = true + [testenv:llms-txt] description = Generate llms.txt files (use --check to validate only, requires Python 3.11+) commands = From ce2b19df375251778dcf84bc7c26bcb5792d9366 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 Jan 2026 00:43:42 +0000 Subject: [PATCH 2/4] docs: update llms.txt files Generated by GitHub Actions --- docs/llms-full.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 8b54d2953..18eb32f88 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -24144,6 +24144,32 @@ datamodel-code-generator detects the OpenAPI version from the `openapi` field: - `openapi: "3.1.x"` -> OpenAPI 3.1 - No `openapi` field -> Fallback to OpenAPI 3.1 + +### Supported Features (from code) + +The following features are tracked in the codebase with their implementation status: + +#### JSON Schema Features + +| Feature | Introduced | Status | Description | +|---------|------------|--------|-------------| +| `Null in type array` | 2020-12 | ✅ Supported | Allows `type: ['string', 'null']` syntax for nullable types | +| `$defs` | 2019-09 | ✅ Supported | Uses `$defs` instead of `definitions` for schema definitions | +| `prefixItems` | 2020-12 | ✅ Supported | Tuple validation using `prefixItems` keyword | +| `Boolean schemas` | Draft 6 | ✅ Supported | Allows `true` and `false` as valid schemas | +| `$id` | Draft 6 | ✅ Supported | Schema identifier field (`id` in Draft 4, `$id` in Draft 6+) | +| `definitions/$defs` | Draft 4 | ✅ Supported | Key for reusable schema definitions | +| `exclusiveMinimum/Maximum as number` | Draft 6 | ✅ Supported | Numeric `exclusiveMinimum`/`exclusiveMaximum` (boolean in Draft 4) | +| `readOnly/writeOnly` | Draft 7 | ✅ Supported | Field visibility hints for read-only and write-only properties | + +#### OpenAPI-Specific Features + +| Feature | Introduced | Status | Description | +|---------|------------|--------|-------------| +| `nullable` | OAS 3.0 | ✅ Supported | Uses `nullable: true` for nullable types (deprecated in 3.1) | +| `discriminator` | OAS 3.0 | ✅ Supported | Polymorphism support via `discriminator` keyword | + + ## Data Format Support ### Common Formats (JSON Schema + OpenAPI) From 50f7ae0b3a551f6dd4e918f9e2e7f373bd860787 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 7 Jan 2026 02:01:49 +0000 Subject: [PATCH 3/4] Add schema-docs to lint SKIP list --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0d9508d89..4c2bfaab0 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -34,7 +34,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.14" - - run: SKIP=readme,config-types uvx prek run --all-files --show-diff-on-failure + - run: SKIP=readme,config-types,schema-docs uvx prek run --all-files --show-diff-on-failure - if: | github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository || From dbceca938cff20263535072d4d56964c50c86d16 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 7 Jan 2026 02:08:12 +0000 Subject: [PATCH 4/4] Improve conditional git handling in schema-docs workflow --- .github/workflows/schema-docs.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/schema-docs.yaml b/.github/workflows/schema-docs.yaml index 7a4e76fb7..e29aadde8 100644 --- a/.github/workflows/schema-docs.yaml +++ b/.github/workflows/schema-docs.yaml @@ -68,7 +68,9 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add docs/supported_formats.md - git diff --staged --quiet || git commit -m "docs: update schema feature documentation + if ! git diff --staged --quiet; then + git commit -m "docs: update schema feature documentation 🤖 Generated by GitHub Actions" - git push + git push + fi