Skip to content

chore: add CI check for openapi spec generation - #751

Open
Pedro S. Lopez (pedroslopez) wants to merge 3 commits into
mainfrom
pedro/check-openapi
Open

Pedro S. Lopez (pedroslopez) wants to merge 3 commits into
mainfrom
pedro/check-openapi

Conversation

@pedroslopez

@pedroslopez Pedro S. Lopez (pedroslopez) commented Sep 5, 2025 •

Copy link
Copy Markdown
Contributor

I forget to update the openapi spec since it's a manual thing.

This adds a CI check to make sure we don't forget. There's already a poe task to regenerate so you should be able to run the command if you forget - i prefer that over autocommitting fixes

Summary by CodeRabbit

  • New Features

    • Expanded API schema with an optional context field across multiple request types and limits, plus a new RequestContext object (workspace_id, project_id). StreamReadSlices now accepts an object slice_descriptor in addition to string/null.
  • Documentation

    • Added guidance on keeping the OpenAPI spec in sync and the CI check that validates it.
  • Chores

    • Introduced a CI job to verify OpenAPI spec consistency and fail when the committed spec is outdated.
    • Set workflow permissions to read repository contents.

@github-actions github-actions Bot added the chore label Sep 5, 2025
@github-actions

github-actions Bot commented Sep 5, 2025

Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

Testing This CDK Version

You can test this version of the CDK using the following:

# Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@pedro/check-openapi#egg=airbyte-python-cdk[dev]' --help

# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch pedro/check-openapi

Helpful Resources

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /autofix - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test - Runs connector tests with the updated CDK
  • /poe build - Regenerate git-committed build artifacts, such as the pydantic models which are generated from the manifest JSON schema in YAML.
  • /poe <command> - Runs any poe command in the CDK environment

📝 Edit this welcome message.

Comment thread .github/workflows/python_lint.yml Fixed
…in permissions

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@pedroslopez
Pedro S. Lopez (pedroslopez) marked this pull request as ready for review September 5, 2025 20:17
Copilot AI review requested due to automatic review settings September 5, 2025 20:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a CI check to automatically validate that the OpenAPI specification is up-to-date whenever API models are modified. The goal is to prevent developers from forgetting to regenerate the OpenAPI spec after making changes to request/response types.

  • Adds a new GitHub Actions job that generates the OpenAPI spec and fails if it differs from the committed version
  • Updates the README with documentation about the automated validation process
  • Includes updates to the OpenAPI spec itself (likely from running the generation command)

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/python_lint.yml Adds new openapi-check job to validate OpenAPI spec is current
airbyte_cdk/manifest_server/README.md Documents the automated OpenAPI validation process and manual regeneration command
airbyte_cdk/manifest_server/openapi.yaml Updates OpenAPI spec with new RequestContext schema and context fields

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread .github/workflows/python_lint.yml
@coderabbitai

coderabbitai Bot commented Sep 5, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds a CI job to verify OpenAPI spec generation in python_lint workflow. Updates README with instructions about automated OpenAPI validation. Extends OpenAPI spec by introducing RequestContext and adding optional context fields to multiple request schemas, and broadens slice_descriptor type to allow objects.

Changes

Cohort / File(s) Summary
CI Workflow Updates
.github/workflows/python_lint.yml
Adds top-level permissions.contents: read and a new openapi-check job to generate OpenAPI via Poetry, diff changes, and fail if spec is outdated.
Documentation
airbyte_cdk/manifest_server/README.md
Adds “Automated OpenAPI Spec Validation” section describing the CI check and local regeneration command; duplicated block appears twice.
OpenAPI Spec
airbyte_cdk/manifest_server/openapi.yaml
Introduces RequestContext schema; adds optional context to Check/Discover/Resolve/FullResolve/StreamTestRead/limits; allows StreamReadSlices.slice_descriptor to be object/string/null.

Sequence Diagram(s)

sequenceDiagram
  actor Dev as Developer
  participant GH as GitHub Actions
  participant Job as openapi-check
  Dev->>GH: Open PR / push
  GH->>Job: Run workflow
  Job->>Job: Checkout repo
  Job->>Job: Setup Poetry 2.0.1 & Python 3.10
  Job->>Job: Install deps (poetry install)
  Job->>Job: Generate OpenAPI (poetry run manifest-server generate-openapi)
  Job->>Job: git diff for changes
  alt Spec changed
    Job-->>GH: Mark job failed (spec outdated)
  else No changes
    Job-->>GH: Job succeeds
  end
Loading
sequenceDiagram
  participant Client
  participant API as Manifest Server API
  participant Handler as Request Handler
  Client->>API: Request (optional context)
  API->>Handler: Parse payload incl. context
  Handler-->>API: Proceed with operation using context (if provided)
  API-->>Client: Response
  note over API,Handler: Context fields are optional (workspace_id, project_id)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

ci

Suggested reviewers

  • aldogonzalez8
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pedro/check-openapi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
.github/workflows/python_lint.yml (1)

2-3: Good security posture: minimal GITHUB_TOKEN perms now declared.

This addresses the prior security bot suggestion to limit default permissions. 👍

airbyte_cdk/manifest_server/openapi.yaml (2)

337-341: Repeat of the context addition—same doc suggestion applies.

Mirroring the note above: add a brief description in the source model so clients see why/when to use context, wdyt?


491-495: Same context field here—carry over the docstring suggestion.

Adding a short description in the model will surface better help in generated clients, wdyt?

🧹 Nitpick comments (5)
airbyte_cdk/manifest_server/README.md (1)

128-139: Nice addition: call out the CI guardrail; consider linking the job name and mentioning the existing poe task.

Could we add the actual job name (“OpenAPI Spec Check”) and a brief note that there’s also a poe task to regenerate the spec, so contributors can choose either command, wdyt?

.github/workflows/python_lint.yml (1)

82-124: Make the diff detection robust to untracked files and scope it to openapi.yaml.

If the spec file ever becomes untracked, git diff --quiet won’t catch it. Would you switch to git status --porcelain scoped to the spec, and set the output accordingly, wdyt?

       - name: Check for changes
         id: git-diff
-        run: |
-          git diff --quiet && echo "No changes to commit" || echo "changes=true" >> $GITHUB_OUTPUT
-        shell: bash
+        shell: bash
+        run: |
+          set -eo pipefail
+          TARGET="airbyte_cdk/manifest_server/openapi.yaml"
+          git update-index -q --refresh
+          if git status --porcelain -- "$TARGET" | grep -q .; then
+            printf "changes=true\n" >> "$GITHUB_OUTPUT"
+          else
+            echo "No changes to OpenAPI spec"
+          fi

Optional follow-ups:

  • Would you prefer installing only what’s needed for generation to speed up CI (e.g., poetry install --extras manifest-server) instead of --all-extras, wdyt?
  • Poetry versions differ across jobs (this one uses 2.0.1 while others use 1.8.4). Standardizing across the workflow can reduce surprises; want to align them in a follow-up?
airbyte_cdk/manifest_server/openapi.yaml (3)

299-303: Additive context field looks good; consider documenting its intent.

To improve generated docs/SDKs, would you add a short description to the context property in the source model (so it flows into the spec), e.g., “Optional request context for tracing/observability,” wdyt?


472-486: Tighten typing for IDs and enrich field docs.

If workspace_id/project_id are UUIDs, would you add format: uuid (via Pydantic Field(..., json_schema_extra={"format": "uuid"})) and brief descriptions so SDKs validate/help users, wdyt? Example in the source model:

from pydantic import BaseModel, Field

class RequestContext(BaseModel):
    workspace_id: str | None = Field(
        default=None,
        description="Workspace identifier associated with the request.",
        json_schema_extra={"format": "uuid"},
    )
    project_id: str | None = Field(
        default=None,
        description="Project identifier associated with the request.",
        json_schema_extra={"format": "uuid"},
    )

575-579: Broadened slice_descriptor = object|string|null—can we define the object shape?

For stronger client generation, would you consider modeling the object form (e.g., with a dedicated schema or oneOf variants) instead of bare type: object, or at least documenting expected keys in the source model so it flows into the spec, wdyt?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f946d41 and f66e05b.

📒 Files selected for processing (3)
  • .github/workflows/python_lint.yml (2 hunks)
  • airbyte_cdk/manifest_server/README.md (1 hunks)
  • airbyte_cdk/manifest_server/openapi.yaml (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Check: source-hardcoded-records
  • GitHub Check: Check: source-pokeapi
  • GitHub Check: Check: source-shopify
  • GitHub Check: Check: destination-motherduck
  • GitHub Check: Check: source-intercom
  • GitHub Check: Pytest (All, Python 3.12, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.13, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Manifest Server Docker Image Build
  • GitHub Check: SDM Docker Image Build
🔇 Additional comments (2)
airbyte_cdk/manifest_server/openapi.yaml (2)

368-372: Context on full resolve—ensure server plumbs it through.

Since this feeds dynamic generation, can we confirm the backend handlers accept and propagate context where needed (logging/metrics/tracing), wdyt?


636-640: context on test_read—ensure it’s utilized or ignored predictably.

Can we confirm test-read handlers either use this for tracing or explicitly ignore it without error, and maybe document that behavior, wdyt?

@github-actions

github-actions Bot commented Sep 5, 2025

Copy link
Copy Markdown

PyTest Results (Fast)

3 742 tests  ±0   3 730 ✅ ±0   6m 16s ⏱️ -3s
    1 suites ±0      12 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 1385a15. ± Comparison against base commit ae0e8aa.

@github-actions

github-actions Bot commented Sep 5, 2025

Copy link
Copy Markdown

PyTest Results (Full)

3 745 tests  ±0   3 733 ✅ ±0   11m 10s ⏱️ +8s
    1 suites ±0      12 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 1385a15. ± Comparison against base commit ae0e8aa.

Dinesh (dinesh-ad) added a commit to Phazeo/truestill that referenced this pull request Sep 2, 2026
…nt carried, and the stages

Ruling 1: components from the TypedDicts, the route join from the
narrowed resolver, the spec committed and CI red on drift. Ruling 2:
openapi-typescript as a frontend dev dependency. The prior art cited
for ruling 1 could not be found and is replaced by
airbytehq/airbyte-python-cdk#751; the body records that a supplied
example was wrong, the same class as the entries wrong about their own
subject.

msgspec was tried on every TypedDict in a throwaway environment. As
shipped it refuses the project's boolean Literal tags on 35 of 133 and
any union of two TypedDicts, and on 3.14 under deferred annotations it
would read every NotRequired field as required because
__optional_keys__ is empty there. With a rebuild pass from
get_type_hints all 133 emit in 30 ms, every optional field correct
against the AST, and openapi-typescript consumes the result. Seven of
the components are dataclasses reached through two internal session
TypedDicts that never go on the wire, so the emitter must take the
resolver's closure, never the inventory - a rule stage C's script has
to carry in its own text.

No TypedDict count is carried any more: 117 was stale three ways and
the AST census misses the inheriting ones. The count is derived at
emission. Stages A-E are listed by what each proves; A closes 4b.

Claude-Session: https://claude.ai/code/session_019ptzxdRZkLsVAFJptStv9V
Dinesh (dinesh-ad) added a commit to Phazeo/truestill that referenced this pull request Sep 3, 2026
… on drift

packages/truestill-app/openapi.json is committed. scripts/emit_openapi.py
--write regenerates it; --check (the default) exits 1 on drift; and
test_the_committed_spec_is_current.py asserts the same in make check,
in Python, with no Node - the airbyte pattern
(airbytehq/airbyte-python-cdk#751). The emitter is deterministic: sorted
keys at dump time, nothing dated, no path outside the routes', two
renders compared.

The derivations moved out of the resolver test into
scripts/payload_contract.py, because a script importing a test is
backwards; both test files and the two living documents that cited the
old home now point at it. Each arm carries its status code and the
method that reaches it. Status: `status_code=` on the response call,
both arms of an `a if c else b`. Method: three lexical facts about the
six two-method handlers, which all branch on `request.method == "POST"`
- a return inside the block belongs to that method; one outside belongs
to the other methods, and to that method too when the block falls
through or the return sits in an except that wraps it. Not branch
narrowing of a value.

Q1268, derived and pinned: no union in the document carries a string
tag, so no OpenAPI discriminator is emitted anywhere. Thirteen
two-member unions carry a boolean tag (`ok`, `valid`, `armed`,
`created`), which TypeScript narrows on without a discriminator. Ten
unions have no shared property at all - every job envelope among them,
because JobStarted is {"job_id"} and nothing else. Those are bare
oneOfs, and openapi-typescript renders them as a union of the exact
shapes: narrowable with `in`, and strictly more than the cast at
main.tsx:37. Giving JobStarted a tag would change a payload and is a
separate decision.

Q1271: every arm of a status is one oneOf, never anyOf, and the test
loops the derived arms asserting each into the document under its
status and method - 111 arms, 38 union operations on 200. The
expired-session refusal is a 409 arm, the bake's missing word a 400,
the catalog-busy refusal a 503 on every operation - none of them a
collision on 200 any more.

Q1270: /api/jobs/{job_id}/events is text/event-stream with the four
frames as one oneOf under schema; openapi-typescript 7.13.0 read the
committed spec into 3,793 lines in 147 ms, with that route as a union
of the four frame types. Q1273: DoneFrame.summary is the oneOf of the
sixteen summary types, derived from the factories, and the document
has no empty schema left.

The two contract test files emit once per module: the first gate ran
the lane at 48 s against the 45 s ceiling because every test re-emitted
the document, and the ceiling is the durable number.

Proved by mutation, control green first: an arm dropped from a
multi-type route, the summary union not filled, oneOf turned to anyOf,
the event stream's content type lost, and status codes collapsed to 200
each turn the drift test red. Stage E does not start here.

Claude-Session: https://claude.ai/code/session_019ptzxdRZkLsVAFJptStv9V

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants