Skip to content

Commit bb383d9

Browse files
committed
feat: include manifest path to local args
1 parent fa8d54d commit bb383d9

File tree

1 file changed

+123
-8
lines changed
  • airbyte_cdk/cli/source_declarative_manifest

1 file changed

+123
-8
lines changed

airbyte_cdk/cli/source_declarative_manifest/_run.py

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
import traceback
2323
from collections.abc import Mapping
2424
from pathlib import Path
25-
from typing import Any, cast
25+
from typing import Any, Dict, cast
2626

2727
import orjson
28+
import yaml
2829

2930
from airbyte_cdk.entrypoint import AirbyteEntrypoint, launch
3031
from airbyte_cdk.models import (
@@ -211,12 +212,59 @@ def _parse_inputs_into_config_catalog_state(
211212
ConfiguredAirbyteCatalog | None,
212213
list[AirbyteStateMessage],
213214
]:
214-
parsed_args = AirbyteEntrypoint.parse_args(args)
215-
config = (
216-
ConcurrentDeclarativeSource.read_config(parsed_args.config)
217-
if hasattr(parsed_args, "config")
218-
else None
219-
)
215+
# Extract the --manifest-path argument if present
216+
manifest_path = None
217+
modified_args = []
218+
i = 0
219+
while i < len(args):
220+
if args[i] == "--manifest-path":
221+
if i + 1 < len(args):
222+
manifest_path = args[i + 1]
223+
i += 2 # Skip both the option and its value
224+
else:
225+
raise ValueError("--manifest-path option requires a path value")
226+
else:
227+
modified_args.append(args[i])
228+
i += 1
229+
230+
# Parse the modified arguments
231+
parsed_args = AirbyteEntrypoint.parse_args(modified_args)
232+
233+
# For spec command, we don't need config or manifest
234+
is_spec_command = len(modified_args) > 0 and modified_args[0] == "spec"
235+
236+
# Read config from file if provided
237+
config = None
238+
if hasattr(parsed_args, "config"):
239+
config = ConcurrentDeclarativeSource.read_config(parsed_args.config)
240+
241+
# If manifest_path is provided, read the manifest and inject it into the config
242+
if manifest_path:
243+
try:
244+
with open(manifest_path, "r") as manifest_file:
245+
manifest_content = yaml.safe_load(manifest_file)
246+
247+
# For commands other than spec, a config must be provided
248+
if not is_spec_command and config is None:
249+
raise ValueError(
250+
"When using --manifest-path with commands other than 'spec', "
251+
"a valid --config must also be provided."
252+
)
253+
254+
# For spec command, we can create an empty config if needed
255+
if config is None:
256+
config = {}
257+
258+
# Convert to a mutable dictionary if it's not already
259+
if not isinstance(config, dict):
260+
config = dict(config)
261+
262+
# Inject the manifest into the config
263+
config["__injected_declarative_manifest"] = manifest_content
264+
except Exception as error:
265+
raise ValueError(f"Failed to load manifest file from {manifest_path}: {error}")
266+
267+
# Read catalog and state if provided
220268
catalog = (
221269
ConcurrentDeclarativeSource.read_catalog(parsed_args.catalog)
222270
if hasattr(parsed_args, "catalog")
@@ -233,4 +281,71 @@ def _parse_inputs_into_config_catalog_state(
233281

234282
def run() -> None:
235283
args: list[str] = sys.argv[1:]
236-
handle_command(args)
284+
285+
# First check if this is a local manifest command - if so, proceed with the standard flow
286+
if _is_local_manifest_command(args):
287+
handle_command(args)
288+
return
289+
290+
# Check for --manifest-path argument
291+
try:
292+
manifest_path_index = args.index("--manifest-path")
293+
# Ensure there's a value after --manifest-path
294+
if manifest_path_index + 1 >= len(args):
295+
print("Error: --manifest-path option requires a path value")
296+
sys.exit(1)
297+
298+
# Extract the manifest path and remove both the option and its value from args
299+
manifest_path = args[manifest_path_index + 1]
300+
filtered_args = args.copy()
301+
filtered_args.pop(manifest_path_index + 1) # Remove the path value first
302+
filtered_args.pop(manifest_path_index) # Then remove the --manifest-path option
303+
304+
# For non-spec commands, we need to inject the manifest into the config
305+
if filtered_args and filtered_args[0] != "spec":
306+
# Check for config argument
307+
if "--config" not in filtered_args:
308+
print("Error: When using --manifest-path with commands other than 'spec', --config must also be provided")
309+
sys.exit(1)
310+
311+
config_index = filtered_args.index("--config")
312+
if config_index + 1 >= len(filtered_args):
313+
print("Error: --config option requires a value")
314+
sys.exit(1)
315+
316+
config_path = filtered_args[config_index + 1]
317+
318+
# Read and modify the config file
319+
with open(config_path, "r") as f:
320+
config = json.load(f)
321+
322+
with open(manifest_path, "r") as f:
323+
manifest = yaml.safe_load(f)
324+
325+
# Inject the manifest
326+
config["__injected_declarative_manifest"] = manifest
327+
328+
# Write to a temporary file
329+
temp_config_path = f"{config_path}.temp"
330+
with open(temp_config_path, "w") as f:
331+
json.dump(config, f)
332+
333+
# Replace the config path
334+
filtered_args[config_index + 1] = temp_config_path
335+
336+
# Process the command with the modified arguments
337+
handle_remote_manifest_command(filtered_args)
338+
339+
except ValueError: # --manifest-path not found in args
340+
# For spec command, it's fine to proceed without manifest
341+
if args and args[0] == "spec":
342+
handle_remote_manifest_command(args)
343+
else:
344+
# For other commands, provide a helpful error message
345+
print("Error: When using the source-declarative-manifest command locally, you must either:")
346+
print(" 1. Provide the --manifest-path option pointing to your YAML manifest file, or")
347+
print(" 2. Include the '__injected_declarative_manifest' key in your config JSON with the manifest content")
348+
sys.exit(1)
349+
except Exception as e:
350+
print(f"Error processing arguments: {e}")
351+
sys.exit(1)

0 commit comments

Comments
 (0)