Skip to content

Commit f0bca51

Browse files
feat(cli): Add manifest validate and migrate commands
- Add validate command to check manifest against declarative component schema - Add migrate command to apply migrations and update to latest CDK version - Both commands default to manifest.yaml but support custom paths via --manifest-path - Migrate command includes --dry-run option for preview - Commands provide clear error messages and exit codes - Leverage existing ManifestMigrationHandler for version-aware migrations - Update manifest version to current CDK version after migrations Co-Authored-By: AJ Steers <[email protected]>
1 parent 0b4195b commit f0bca51

File tree

1 file changed

+153
-3
lines changed

1 file changed

+153
-3
lines changed

airbyte_cdk/cli/airbyte_cdk/_manifest.py

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
22
"""Manifest related commands.
33
4-
Coming soon.
5-
6-
This module is planned to provide a command line interface (CLI) for validating
4+
This module provides a command line interface (CLI) for validating and migrating
75
Airbyte CDK manifests.
86
"""
97

8+
import sys
9+
from importlib import metadata
10+
from pathlib import Path
11+
from typing import Any, Dict
12+
1013
import rich_click as click
14+
import yaml
15+
from jsonschema.exceptions import ValidationError
16+
from jsonschema.validators import validate
17+
18+
from airbyte_cdk.manifest_migrations.migration_handler import ManifestMigrationHandler
19+
from airbyte_cdk.sources.declarative.manifest_declarative_source import (
20+
_get_declarative_component_schema,
21+
)
1122

1223

1324
@click.group(
@@ -19,6 +30,145 @@ def manifest_cli_group() -> None:
1930
pass
2031

2132

33+
@manifest_cli_group.command("validate")
34+
@click.option(
35+
"--manifest-path",
36+
type=click.Path(exists=True, path_type=Path),
37+
default="manifest.yaml",
38+
help="Path to the manifest file to validate (default: manifest.yaml)",
39+
)
40+
def validate_manifest(manifest_path: Path) -> None:
41+
"""Validate a manifest file against the declarative component schema.
42+
43+
This command validates the manifest file and checks version compatibility.
44+
If validation fails, it will suggest running the migrate command if needed.
45+
"""
46+
try:
47+
with open(manifest_path, "r") as f:
48+
manifest_dict = yaml.safe_load(f)
49+
50+
if not isinstance(manifest_dict, dict):
51+
click.echo(
52+
f"❌ Error: Manifest file {manifest_path} does not contain a valid YAML dictionary",
53+
err=True,
54+
)
55+
sys.exit(1)
56+
57+
schema = _get_declarative_component_schema()
58+
59+
validate(manifest_dict, schema)
60+
61+
migration_handler = ManifestMigrationHandler(manifest_dict)
62+
migrated_manifest = migration_handler.apply_migrations()
63+
64+
if migrated_manifest != manifest_dict:
65+
click.echo(
66+
f"⚠️ Manifest {manifest_path} is valid but could benefit from migration to the latest version.",
67+
err=True,
68+
)
69+
click.echo(
70+
"Run 'airbyte-cdk manifest migrate' to apply available migrations.", err=True
71+
)
72+
sys.exit(1)
73+
74+
click.echo(f"✅ Manifest {manifest_path} is valid and up to date.")
75+
76+
except FileNotFoundError:
77+
click.echo(f"❌ Error: Manifest file {manifest_path} not found", err=True)
78+
sys.exit(1)
79+
except yaml.YAMLError as e:
80+
click.echo(f"❌ Error: Invalid YAML in {manifest_path}: {e}", err=True)
81+
sys.exit(1)
82+
except ValidationError as e:
83+
click.echo(f"❌ Validation failed for {manifest_path}:", err=True)
84+
click.echo(f" {e.message}", err=True)
85+
click.echo(
86+
"Run 'airbyte-cdk manifest migrate' to apply available migrations that might fix this issue.",
87+
err=True,
88+
)
89+
sys.exit(1)
90+
except Exception as e:
91+
click.echo(f"❌ Unexpected error validating {manifest_path}: {e}", err=True)
92+
sys.exit(1)
93+
94+
95+
@manifest_cli_group.command("migrate")
96+
@click.option(
97+
"--manifest-path",
98+
type=click.Path(exists=True, path_type=Path),
99+
default="manifest.yaml",
100+
help="Path to the manifest file to migrate (default: manifest.yaml)",
101+
)
102+
@click.option(
103+
"--dry-run",
104+
is_flag=True,
105+
help="Show what changes would be made without actually modifying the file",
106+
)
107+
def migrate_manifest(manifest_path: Path, dry_run: bool) -> None:
108+
"""Apply migrations to make a manifest file compatible with the latest version.
109+
110+
This command applies all necessary migrations to update the manifest file
111+
to be compatible with the latest CDK version.
112+
"""
113+
try:
114+
with open(manifest_path, "r") as f:
115+
original_manifest = yaml.safe_load(f)
116+
117+
if not isinstance(original_manifest, dict):
118+
click.echo(
119+
f"❌ Error: Manifest file {manifest_path} does not contain a valid YAML dictionary",
120+
err=True,
121+
)
122+
sys.exit(1)
123+
124+
migration_handler = ManifestMigrationHandler(original_manifest)
125+
migrated_manifest = migration_handler.apply_migrations()
126+
127+
if migrated_manifest == original_manifest:
128+
click.echo(f"✅ Manifest {manifest_path} is already up to date - no migrations needed.")
129+
return
130+
131+
if dry_run:
132+
click.echo(f"🔍 Dry run - changes that would be made to {manifest_path}:")
133+
click.echo(
134+
" Migrations would be applied to update the manifest to the latest version."
135+
)
136+
click.echo(" Run without --dry-run to apply the changes.")
137+
return
138+
139+
current_cdk_version = metadata.version("airbyte_cdk")
140+
migrated_manifest["version"] = current_cdk_version
141+
142+
with open(manifest_path, "w") as f:
143+
yaml.dump(migrated_manifest, f, default_flow_style=False, sort_keys=False)
144+
145+
click.echo(
146+
f"✅ Successfully migrated {manifest_path} to the latest version ({current_cdk_version})."
147+
)
148+
149+
try:
150+
schema = _get_declarative_component_schema()
151+
validate(migrated_manifest, schema)
152+
click.echo(f"✅ Migrated manifest {manifest_path} passes validation.")
153+
except ValidationError as e:
154+
click.echo(
155+
f"⚠️ Warning: Migrated manifest {manifest_path} still has validation issues:",
156+
err=True,
157+
)
158+
click.echo(f" {e.message}", err=True)
159+
click.echo(" Manual fixes may be required.", err=True)
160+
161+
except FileNotFoundError:
162+
click.echo(f"❌ Error: Manifest file {manifest_path} not found", err=True)
163+
sys.exit(1)
164+
except yaml.YAMLError as e:
165+
click.echo(f"❌ Error: Invalid YAML in {manifest_path}: {e}", err=True)
166+
sys.exit(1)
167+
except Exception as e:
168+
click.echo(f"❌ Unexpected error migrating {manifest_path}: {e}", err=True)
169+
sys.exit(1)
170+
171+
22172
__all__ = [
23173
"manifest_cli_group",
24174
]

0 commit comments

Comments
 (0)