airbyte-internal-ops 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {airbyte_internal_ops-0.2.0.dist-info → airbyte_internal_ops-0.2.1.dist-info}/METADATA +2 -2
  2. {airbyte_internal_ops-0.2.0.dist-info → airbyte_internal_ops-0.2.1.dist-info}/RECORD +36 -36
  3. airbyte_ops_mcp/__init__.py +2 -2
  4. airbyte_ops_mcp/cli/cloud.py +207 -306
  5. airbyte_ops_mcp/cloud_admin/api_client.py +51 -26
  6. airbyte_ops_mcp/cloud_admin/connection_config.py +2 -2
  7. airbyte_ops_mcp/github_actions.py +69 -1
  8. airbyte_ops_mcp/mcp/_http_headers.py +56 -0
  9. airbyte_ops_mcp/mcp/_mcp_utils.py +2 -2
  10. airbyte_ops_mcp/mcp/cloud_connector_versions.py +57 -43
  11. airbyte_ops_mcp/mcp/github.py +34 -1
  12. airbyte_ops_mcp/mcp/{live_tests.py → regression_tests.py} +158 -147
  13. airbyte_ops_mcp/mcp/server.py +3 -3
  14. airbyte_ops_mcp/prod_db_access/db_engine.py +7 -11
  15. airbyte_ops_mcp/{live_tests → regression_tests}/__init__.py +3 -3
  16. airbyte_ops_mcp/{live_tests → regression_tests}/cdk_secrets.py +1 -1
  17. airbyte_ops_mcp/{live_tests → regression_tests}/connection_secret_retriever.py +3 -3
  18. airbyte_ops_mcp/{live_tests → regression_tests}/connector_runner.py +1 -1
  19. airbyte_ops_mcp/{live_tests → regression_tests}/message_cache/__init__.py +3 -1
  20. airbyte_ops_mcp/{live_tests → regression_tests}/regression/__init__.py +1 -1
  21. airbyte_ops_mcp/{live_tests → regression_tests}/schema_generation.py +3 -1
  22. airbyte_ops_mcp/{live_tests → regression_tests}/validation/__init__.py +2 -2
  23. airbyte_ops_mcp/{live_tests → regression_tests}/validation/record_validators.py +4 -2
  24. {airbyte_internal_ops-0.2.0.dist-info → airbyte_internal_ops-0.2.1.dist-info}/WHEEL +0 -0
  25. {airbyte_internal_ops-0.2.0.dist-info → airbyte_internal_ops-0.2.1.dist-info}/entry_points.txt +0 -0
  26. /airbyte_ops_mcp/{live_tests → regression_tests}/ci_output.py +0 -0
  27. /airbyte_ops_mcp/{live_tests → regression_tests}/commons/__init__.py +0 -0
  28. /airbyte_ops_mcp/{live_tests → regression_tests}/config.py +0 -0
  29. /airbyte_ops_mcp/{live_tests → regression_tests}/connection_fetcher.py +0 -0
  30. /airbyte_ops_mcp/{live_tests → regression_tests}/evaluation_modes.py +0 -0
  31. /airbyte_ops_mcp/{live_tests → regression_tests}/http_metrics.py +0 -0
  32. /airbyte_ops_mcp/{live_tests → regression_tests}/message_cache/duckdb_cache.py +0 -0
  33. /airbyte_ops_mcp/{live_tests → regression_tests}/models.py +0 -0
  34. /airbyte_ops_mcp/{live_tests → regression_tests}/obfuscation.py +0 -0
  35. /airbyte_ops_mcp/{live_tests → regression_tests}/regression/comparators.py +0 -0
  36. /airbyte_ops_mcp/{live_tests → regression_tests}/validation/catalog_validators.py +0 -0
@@ -5,8 +5,7 @@ Commands:
5
5
  airbyte-ops cloud connector get-version-info - Get connector version info
6
6
  airbyte-ops cloud connector set-version-override - Set connector version override
7
7
  airbyte-ops cloud connector clear-version-override - Clear connector version override
8
- airbyte-ops cloud connector live-test - Run live validation tests on a connector
9
- airbyte-ops cloud connector regression-test - Run regression tests comparing connector versions
8
+ airbyte-ops cloud connector regression-test - Run regression tests (single-version or comparison)
10
9
  airbyte-ops cloud connector fetch-connection-config - Fetch connection config to local file
11
10
  """
12
11
 
@@ -45,8 +44,12 @@ from airbyte_ops_mcp.constants import (
45
44
  DEFAULT_CLOUD_SQL_PROXY_PORT,
46
45
  ENV_GCP_PROD_DB_ACCESS_CREDENTIALS,
47
46
  )
48
- from airbyte_ops_mcp.live_tests.cdk_secrets import get_first_config_from_secrets
49
- from airbyte_ops_mcp.live_tests.ci_output import (
47
+ from airbyte_ops_mcp.mcp.cloud_connector_versions import (
48
+ get_cloud_connector_version,
49
+ set_cloud_connector_version_override,
50
+ )
51
+ from airbyte_ops_mcp.regression_tests.cdk_secrets import get_first_config_from_secrets
52
+ from airbyte_ops_mcp.regression_tests.ci_output import (
50
53
  generate_regression_report,
51
54
  get_report_summary,
52
55
  write_github_output,
@@ -55,32 +58,28 @@ from airbyte_ops_mcp.live_tests.ci_output import (
55
58
  write_json_output,
56
59
  write_test_summary,
57
60
  )
58
- from airbyte_ops_mcp.live_tests.connection_fetcher import (
61
+ from airbyte_ops_mcp.regression_tests.connection_fetcher import (
59
62
  fetch_connection_data,
60
63
  save_connection_data_to_files,
61
64
  )
62
- from airbyte_ops_mcp.live_tests.connection_secret_retriever import (
65
+ from airbyte_ops_mcp.regression_tests.connection_secret_retriever import (
63
66
  enrich_config_with_secrets,
64
67
  should_use_secret_retriever,
65
68
  )
66
- from airbyte_ops_mcp.live_tests.connector_runner import (
69
+ from airbyte_ops_mcp.regression_tests.connector_runner import (
67
70
  ConnectorRunner,
68
71
  ensure_image_available,
69
72
  )
70
- from airbyte_ops_mcp.live_tests.http_metrics import (
73
+ from airbyte_ops_mcp.regression_tests.http_metrics import (
71
74
  MitmproxyManager,
72
75
  parse_http_dump,
73
76
  )
74
- from airbyte_ops_mcp.live_tests.models import (
77
+ from airbyte_ops_mcp.regression_tests.models import (
75
78
  Command,
76
79
  ConnectorUnderTest,
77
80
  ExecutionInputs,
78
81
  TargetOrControl,
79
82
  )
80
- from airbyte_ops_mcp.mcp.cloud_connector_versions import (
81
- get_cloud_connector_version,
82
- set_cloud_connector_version_override,
83
- )
84
83
 
85
84
  # Path to connectors directory within the airbyte repo
86
85
  CONNECTORS_SUBDIR = Path("airbyte-integrations") / "connectors"
@@ -596,187 +595,6 @@ def _fetch_control_image_from_metadata(connector_name: str) -> str | None:
596
595
  return f"{docker_repository}:{docker_image_tag}"
597
596
 
598
597
 
599
- @connector_app.command(name="live-test")
600
- def live_test(
601
- connector_image: Annotated[
602
- str | None,
603
- Parameter(
604
- help="Full connector image name with tag (e.g., airbyte/source-github:1.0.0). "
605
- "Optional if connector_name or connection_id is provided."
606
- ),
607
- ] = None,
608
- connector_name: Annotated[
609
- str | None,
610
- Parameter(
611
- help="Connector name to build from source (e.g., 'source-pokeapi'). "
612
- "If provided, builds the image locally with tag 'dev'."
613
- ),
614
- ] = None,
615
- repo_root: Annotated[
616
- str | None,
617
- Parameter(
618
- help="Path to the airbyte repo root. Required if connector_name is provided "
619
- "and the repo cannot be auto-detected."
620
- ),
621
- ] = None,
622
- command: Annotated[
623
- Literal["spec", "check", "discover", "read"],
624
- Parameter(help="The Airbyte command to run."),
625
- ] = "check",
626
- connection_id: Annotated[
627
- str | None,
628
- Parameter(
629
- help="Airbyte Cloud connection ID to fetch config/catalog from. "
630
- "Mutually exclusive with config-path/catalog-path. "
631
- "If provided, connector_image can be auto-detected."
632
- ),
633
- ] = None,
634
- config_path: Annotated[
635
- str | None,
636
- Parameter(help="Path to the connector config JSON file."),
637
- ] = None,
638
- catalog_path: Annotated[
639
- str | None,
640
- Parameter(help="Path to the configured catalog JSON file (required for read)."),
641
- ] = None,
642
- state_path: Annotated[
643
- str | None,
644
- Parameter(help="Path to the state JSON file (optional for read)."),
645
- ] = None,
646
- output_dir: Annotated[
647
- str,
648
- Parameter(help="Directory to store test artifacts."),
649
- ] = "/tmp/live_test_artifacts",
650
- ) -> None:
651
- """Run live validation tests on a connector.
652
-
653
- This command runs the specified Airbyte protocol command against a connector
654
- and validates the output. Results are written to the output directory and
655
- to GitHub Actions outputs if running in CI.
656
-
657
- You can provide the connector image in three ways:
658
- 1. --connector-image: Use a pre-built image from Docker registry
659
- 2. --connector-name: Build the image locally from source code
660
- 3. --connection-id: Auto-detect from an Airbyte Cloud connection
661
-
662
- You can provide config/catalog either via file paths OR via a connection_id
663
- that fetches them from Airbyte Cloud.
664
- """
665
- output_path = Path(output_dir)
666
- output_path.mkdir(parents=True, exist_ok=True)
667
-
668
- cmd = Command(command)
669
-
670
- config_file: Path | None = None
671
- catalog_file: Path | None = None
672
- state_file = Path(state_path) if state_path else None
673
- resolved_connector_image: str | None = connector_image
674
-
675
- # If connector_name is provided, build the image from source
676
- if connector_name:
677
- if connector_image:
678
- write_github_output("success", False)
679
- write_github_output(
680
- "error", "Cannot specify both connector_image and connector_name"
681
- )
682
- exit_with_error("Cannot specify both connector_image and connector_name")
683
-
684
- repo_root_path = Path(repo_root) if repo_root else None
685
- built_image = _build_connector_image_from_source(
686
- connector_name=connector_name,
687
- repo_root=repo_root_path,
688
- tag="dev",
689
- )
690
- if not built_image:
691
- write_github_output("success", False)
692
- write_github_output("error", f"Failed to build image for {connector_name}")
693
- exit_with_error(f"Failed to build image for {connector_name}")
694
- resolved_connector_image = built_image
695
-
696
- if connection_id:
697
- if config_path or catalog_path:
698
- write_github_output("success", False)
699
- write_github_output(
700
- "error", "Cannot specify both connection_id and file paths"
701
- )
702
- exit_with_error(
703
- "Cannot specify both connection_id and config_path/catalog_path"
704
- )
705
-
706
- print_success(f"Fetching config/catalog from connection: {connection_id}")
707
- connection_data = fetch_connection_data(connection_id)
708
- config_file, catalog_file = save_connection_data_to_files(
709
- connection_data, output_path / "connection_data"
710
- )
711
- print_success(
712
- f"Fetched config for source: {connection_data.source_name} "
713
- f"with {len(connection_data.stream_names)} streams"
714
- )
715
-
716
- if not resolved_connector_image and connection_data.connector_image:
717
- resolved_connector_image = connection_data.connector_image
718
- print_success(f"Auto-detected connector image: {resolved_connector_image}")
719
- else:
720
- config_file = Path(config_path) if config_path else None
721
- catalog_file = Path(catalog_path) if catalog_path else None
722
-
723
- if not resolved_connector_image:
724
- write_github_output("success", False)
725
- write_github_output("error", "Missing connector image")
726
- exit_with_error(
727
- "You must provide one of the following: a connector_image, a connector_name, "
728
- "or a connection_id for a connection that has an associated connector image. "
729
- "If using connection_id, ensure the connection has a connector image configured."
730
- )
731
-
732
- # If connector_name was provided, we just built the image locally and it is already
733
- # available in Docker, so we skip the image availability check/pull. Only try to pull
734
- # if we didn't just build it (i.e., using a pre-built image from registry).
735
- if not connector_name and not ensure_image_available(resolved_connector_image):
736
- write_github_output("success", False)
737
- write_github_output(
738
- "error", f"Failed to pull image: {resolved_connector_image}"
739
- )
740
- exit_with_error(f"Failed to pull connector image: {resolved_connector_image}")
741
-
742
- result = _run_connector_command(
743
- connector_image=resolved_connector_image,
744
- command=cmd,
745
- output_dir=output_path,
746
- target_or_control=TargetOrControl.TARGET,
747
- config_path=config_file,
748
- catalog_path=catalog_file,
749
- state_path=state_file,
750
- )
751
-
752
- print_json(result)
753
-
754
- write_github_outputs(
755
- {
756
- "success": result["success"],
757
- "connector": resolved_connector_image,
758
- "command": command,
759
- "exit_code": result["exit_code"],
760
- }
761
- )
762
-
763
- write_test_summary(
764
- connector_image=resolved_connector_image,
765
- test_type="live-test",
766
- success=result["success"],
767
- results={
768
- "command": command,
769
- "exit_code": result["exit_code"],
770
- "output_dir": output_dir,
771
- },
772
- )
773
-
774
- if result["success"]:
775
- print_success(f"Live test passed for {resolved_connector_image}")
776
- else:
777
- exit_with_error(f"Live test failed for {resolved_connector_image}")
778
-
779
-
780
598
  def _run_with_optional_http_metrics(
781
599
  connector_image: str,
782
600
  command: Command,
@@ -855,25 +673,34 @@ def _run_with_optional_http_metrics(
855
673
 
856
674
  @connector_app.command(name="regression-test")
857
675
  def regression_test(
858
- target_image: Annotated[
676
+ skip_compare: Annotated[
677
+ bool,
678
+ Parameter(
679
+ help="If True, skip comparison and run single-version tests only. "
680
+ "If False (default), run comparison tests (target vs control)."
681
+ ),
682
+ ] = False,
683
+ test_image: Annotated[
859
684
  str | None,
860
685
  Parameter(
861
- help="Target connector image (new version) with tag (e.g., airbyte/source-github:2.0.0). "
862
- "Optional if connector_name is provided."
686
+ help="Test connector image with tag (e.g., airbyte/source-github:1.0.0). "
687
+ "This is the image under test - in comparison mode, it's compared against control_image."
863
688
  ),
864
689
  ] = None,
865
690
  control_image: Annotated[
866
691
  str | None,
867
692
  Parameter(
868
693
  help="Control connector image (baseline version) with tag (e.g., airbyte/source-github:1.0.0). "
869
- "Optional if connection_id is provided (auto-detected from connection)."
694
+ "Ignored if `skip_compare=True`."
870
695
  ),
871
696
  ] = None,
872
697
  connector_name: Annotated[
873
698
  str | None,
874
699
  Parameter(
875
- help="Connector name to build target image from source (e.g., 'source-pokeapi'). "
876
- "If provided, builds the target image locally with tag 'dev'."
700
+ help="Connector name to build image from source (e.g., 'source-pokeapi'). "
701
+ "If provided, builds the image locally with tag 'dev'. "
702
+ "For comparison tests (default), this builds the target image. "
703
+ "For single-version tests (skip_compare=True), this builds the test image."
877
704
  ),
878
705
  ] = None,
879
706
  repo_root: Annotated[
@@ -892,7 +719,7 @@ def regression_test(
892
719
  Parameter(
893
720
  help="Airbyte Cloud connection ID to fetch config/catalog from. "
894
721
  "Mutually exclusive with config-path/catalog-path. "
895
- "If provided, control_image can be auto-detected."
722
+ "If provided, test_image/control_image can be auto-detected."
896
723
  ),
897
724
  ] = None,
898
725
  config_path: Annotated[
@@ -915,26 +742,30 @@ def regression_test(
915
742
  bool,
916
743
  Parameter(
917
744
  help="Capture HTTP traffic metrics via mitmproxy (experimental). "
918
- "Requires mitmdump to be installed."
745
+ "Requires mitmdump to be installed. Only used in comparison mode."
919
746
  ),
920
747
  ] = False,
921
748
  ) -> None:
922
- """Run regression tests comparing two connector versions.
749
+ """Run regression tests on connectors.
750
+
751
+ This command supports two modes:
923
752
 
924
- This command runs the specified Airbyte protocol command against both the
925
- target (new) and control (baseline) connector versions, then compares the
926
- results. This helps identify regressions between versions.
753
+ Comparison mode (skip_compare=False, default):
754
+ Runs the specified Airbyte protocol command against both the target (new)
755
+ and control (baseline) connector versions, then compares the results.
756
+ This helps identify regressions between versions.
757
+
758
+ Single-version mode (skip_compare=True):
759
+ Runs the specified Airbyte protocol command against a single connector
760
+ and validates the output. No comparison is performed.
927
761
 
928
762
  Results are written to the output directory and to GitHub Actions outputs
929
763
  if running in CI.
930
764
 
931
- You can provide the target image in two ways:
932
- 1. --target-image: Use a pre-built image from Docker registry
933
- 2. --connector-name: Build the target image locally from source code
934
-
935
- You can provide the control image in two ways:
936
- 1. --control-image: Use a pre-built image from Docker registry
937
- 2. --connection-id: Auto-detect from an Airbyte Cloud connection
765
+ You can provide the test image in three ways:
766
+ 1. --test-image: Use a pre-built image from Docker registry
767
+ 2. --connector-name: Build the image locally from source code
768
+ 3. --connection-id: Auto-detect from an Airbyte Cloud connection
938
769
 
939
770
  You can provide config/catalog either via file paths OR via a connection_id
940
771
  that fetches them from Airbyte Cloud.
@@ -947,17 +778,31 @@ def regression_test(
947
778
  config_file: Path | None = None
948
779
  catalog_file: Path | None = None
949
780
  state_file = Path(state_path) if state_path else None
950
- resolved_target_image: str | None = target_image
781
+
782
+ # Resolve the test image (used in both single-version and comparison modes)
783
+ resolved_test_image: str | None = test_image
951
784
  resolved_control_image: str | None = control_image
952
785
 
953
- # If connector_name is provided, build the target image from source
786
+ # Validate conflicting parameters
787
+ # Single-version mode: reject comparison-specific parameters
788
+ if skip_compare and control_image:
789
+ write_github_output("success", False)
790
+ write_github_output(
791
+ "error", "Cannot specify control_image with skip_compare=True"
792
+ )
793
+ exit_with_error(
794
+ "Cannot specify --control-image with --skip-compare. "
795
+ "Control image is only used in comparison mode."
796
+ )
797
+
798
+ # If connector_name is provided, build the image from source
954
799
  if connector_name:
955
- if target_image:
800
+ if resolved_test_image:
956
801
  write_github_output("success", False)
957
802
  write_github_output(
958
- "error", "Cannot specify both target_image and connector_name"
803
+ "error", "Cannot specify both test_image and connector_name"
959
804
  )
960
- exit_with_error("Cannot specify both target_image and connector_name")
805
+ exit_with_error("Cannot specify both --test-image and --connector-name")
961
806
 
962
807
  repo_root_path = Path(repo_root) if repo_root else None
963
808
  built_image = _build_connector_image_from_source(
@@ -969,7 +814,7 @@ def regression_test(
969
814
  write_github_output("success", False)
970
815
  write_github_output("error", f"Failed to build image for {connector_name}")
971
816
  exit_with_error(f"Failed to build image for {connector_name}")
972
- resolved_target_image = built_image
817
+ resolved_test_image = built_image
973
818
 
974
819
  if connection_id:
975
820
  if config_path or catalog_path:
@@ -997,10 +842,11 @@ def regression_test(
997
842
  print_success("Successfully retrieved unmasked secrets from database")
998
843
  except Exception as e:
999
844
  print_error(f"Failed to retrieve unmasked secrets: {e}")
1000
- print_error(
1001
- "Proceeding with masked config from public API - tests may fail due to masked credentials. "
1002
- "If you expected unmasked secrets, verify that the USE_CONNECTION_SECRET_RETRIEVER flag is enabled "
1003
- f"and that the {ENV_GCP_PROD_DB_ACCESS_CREDENTIALS} environment variable is set with valid database credentials."
845
+ exit_with_error(
846
+ f"Failed to retrieve unmasked secrets: {e}\n"
847
+ f"Unset USE_CONNECTION_SECRET_RETRIEVER or verify that the "
848
+ f"{ENV_GCP_PROD_DB_ACCESS_CREDENTIALS} environment variable is set "
849
+ f"with valid database credentials and that the Cloud SQL Proxy is running."
1004
850
  )
1005
851
 
1006
852
  config_file, catalog_file = save_connection_data_to_files(
@@ -1011,8 +857,16 @@ def regression_test(
1011
857
  f"with {len(connection_data.stream_names)} streams"
1012
858
  )
1013
859
 
1014
- # Auto-detect control_image from connection if not provided
1015
- if not resolved_control_image and connection_data.connector_image:
860
+ # Auto-detect test/control image from connection if not provided
861
+ if not resolved_test_image and connection_data.connector_image:
862
+ resolved_test_image = connection_data.connector_image
863
+ print_success(f"Auto-detected test image: {resolved_test_image}")
864
+
865
+ if (
866
+ not skip_compare
867
+ and not resolved_control_image
868
+ and connection_data.connector_image
869
+ ):
1016
870
  resolved_control_image = connection_data.connector_image
1017
871
  print_success(f"Auto-detected control image: {resolved_control_image}")
1018
872
  elif config_path:
@@ -1049,24 +903,24 @@ def regression_test(
1049
903
  config_file = None
1050
904
  catalog_file = Path(catalog_path) if catalog_path else None
1051
905
 
1052
- # Auto-detect control_image from metadata.yaml if connector_name is provided
1053
- if not resolved_control_image and connector_name:
906
+ # Auto-detect control_image from metadata.yaml if connector_name is provided (comparison mode only)
907
+ if not skip_compare and not resolved_control_image and connector_name:
1054
908
  resolved_control_image = _fetch_control_image_from_metadata(connector_name)
1055
909
  if resolved_control_image:
1056
910
  print_success(
1057
911
  f"Auto-detected control image from metadata.yaml: {resolved_control_image}"
1058
912
  )
1059
913
 
1060
- # Validate that we have both images
1061
- if not resolved_target_image:
914
+ # Validate that we have the required images
915
+ if not resolved_test_image:
1062
916
  write_github_output("success", False)
1063
- write_github_output("error", "No target image specified")
917
+ write_github_output("error", "No test image specified")
1064
918
  exit_with_error(
1065
- "You must provide one of the following: a target_image or a connector_name "
1066
- "to build the target image from source."
919
+ "You must provide one of the following: a test_image, a connector_name "
920
+ "to build the image from source, or a connection_id to auto-detect the image."
1067
921
  )
1068
922
 
1069
- if not resolved_control_image:
923
+ if not skip_compare and not resolved_control_image:
1070
924
  write_github_output("success", False)
1071
925
  write_github_output("error", "No control image specified")
1072
926
  exit_with_error(
@@ -1076,97 +930,144 @@ def regression_test(
1076
930
  )
1077
931
 
1078
932
  # Pull images if they weren't just built locally
1079
- # If connector_name was provided, we just built the target image locally
1080
- if not connector_name and not ensure_image_available(resolved_target_image):
933
+ # If connector_name was provided, we just built the test image locally
934
+ if not connector_name and not ensure_image_available(resolved_test_image):
1081
935
  write_github_output("success", False)
1082
- write_github_output("error", f"Failed to pull image: {resolved_target_image}")
1083
- exit_with_error(
1084
- f"Failed to pull target connector image: {resolved_target_image}"
1085
- )
1086
-
1087
- if not ensure_image_available(resolved_control_image):
936
+ write_github_output("error", f"Failed to pull image: {resolved_test_image}")
937
+ exit_with_error(f"Failed to pull test image: {resolved_test_image}")
938
+
939
+ if (
940
+ not skip_compare
941
+ and resolved_control_image
942
+ and not ensure_image_available(resolved_control_image)
943
+ ):
1088
944
  write_github_output("success", False)
1089
945
  write_github_output("error", f"Failed to pull image: {resolved_control_image}")
1090
946
  exit_with_error(
1091
947
  f"Failed to pull control connector image: {resolved_control_image}"
1092
948
  )
1093
949
 
1094
- target_output = output_path / "target"
1095
- control_output = output_path / "control"
1096
-
1097
- target_result = _run_with_optional_http_metrics(
1098
- connector_image=resolved_target_image,
1099
- command=cmd,
1100
- output_dir=target_output,
1101
- target_or_control=TargetOrControl.TARGET,
1102
- enable_http_metrics=enable_http_metrics,
1103
- config_path=config_file,
1104
- catalog_path=catalog_file,
1105
- state_path=state_file,
1106
- )
950
+ # Execute the appropriate mode
951
+ if skip_compare:
952
+ # Single-version mode: run only the connector image
953
+ result = _run_connector_command(
954
+ connector_image=resolved_test_image,
955
+ command=cmd,
956
+ output_dir=output_path,
957
+ target_or_control=TargetOrControl.TARGET,
958
+ config_path=config_file,
959
+ catalog_path=catalog_file,
960
+ state_path=state_file,
961
+ )
1107
962
 
1108
- control_result = _run_with_optional_http_metrics(
1109
- connector_image=resolved_control_image,
1110
- command=cmd,
1111
- output_dir=control_output,
1112
- target_or_control=TargetOrControl.CONTROL,
1113
- enable_http_metrics=enable_http_metrics,
1114
- config_path=config_file,
1115
- catalog_path=catalog_file,
1116
- state_path=state_file,
1117
- )
963
+ print_json(result)
1118
964
 
1119
- both_succeeded = target_result["success"] and control_result["success"]
1120
- regression_detected = target_result["success"] != control_result["success"]
965
+ write_github_outputs(
966
+ {
967
+ "success": result["success"],
968
+ "connector": resolved_test_image,
969
+ "command": command,
970
+ "exit_code": result["exit_code"],
971
+ }
972
+ )
1121
973
 
1122
- combined_result = {
1123
- "target": target_result,
1124
- "control": control_result,
1125
- "both_succeeded": both_succeeded,
1126
- "regression_detected": regression_detected,
1127
- }
974
+ write_test_summary(
975
+ connector_image=resolved_test_image,
976
+ test_type="regression-test",
977
+ success=result["success"],
978
+ results={
979
+ "command": command,
980
+ "exit_code": result["exit_code"],
981
+ "output_dir": output_dir,
982
+ },
983
+ )
984
+
985
+ if result["success"]:
986
+ print_success(
987
+ f"Single-version regression test passed for {resolved_test_image}"
988
+ )
989
+ else:
990
+ exit_with_error(
991
+ f"Single-version regression test failed for {resolved_test_image}"
992
+ )
993
+ else:
994
+ # Comparison mode: run both target and control images
995
+ target_output = output_path / "target"
996
+ control_output = output_path / "control"
997
+
998
+ target_result = _run_with_optional_http_metrics(
999
+ connector_image=resolved_test_image,
1000
+ command=cmd,
1001
+ output_dir=target_output,
1002
+ target_or_control=TargetOrControl.TARGET,
1003
+ enable_http_metrics=enable_http_metrics,
1004
+ config_path=config_file,
1005
+ catalog_path=catalog_file,
1006
+ state_path=state_file,
1007
+ )
1128
1008
 
1129
- print_json(combined_result)
1009
+ control_result = _run_with_optional_http_metrics(
1010
+ connector_image=resolved_control_image, # type: ignore[arg-type]
1011
+ command=cmd,
1012
+ output_dir=control_output,
1013
+ target_or_control=TargetOrControl.CONTROL,
1014
+ enable_http_metrics=enable_http_metrics,
1015
+ config_path=config_file,
1016
+ catalog_path=catalog_file,
1017
+ state_path=state_file,
1018
+ )
1019
+
1020
+ both_succeeded = target_result["success"] and control_result["success"]
1021
+ regression_detected = target_result["success"] != control_result["success"]
1130
1022
 
1131
- write_github_outputs(
1132
- {
1133
- "success": both_succeeded and not regression_detected,
1134
- "target_image": resolved_target_image,
1135
- "control_image": resolved_control_image,
1136
- "command": command,
1137
- "target_exit_code": target_result["exit_code"],
1138
- "control_exit_code": control_result["exit_code"],
1023
+ combined_result = {
1024
+ "target": target_result,
1025
+ "control": control_result,
1026
+ "both_succeeded": both_succeeded,
1139
1027
  "regression_detected": regression_detected,
1140
1028
  }
1141
- )
1142
1029
 
1143
- write_json_output("regression_report", combined_result)
1144
-
1145
- report_path = generate_regression_report(
1146
- target_image=resolved_target_image,
1147
- control_image=resolved_control_image,
1148
- command=command,
1149
- target_result=target_result,
1150
- control_result=control_result,
1151
- output_dir=output_path,
1152
- )
1153
- print_success(f"Generated regression report: {report_path}")
1030
+ print_json(combined_result)
1031
+
1032
+ write_github_outputs(
1033
+ {
1034
+ "success": both_succeeded and not regression_detected,
1035
+ "target_image": resolved_test_image,
1036
+ "control_image": resolved_control_image,
1037
+ "command": command,
1038
+ "target_exit_code": target_result["exit_code"],
1039
+ "control_exit_code": control_result["exit_code"],
1040
+ "regression_detected": regression_detected,
1041
+ }
1042
+ )
1154
1043
 
1155
- summary = get_report_summary(report_path)
1156
- write_github_summary(summary)
1044
+ write_json_output("regression_report", combined_result)
1157
1045
 
1158
- if regression_detected:
1159
- exit_with_error(
1160
- f"Regression detected between {resolved_target_image} and {resolved_control_image}"
1161
- )
1162
- elif both_succeeded:
1163
- print_success(
1164
- f"Regression test passed for {resolved_target_image} vs {resolved_control_image}"
1165
- )
1166
- else:
1167
- exit_with_error(
1168
- f"Both versions failed for {resolved_target_image} vs {resolved_control_image}"
1046
+ report_path = generate_regression_report(
1047
+ target_image=resolved_test_image,
1048
+ control_image=resolved_control_image, # type: ignore[arg-type]
1049
+ command=command,
1050
+ target_result=target_result,
1051
+ control_result=control_result,
1052
+ output_dir=output_path,
1169
1053
  )
1054
+ print_success(f"Generated regression report: {report_path}")
1055
+
1056
+ summary = get_report_summary(report_path)
1057
+ write_github_summary(summary)
1058
+
1059
+ if regression_detected:
1060
+ exit_with_error(
1061
+ f"Regression detected between {resolved_test_image} and {resolved_control_image}"
1062
+ )
1063
+ elif both_succeeded:
1064
+ print_success(
1065
+ f"Regression test passed for {resolved_test_image} vs {resolved_control_image}"
1066
+ )
1067
+ else:
1068
+ exit_with_error(
1069
+ f"Both versions failed for {resolved_test_image} vs {resolved_control_image}"
1070
+ )
1170
1071
 
1171
1072
 
1172
1073
  @connector_app.command(name="fetch-connection-config")