Skip to content

feat(cli): add airbyte-cloud and airbyte-local CLIs#1010

Closed
Aaron ("AJ") Steers (aaronsteers) wants to merge 41 commits into
mainfrom
devin/1775171846-airbyte-cli
Closed

feat(cli): add airbyte-cloud and airbyte-local CLIs#1010
Aaron ("AJ") Steers (aaronsteers) wants to merge 41 commits into
mainfrom
devin/1775171846-airbyte-cli

Conversation

@aaronsteers
Copy link
Copy Markdown
Member

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Apr 2, 2026

Summary

Adds Cyclopts-backed PyAirbyte CLI entrypoints split by operating mode and routes Cloud auth/workspace construction through shared public Cloud APIs:

  • airbyte-cloud ... for Airbyte Cloud and self-managed control-plane API operations.
  • airbyte-local ... for local PyAirbyte connector workflows migrated from the old local CLI commands.
  • Removes the root airbyte, pyab, and pyairbyte console-script entrypoints.
  • Adds airbyte.cloud.CloudClient as the higher-level authenticated Cloud/self-managed API facade, with optional organization_id support.
  • Co-locates CloudClient with CloudWorkspace in airbyte.cloud.workspaces and removes airbyte/cloud/client.py, avoiding circular dependencies and inline imports.
  • Removes CloudClient.get_workspace_from_auth(); CLI workspace-scoped commands use the public CloudWorkspace(...) constructor directly, and CloudWorkspace resolves credentials through CloudClient internally.
  • Moves shared credential resolution into airbyte.cloud.credentials, including explicit inputs, env vars, and ~/.airbyte/credentials.
  • Deletes the CLI-specific _cli_auth.py helper layer; Cloud CLI and MCP now call shared Cloud code instead of building credentials/workspaces themselves.
  • Adds CloudClient.login(interactive: bool | None = None) and CloudClient.logout(); non-interactive client-credentials login stores a bearer token locally, while interactive browser login is intentionally left as a NotImplementedError with a TK-TODO before merge.
  • Uses explicit public_api_root and config_api_root naming for new shared APIs. Self-managed login requires --public-api-root, --config-api-root, --client-id, and --client-secret.
  • Keeps Cloud CLI command handlers as thin presentation wrappers over public Cloud classes: CloudClient, CloudWorkspace, CloudConnection, CloudSource, CloudDestination, and SyncResult.
  • Uses explicit Cloud create/update options instead of a prominent generic --json payload.
  • Supports positional-or-named entity IDs for matching get/delete/sync/rename/update commands, with conflict validation if both are supplied and differ.
  • Keeps rename name-only and reserves update for config/settings changes.
  • Adds connections enable, connections disable, connections schedule set, and connections schedule manual instead of exposing raw status changes.
  • Adds connections sync --wait/--no-wait --wait-timeout and airbyte-cloud jobs wait.
  • Switches CLI docs generation to cyclopts.docs.generate_markdown_docs, matching the Airbyte-Ops-MCP CLI documentation pattern.
  • Renames the docs generator module to docs/generate_cli_docs.py for clarity and updates imports, tests, docstrings, and contributor docs.
  • Adds unit coverage for Cloud credential login behavior, including a Windows-safe test path for POSIX-only file-mode assertions.

Current public command shape:

airbyte-cloud
├── login, logout
├── workspaces (list, get)
├── sources (list, get, create, rename, update, delete)
├── destinations (list, get, create, rename, update, delete)
├── connections
│   ├── list, get, create, rename, update, delete, sync
│   ├── enable, disable
│   └── schedule (set, manual)
└── jobs (list, get, wait)

airbyte-local
├── connectors
│   ├── benchmark
│   └── validate
├── debug
│   └── destinations
│       └── smoke-test
└── sync

Review & Testing Checklist for Human

  • Verify the command hierarchy and help text match the intended public CLI surface:

    uv run airbyte-cloud --help
    uv run airbyte-cloud login --help
    uv run airbyte-cloud workspaces list --help
    uv run airbyte-cloud sources create --help
    uv run airbyte-cloud destinations create --help
    uv run airbyte-cloud connections create --help
    uv run airbyte-cloud connections update --help
    uv run airbyte-cloud connections enable --help
    uv run airbyte-cloud connections disable --help
    uv run airbyte-cloud connections schedule set --help
    uv run airbyte-cloud jobs wait --help
    uv run airbyte-local --help
  • Verify Cloud auth behavior against real credentials, including:

    uv run airbyte-cloud login --client-id ... --client-secret ...
    uv run airbyte-cloud workspaces list
    uv run airbyte-cloud logout
  • Verify a self-managed non-interactive login requires both base API roots:

    uv run airbyte-cloud login \
      --public-api-root ... \
      --config-api-root ... \
      --client-id ... \
      --client-secret ...
  • Verify existing public Python construction still works with direct CloudWorkspace(...) usage:

    from airbyte.cloud import CloudClient, CloudWorkspace
    
    workspace = CloudWorkspace(workspace_id="...", bearer_token="...")
    client = CloudClient.from_auth()
  • Verify MCP Cloud tools still authenticate from MCP config and can list/describe workspaces and organizations.

  • Confirm the interactive browser-login TK-TODO is resolved before merging, or intentionally keep this PR draft until the browser-login implementation lands.

Notes

Local validation passed after the latest same-module CloudClient/CloudWorkspace cleanup:

uv run poe docs-generate
uv run ruff check .
uv run pyrefly check
uv run pytest tests/unit_tests/test_cloud_credentials.py tests/docs_tests/test_cli_docs.py
uv run airbyte-cloud login --help
uv run airbyte-cloud logout --help
python -m compileall -q airbyte/cloud airbyte/cli airbyte/mcp

pyrefly reports 0 errors with existing suppressed warnings. ruff reports only the existing repo-level deprecation warning for top-level linter settings.

Link to Devin session: https://app.devin.ai/sessions/150e0657e5344e518579eab6db4f0221
Requested by: Aaron ("AJ") Steers (@aaronsteers)

Implements a new CLI invokable as 'uvx airbyte ...' or 'airbyte' when installed.

Commands:
- airbyte workspaces list/get
- airbyte sources list/get/create/delete
- airbyte destinations list/get/create/delete
- airbyte connections list/get/create/delete/sync
- airbyte jobs list/get

Features:
- Structured JSON output for agent consumption
- --describe flag for schema discovery
- Credential resolution: env vars -> ~/.airbyte/credentials file
- Thin wrappers over existing api_util core module
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

Testing This PyAirbyte Version

You can test this version of PyAirbyte using the following:

# Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1775171846-airbyte-cli' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1775171846-airbyte-cli'

PR Slash Commands

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

  • /fix-pr - Fixes most formatting and linting issues
  • /uv-lock - Updates uv.lock file
  • /test-pr - Runs tests with the updated PyAirbyte
  • /prerelease - Builds and publishes a prerelease version to PyPI
📚 Show Repo Guidance

Helpful Resources

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a JSON-first Click Cloud CLI with structured error payloads, lazy credential resolution helpers, SDK-to-dict serializers, command groups for workspaces/sources/destinations/connections/jobs, a main() runner that formats errors as JSON, a docs generator to render Click commands to Markdown, and a console script entry point.

Changes

Cloud CLI

Layer / File(s) Summary
CLI core and JSON helpers
airbyte/cli/cloud_cli.py
JSON stdout and structured stderr helpers, --json option parsing, JSON-help schema registry, and custom Click classes that emit JSON help.
Auth resolvers and context helpers
airbyte/cli/cloud_cli.py, airbyte/cli/_cli_auth.py
Lazy auth-context helpers and Click root group that store raw credential inputs for later resolution via the new auth resolvers.
SDK serializers
airbyte/cli/cloud_cli.py
Serializers that convert SDK response models (workspaces, sources, destinations, connections, jobs) into plain dicts for JSON output.
Workspaces commands
airbyte/cli/cloud_cli.py
workspaces subgroup: list and get commands resolving auth/workspace and emitting serialized workspace dicts.
Sources commands
airbyte/cli/cloud_cli.py
sources subgroup: list, get, create (parses/validates --json), and delete (supports --force → safe_mode).
Destinations commands
airbyte/cli/cloud_cli.py
destinations subgroup: list, get, create (parses/validates --json), and delete (supports --force → safe_mode).
Connections commands
airbyte/cli/cloud_cli.py
connections subgroup: list, get, create (validates name, source_id, destination_id), delete (supports --force), and sync (triggers run, returns job data).
Jobs commands
airbyte/cli/cloud_cli.py
jobs subgroup: list and get returning serialized job dictionaries; list supports connection filtering and --limit.
Entrypoint, packaging, and exports
airbyte/cli/cloud_cli.py, pyproject.toml, airbyte/cli/__init__.py
main() runner maps common exceptions to structured JSON error payloads; package exports updated; pyproject.toml adds airbyte = "airbyte.cli.cloud_cli:main" console script.

CLI docs generator & generated references

Layer / File(s) Summary
Docs generator implementation
docs/generate_cli.py
Renders Click command trees to Markdown, formats options/usages, and writes a combined CLI reference and per-group Markdown files.
Integrate generator into docs build
docs/generate.py
Adds GENERATED_DIR constants, deletes and writes generated CLI references before running pdoc, and updates invocation text.
Generated CLI include modules, contributing doc, and tests
airbyte/cli/connections.py, airbyte/cli/destinations.py, airbyte/cli/jobs.py, airbyte/cli/sources.py, airbyte/cli/workspaces.py, docs/CONTRIBUTING.md, tests/docs_tests/test_cli_docs.py
Adds per-group generated CLI include modules (empty __all__), documents how to regenerate CLI docs, and adds smoke tests that validate the generated Markdown files are produced and non-empty.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as Cloud CLI
    participant Auth as Auth Resolver
    participant Env as Env/Credentials File
    participant API as Airbyte API

    User->>CLI: run command with options
    CLI->>Auth: resolve_* (explicit or ctx values)
    Auth->>Env: check explicit → short env vars → long env vars → credentials file
    Env-->>Auth: return value or empty
    Auth-->>CLI: resolved credentials or raise PyAirbyteInputError
    CLI->>API: call endpoint (workspaces/sources/...)
    API-->>CLI: SDK response
    CLI->>CLI: serialize to dict/JSON
    CLI-->>User: emit JSON (stdout) or error JSON (stderr)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The PR title mentions 'airbyte-cloud' and 'airbyte-local' CLIs, but the actual changeset only implements an 'airbyte' CLI for Cloud operations. No 'airbyte-local' implementation is present. Update the title to accurately reflect the changes, such as 'feat(cli): add agent-first Airbyte Cloud CLI' or 'feat(cli): add airbyte CLI for Cloud operations', or implement the missing airbyte-local CLI.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1775171846-airbyte-cli

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

PyTest Results (Fast Tests Only, No Creds)

85 tests   - 344   84 ✅  - 345   23s ⏱️ - 5m 38s
 1 suites ±  0    0 💤 ±  0 
 1 files   ±  0    1 ❌ +  1 

For more details on these failures, see this check.

Results for commit a8236b5. ± Comparison against base commit 70a7d82.

This pull request removes 344 tests.
tests.docs_tests.test_docs_checked_in ‑ test_docs_generation
tests.integration_tests.destinations.test_source_to_destination ‑ test_destination_write_from_read_result
tests.integration_tests.destinations.test_source_to_destination ‑ test_destination_write_from_source_with_cache
tests.integration_tests.destinations.test_source_to_destination ‑ test_destination_write_from_source_without_cache
tests.integration_tests.destinations.test_source_to_destination ‑ test_duckdb_destination_check
tests.integration_tests.destinations.test_source_to_destination ‑ test_duckdb_destination_spec
tests.integration_tests.destinations.test_source_to_destination ‑ test_duckdb_destination_write_components
tests.integration_tests.secrets.test_gsm_secrets ‑ test_first_connector_secret
tests.integration_tests.secrets.test_gsm_secrets ‑ test_get_connector_secrets
tests.integration_tests.secrets.test_gsm_secrets ‑ test_get_gsm_secret
…

♻️ This comment has been updated with latest results.

coderabbitai[bot]

This comment was marked as resolved.

…ructured errors

- Store raw auth values in ctx.obj; resolve lazily in subcommands so
  --describe works without credentials configured
- Catch OSError and yaml.YAMLError separately in _read_credentials_file
  using guard statements instead of broad try/except
- Add --force flag to delete commands (sources, destinations, connections)
  with safe_mode=True by default
- Wrap main() entry point to produce structured JSON errors on stderr
- Add PyAirbyteInputError import for structured error handling
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

All commands now defer required-option validation until after the
--describe check.  This lets agents discover schemas without providing
auth or resource IDs.
- Add _register_schema() and _emit_json_help() helpers
- Create _JsonHelpGroup and _JsonHelpCommand Click classes
- Add --format option to root cli group (text|json)
- Remove all --describe flags from all commands
- All commands now support --format json --help for JSON schemas
- Root-level --format json --help works via sys.argv fallback
coderabbitai[bot]

This comment was marked as resolved.

- Change _error_json return type to NoReturn for type narrowing
- Always call resolve_workspace_id() in workspaces_list for env/creds fallback
- Add catch-all except Exception handler in main() for JSON error contract
Broad exception catching violates team coding standards. Only catch
specific exceptions that can be handled meaningfully.
Pyrefly does not narrow Optional types after NoReturn calls without
an explicit return statement in the guard clause.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 3, 2026

PyTest Results (Full)

512 tests  +13   493 ✅ +12   23m 46s ⏱️ - 1m 14s
  1 suites ± 0    18 💤 ± 0 
  1 files   ± 0     1 ❌ + 1 

For more details on these failures, see this check.

Results for commit a8236b5. ± Comparison against base commit 70a7d82.

♻️ This comment has been updated with latest results.

@devin-ai-integration

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread docs/generate_cli_docs.py
@aaronsteers Aaron ("AJ") Steers (aaronsteers) changed the title feat(cli): add Cyclopts airbyte cloud and local command groups feat(cli): add airbyte cloud CLI May 18, 2026
aaronsteers

This comment was marked as outdated.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Aaron ("AJ") Steers (@aaronsteers) Done — the requested revert is pushed as b9b5742.

Changed:

  • Removed the extra CLI alias from pyproject.toml.
  • Restored both PR welcome templates to their original uvx / pip install guidance.
  • Refreshed the PR description; it now has 0 occurrences of the requested term.

Validation passed:

  • TOML parse
  • uv run ruff check pyproject.toml
  • git diff --check --cached
  • uv run python -m docs.generate_cli_docs
  • uv run airbyte --help

Devin session

@devin-ai-integration devin-ai-integration Bot changed the title feat(cli): add airbyte cloud CLI feat(cli): add airbyte-cloud and airbyte-local CLIs May 18, 2026
@aaronsteers Aaron ("AJ") Steers (aaronsteers) changed the title feat(cli): add airbyte-cloud and airbyte-local CLIs feat(cli): add airbyte-cloud and airbyte-local CLIs May 18, 2026
Comment thread airbyte/mcp/cloud.py
client_secret=client_secret,
bearer_token=bearer_token,
)
).get_organization(organization_id=organization_id, organization_name=organization_name)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Use this syntax with two or more named args.

Suggested change
).get_organization(organization_id=organization_id, organization_name=organization_name)
).get_organization(
organization_id=organization_id,
organization_name=organization_name,
)

Comment on lines +245 to +269
def list_workspaces_in_organization(
self,
organization_id: str | None = None,
*,
name_contains: str | None = None,
limit: int | None = None,
) -> list[dict[str, object]]:
"""List workspaces in an organization using the Config API."""
resolved_organization_id = organization_id or self.organization_id
if not resolved_organization_id:
raise exc.PyAirbyteInputError(
message="Organization ID is required.",
guidance="Provide an organization ID.",
)

return api_util.list_workspaces_in_organization(
organization_id=resolved_organization_id,
api_root=self.public_api_root,
config_api_root=self.config_api_root,
client_id=self.client_id,
client_secret=self.client_secret,
bearer_token=self.bearer_token,
name_contains=name_contains,
limit=limit,
)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This method can be combined with list_workspaces, with an optional org id input.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@aaronsteers
Copy link
Copy Markdown
Member Author

Reverting to draft (on hold), as we are pivoting to first an internal CLI app launch.

@aaronsteers Aaron ("AJ") Steers (aaronsteers) marked this pull request as draft May 20, 2026 01:51
@aaronsteers
Copy link
Copy Markdown
Member Author

Closing. Migrated to ops repo cli (internal).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants