Skip to content

Commit 3f2ba17

Browse files
aaronsteersoctavia-squidington-iiidevin-ai-integration[bot]
authored
feat: add airbyte-cdk CLI with support for: connector test, secrets fetch and --version (#493)
Co-authored-by: octavia-squidington-iii <[email protected]> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 68ad58d commit 3f2ba17

16 files changed

+1289
-143
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ dist
1515
.idea
1616
.vscode
1717
**/__pycache__
18+
.tmp
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2+
"""CLI commands for `airbyte-cdk`.
3+
4+
This CLI interface allows you to interact with your connector, including
5+
testing and running commands.
6+
7+
**Basic Usage:**
8+
9+
```bash
10+
airbyte-cdk --help
11+
airbyte-cdk --version
12+
airbyte-cdk connector --help
13+
airbyte-cdk manifest --help
14+
```
15+
16+
**Running Statelessly:**
17+
18+
You can run the latest version of this CLI, from any machine, using `pipx` or `uvx`:
19+
20+
```bash
21+
# Run the latest version of the CLI:
22+
pipx run airbyte-cdk connector --help
23+
uvx airbyte-cdk connector --help
24+
25+
# Run from a specific CDK version:
26+
pipx run airbyte-cdk==6.5.1 connector --help
27+
uvx airbyte-cdk==6.5.1 connector --help
28+
```
29+
30+
**Running within your virtualenv:**
31+
32+
You can also run from your connector's virtualenv:
33+
34+
```bash
35+
poetry run airbyte-cdk connector --help
36+
```
37+
38+
"""
39+
40+
from typing import cast
41+
42+
import rich_click as click
43+
44+
from airbyte_cdk.cli.airbyte_cdk._connector import connector_cli_group
45+
from airbyte_cdk.cli.airbyte_cdk._image import image_cli_group
46+
from airbyte_cdk.cli.airbyte_cdk._manifest import manifest_cli_group
47+
from airbyte_cdk.cli.airbyte_cdk._secrets import secrets_cli_group
48+
from airbyte_cdk.cli.airbyte_cdk._version import print_version
49+
50+
51+
@click.group(
52+
help=__doc__.replace("\n", "\n\n"), # Render docstring as help text (markdown)
53+
invoke_without_command=True,
54+
)
55+
@click.option(
56+
"--version",
57+
is_flag=True,
58+
help="Show the version of the Airbyte CDK.",
59+
)
60+
@click.pass_context
61+
def cli(
62+
ctx: click.Context,
63+
version: bool,
64+
) -> None:
65+
"""Airbyte CDK CLI.
66+
67+
Help text is provided from the file-level docstring.
68+
"""
69+
if version:
70+
print_version(short=False)
71+
ctx.exit()
72+
73+
if ctx.invoked_subcommand is None:
74+
# If no subcommand is provided, show the help message.
75+
click.echo(ctx.get_help())
76+
ctx.exit()
77+
78+
79+
cli.add_command(connector_cli_group)
80+
cli.add_command(manifest_cli_group)
81+
cli.add_command(image_cli_group)
82+
cli.add_command(secrets_cli_group)
83+
84+
85+
if __name__ == "__main__":
86+
cli()
+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2+
"""CLI command for `airbyte-cdk`."""
3+
4+
USAGE = """CLI command for `airbyte-cdk`.
5+
6+
This CLI interface allows you to interact with your connector, including
7+
testing and running commands.
8+
9+
**Basic Usage:**
10+
11+
```bash
12+
airbyte-cdk --help
13+
airbyte-cdk connector --help
14+
airbyte-cdk manifest --help
15+
```
16+
17+
**Running Statelessly:**
18+
19+
You can run the latest version of this CLI, from any machine, using `pipx` or `uvx`:
20+
21+
```bash
22+
# Run the latest version of the CLI:
23+
pipx run airbyte-cdk connector --help
24+
uvx airbyte-cdk connector --help
25+
26+
# Run from a specific CDK version:
27+
pipx run airbyte-cdk==6.5.1 connector --help
28+
uvx airbyte-cdk==6.5.1 connector --help
29+
```
30+
31+
**Running within your virtualenv:**
32+
33+
You can also run from your connector's virtualenv:
34+
35+
```bash
36+
poetry run airbyte-cdk connector --help
37+
```
38+
39+
"""
40+
41+
import os
42+
from pathlib import Path
43+
from types import ModuleType
44+
45+
import rich_click as click
46+
47+
# from airbyte_cdk.test.standard_tests import pytest_hooks
48+
from airbyte_cdk.cli.airbyte_cdk._util import resolve_connector_name_and_directory
49+
from airbyte_cdk.test.standard_tests.test_resources import find_connector_root_from_name
50+
from airbyte_cdk.test.standard_tests.util import create_connector_test_suite
51+
52+
click.rich_click.TEXT_MARKUP = "markdown"
53+
54+
pytest: ModuleType | None
55+
try:
56+
import pytest
57+
except ImportError:
58+
pytest = None
59+
# Handle the case where pytest is not installed.
60+
# This prevents import errors when running the script without pytest installed.
61+
# We will raise an error later if pytest is required for a given command.
62+
63+
64+
TEST_FILE_TEMPLATE = '''
65+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
66+
"""FAST Airbyte Standard Tests for the source_pokeapi_w_components source."""
67+
68+
#from airbyte_cdk.test.standard_tests import {base_class_name}
69+
from airbyte_cdk.test.standard_tests.util import create_connector_test_suite
70+
from pathlib import Path
71+
72+
pytest_plugins = [
73+
"airbyte_cdk.test.standard_tests.pytest_hooks",
74+
]
75+
76+
TestSuite = create_connector_test_suite(
77+
connector_directory=Path(),
78+
)
79+
80+
# class TestSuite({base_class_name}):
81+
# """Test suite for the source_pokeapi_w_components source.
82+
83+
# This class inherits from SourceTestSuiteBase and implements all of the tests in the suite.
84+
85+
# As long as the class name starts with "Test", pytest will automatically discover and run the
86+
# tests in this class.
87+
# """
88+
'''
89+
90+
91+
@click.group(
92+
name="connector",
93+
help=__doc__.replace("\n", "\n\n"), # Render docstring as help text (markdown)
94+
)
95+
def connector_cli_group() -> None:
96+
"""Connector related commands."""
97+
pass
98+
99+
100+
@connector_cli_group.command()
101+
@click.option(
102+
"--connector-name",
103+
type=str,
104+
help="Name of the connector to test. Ignored if --connector-directory is provided.",
105+
)
106+
@click.option(
107+
"--connector-directory",
108+
type=click.Path(exists=True, file_okay=False, path_type=Path),
109+
help="Path to the connector directory.",
110+
)
111+
@click.option(
112+
"--collect-only",
113+
is_flag=True,
114+
default=False,
115+
help="Only collect tests, do not run them.",
116+
)
117+
def test(
118+
connector_name: str | None = None,
119+
connector_directory: Path | None = None,
120+
*,
121+
collect_only: bool = False,
122+
) -> None:
123+
"""Run connector tests.
124+
125+
This command runs the standard connector tests for a specific connector.
126+
127+
If no connector name or directory is provided, we will look within the current working
128+
directory. If the current working directory is not a connector directory (e.g. starting
129+
with 'source-') and no connector name or path is provided, the process will fail.
130+
"""
131+
if pytest is None:
132+
raise ImportError(
133+
"pytest is not installed. Please install pytest to run the connector tests."
134+
)
135+
click.echo("Connector test command executed.")
136+
connector_name, connector_directory = resolve_connector_name_and_directory(
137+
connector_name=connector_name,
138+
connector_directory=connector_directory,
139+
)
140+
141+
connector_test_suite = create_connector_test_suite(
142+
connector_name=connector_name if not connector_directory else None,
143+
connector_directory=connector_directory,
144+
)
145+
146+
pytest_args: list[str] = []
147+
if connector_directory:
148+
pytest_args.append(f"--rootdir={connector_directory}")
149+
os.chdir(str(connector_directory))
150+
else:
151+
print("No connector directory provided. Running tests in the current directory.")
152+
153+
file_text = TEST_FILE_TEMPLATE.format(
154+
base_class_name=connector_test_suite.__bases__[0].__name__,
155+
connector_directory=str(connector_directory),
156+
)
157+
test_file_path = Path() / ".tmp" / "integration_tests/test_airbyte_standards.py"
158+
test_file_path = test_file_path.resolve().absolute()
159+
test_file_path.parent.mkdir(parents=True, exist_ok=True)
160+
test_file_path.write_text(file_text)
161+
162+
if collect_only:
163+
pytest_args.append("--collect-only")
164+
165+
pytest_args.append(str(test_file_path))
166+
click.echo(f"Running tests from connector directory: {connector_directory}...")
167+
click.echo(f"Test file: {test_file_path}")
168+
click.echo(f"Collect only: {collect_only}")
169+
click.echo(f"Pytest args: {pytest_args}")
170+
click.echo("Invoking Pytest...")
171+
pytest.main(
172+
pytest_args,
173+
plugins=[],
174+
)
175+
176+
177+
__all__ = [
178+
"connector_cli_group",
179+
]

airbyte_cdk/cli/airbyte_cdk/_image.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2+
"""Docker image commands.
3+
4+
Coming soon.
5+
6+
This module is planned to provide a command line interface (CLI) for building
7+
Docker images for Airbyte CDK connectors.
8+
"""
9+
10+
import click
11+
12+
13+
@click.group(
14+
name="image",
15+
help=__doc__.replace("\n", "\n\n"), # Render docstring as help text (markdown)
16+
)
17+
def image_cli_group() -> None:
18+
"""Docker image commands."""
19+
pass
20+
21+
22+
__all__ = [
23+
"image_cli_group",
24+
]
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2+
"""Manifest related commands.
3+
4+
Coming soon.
5+
6+
This module is planned to provide a command line interface (CLI) for validating
7+
Airbyte CDK manifests.
8+
"""
9+
10+
import rich_click as click
11+
12+
13+
@click.group(
14+
name="manifest",
15+
help=__doc__.replace("\n", "\n\n"), # Render docstring as help text (markdown)
16+
)
17+
def manifest_cli_group() -> None:
18+
"""Manifest related commands."""
19+
pass
20+
21+
22+
__all__ = [
23+
"manifest_cli_group",
24+
]

0 commit comments

Comments
 (0)