airbyte_ops_mcp.cli.gh
CLI commands for GitHub operations.
Commands:
airbyte-ops gh workflow status - Check GitHub Actions workflow status airbyte-ops gh workflow trigger - Trigger a GitHub Actions CI workflow airbyte-ops gh connector info - Get connector metadata from GitHub airbyte-ops gh connector get-version - Get connector version from GitHub airbyte-ops gh connector list - List connectors via GitHub API
CLI reference
The commands below are regenerated by poe docs-generate via cyclopts's
programmatic docs API; see docs/generate_cli.py.
airbyte-ops gh COMMAND
GitHub operations.
Commands:
airbyte-ops gh workflow
GitHub Actions workflow operations.
airbyte-ops gh workflow status
airbyte-ops gh workflow status [ARGS]
Check the status of a GitHub Actions workflow run.
Provide either --url OR all of (--owner, --repo, --run-id).
Parameters:
URL, --url: Full GitHub Actions workflow run URL (e.g., 'https://github.com/owner/repo/actions/runs/12345').OWNER, --owner: Repository owner (e.g., 'airbytehq').REPO, --repo: Repository name (e.g., 'airbyte').RUN-ID, --run-id: Workflow run ID.
airbyte-ops gh workflow trigger
airbyte-ops gh workflow trigger OWNER REPO WORKFLOW-FILE [ARGS]
Trigger a GitHub Actions CI workflow via workflow_dispatch.
This command triggers a workflow in any GitHub repository that has workflow_dispatch enabled. It resolves PR numbers to branch names automatically.
Parameters:
OWNER, --owner: Repository owner (e.g., 'airbytehq'). [required]REPO, --repo: Repository name (e.g., 'airbyte'). [required]WORKFLOW-FILE, --workflow-file: Workflow file name (e.g., 'connector-regression-test.yml'). [required]WORKFLOW-DEFINITION-REF, --workflow-definition-ref: Branch name or PR number for the workflow definition to use. If a PR number is provided, it resolves to the PR's head branch name. Defaults to 'main' if not specified.INPUTS, --inputs: Workflow inputs as a JSON string (e.g., '{"key": "value"}').WAIT, --wait, --no-wait: Wait for the workflow to complete before returning. [default: False]WAIT-SECONDS, --wait-seconds: Maximum seconds to wait for workflow completion (default: 600). [default: 600]
airbyte-ops gh connector
Connector operations via GitHub API.
airbyte-ops gh connector info
airbyte-ops gh connector info NAME REF [ARGS]
Get connector metadata from GitHub.
Parameters:
NAME, --name: Connector technical name (e.g., source-github). [required]REF, --ref: Git ref to read metadata.yaml from. [required]OWNER, --owner: Repository owner (e.g., airbytehq). [default: airbytehq]REPO, --repo: Repository name (e.g., airbyte or airbyte-enterprise). [default: airbyte]GH-TOKEN, --gh-token: GitHub API token. If omitted, usesresolve_default_github_token; public repos can fall back to unauthenticated requests.DPATH, --dpath: Evaluate this dpath expression against the parsed metadata.yaml object and print only that value (e.g., data/dockerImageTag).
airbyte-ops gh connector get-version
airbyte-ops gh connector get-version NAME REF [ARGS]
Get connector version from GitHub.
This is analogous to gh connector info --dpath data/dockerImageTag and uses
the same dpath evaluation internally.
Parameters:
NAME, --name: Connector technical name (e.g., source-github). [required]REF, --ref: Git ref to read metadata.yaml from. [required]OWNER, --owner: Repository owner (e.g., airbytehq). [default: airbytehq]REPO, --repo: Repository name (e.g., airbyte or airbyte-enterprise). [default: airbyte]GH-TOKEN, --gh-token: GitHub API token. If omitted, usesresolve_default_github_token; public repos can fall back to unauthenticated requests.
airbyte-ops gh connector list
airbyte-ops gh connector list [ARGS]
List connector names from GitHub without a local checkout.
Parameters:
MODIFIED-ONLY, --modified-only, --no-modified-only: Include only modified connectors from the provided PR. [default: False]PR, --pr: Pull request number or GitHub URL to inspect for changed connector files.OWNER, --owner: Repository owner (e.g., airbytehq). [default: airbytehq]REPO, --repo: Repository name (e.g., airbyte or airbyte-enterprise). [default: airbyte]GH-TOKEN, --gh-token: GitHub API token. If omitted, usesresolve_default_github_token.OUTPUT-FORMAT, --output-format: Output format: "lines", "csv", "json", or "json-gh-matrix". The GitHub Actions matrix format is {"connector":["source-x"]} and returns {"connector":[""]} when no connectors changed. [choices: lines, csv, json, json-gh-matrix] [default: lines]
1# Copyright (c) 2025 Airbyte, Inc., all rights reserved. 2"""CLI commands for GitHub operations. 3 4Commands: 5 airbyte-ops gh workflow status - Check GitHub Actions workflow status 6 airbyte-ops gh workflow trigger - Trigger a GitHub Actions CI workflow 7 airbyte-ops gh connector info - Get connector metadata from GitHub 8 airbyte-ops gh connector get-version - Get connector version from GitHub 9 airbyte-ops gh connector list - List connectors via GitHub API 10 11## CLI reference 12 13The commands below are regenerated by `poe docs-generate` via cyclopts's 14programmatic docs API; see `docs/generate_cli.py`. 15 16.. include:: ../../../docs/generated/cli/gh.md 17 :start-line: 2 18""" 19 20from __future__ import annotations 21 22# Hide Python-level members from the pdoc page for this module; the rendered 23# docs for this CLI group come entirely from the grafted `.. include::` in 24# the module docstring above. 25__all__: list[str] = [] 26 27import json 28import sys 29from typing import Annotated, Literal 30 31from cyclopts import Parameter 32from fastmcp_extensions.cli import exit_with_error, print_json 33 34from airbyte_ops_mcp.airbyte_repo.list_connectors import ( 35 format_github_actions_connector_matrix, 36 get_modified_connectors_from_github, 37) 38from airbyte_ops_mcp.airbyte_repo.utils import parse_pr_info 39from airbyte_ops_mcp.cli._base import App, app 40from airbyte_ops_mcp.connector_metadata import ( 41 ConnectorMetadataDpathError, 42 ConnectorMetadataDpathNotFoundError, 43 format_metadata_dpath_value, 44 get_connector_version_from_metadata, 45 load_raw_connector_metadata_from_github, 46) 47from airbyte_ops_mcp.github_api import GitHubAPIError, resolve_default_github_token 48from airbyte_ops_mcp.mcp.github_ops import ( 49 check_ci_workflow_status, 50 trigger_ci_workflow, 51) 52 53AIRBYTE_REPO_OWNER = "airbytehq" 54AIRBYTE_REPO_NAME = "airbyte" 55 56# Create the gh sub-app 57gh_app = App(name="gh", help="GitHub operations.") 58app.command(gh_app) 59 60# Create the workflow sub-app under gh 61workflow_app = App(name="workflow", help="GitHub Actions workflow operations.") 62gh_app.command(workflow_app) 63 64# Create the connector sub-app under gh 65connector_app = App(name="connector", help="Connector operations via GitHub API.") 66gh_app.command(connector_app) 67 68 69ConnectorListOutputFormat = Literal["lines", "csv", "json", "json-gh-matrix"] 70 71 72def _parse_pr_details(pr: str) -> tuple[int, str | None, str | None]: 73 """Parse pull request details from a number or GitHub PR URL.""" 74 pr_number, pr_owner, pr_repo = parse_pr_info(pr) 75 if pr_number is None: 76 exit_with_error( 77 "PR must be a pull request number or GitHub URL like " 78 "https://github.com/airbytehq/airbyte/pull/123." 79 ) 80 return pr_number, pr_owner, pr_repo 81 82 83def _print_connector_list( 84 connectors: list[str], 85 output_format: ConnectorListOutputFormat, 86) -> None: 87 """Print connector names in the requested format.""" 88 if output_format == "lines": 89 for connector in connectors: 90 sys.stdout.write(connector + "\n") 91 elif output_format == "csv": 92 sys.stdout.write(",".join(connectors) + "\n") 93 elif output_format == "json": 94 sys.stdout.write(json.dumps(connectors, separators=(",", ":")) + "\n") 95 elif output_format == "json-gh-matrix": 96 sys.stdout.write( 97 json.dumps( 98 format_github_actions_connector_matrix(connectors), 99 separators=(",", ":"), 100 ) 101 + "\n" 102 ) 103 104 105@connector_app.command(name="info") 106def connector_info( 107 name: Annotated[ 108 str, 109 Parameter(help="Connector technical name (e.g., source-github)."), 110 ], 111 ref: Annotated[ 112 str, 113 Parameter(help="Git ref to read metadata.yaml from."), 114 ], 115 owner: Annotated[ 116 str, 117 Parameter(help="Repository owner (e.g., airbytehq)."), 118 ] = AIRBYTE_REPO_OWNER, 119 repo: Annotated[ 120 str, 121 Parameter(help="Repository name (e.g., airbyte or airbyte-enterprise)."), 122 ] = AIRBYTE_REPO_NAME, 123 gh_token: Annotated[ 124 str | None, 125 Parameter( 126 help=( 127 "GitHub API token. If omitted, uses `resolve_default_github_token`; " 128 "public repos can fall back to unauthenticated requests." 129 ) 130 ), 131 ] = None, 132 dpath_expression: Annotated[ 133 str | None, 134 Parameter( 135 name="--dpath", 136 help=( 137 "Evaluate this dpath expression against the parsed metadata.yaml " 138 "object and print only that value (e.g., data/dockerImageTag)." 139 ), 140 ), 141 ] = None, 142) -> None: 143 """Get connector metadata from GitHub.""" 144 try: 145 value = load_raw_connector_metadata_from_github( 146 name, 147 owner=owner, 148 repo=repo, 149 ref=ref, 150 gh_token=gh_token or resolve_default_github_token(allow_none=True), 151 dpath_expression=dpath_expression, 152 ) 153 except ( 154 ConnectorMetadataDpathError, 155 ConnectorMetadataDpathNotFoundError, 156 FileNotFoundError, 157 ValueError, 158 ) as e: 159 exit_with_error(str(e)) 160 161 if dpath_expression is None: 162 print_json(value) 163 return 164 165 sys.stdout.write(format_metadata_dpath_value(value) + "\n") 166 167 168@connector_app.command(name="get-version") 169def connector_get_version( 170 name: Annotated[ 171 str, 172 Parameter(help="Connector technical name (e.g., source-github)."), 173 ], 174 ref: Annotated[ 175 str, 176 Parameter(help="Git ref to read metadata.yaml from."), 177 ], 178 owner: Annotated[ 179 str, 180 Parameter(help="Repository owner (e.g., airbytehq)."), 181 ] = AIRBYTE_REPO_OWNER, 182 repo: Annotated[ 183 str, 184 Parameter(help="Repository name (e.g., airbyte or airbyte-enterprise)."), 185 ] = AIRBYTE_REPO_NAME, 186 gh_token: Annotated[ 187 str | None, 188 Parameter( 189 help=( 190 "GitHub API token. If omitted, uses `resolve_default_github_token`; " 191 "public repos can fall back to unauthenticated requests." 192 ) 193 ), 194 ] = None, 195) -> None: 196 """Get connector version from GitHub. 197 198 This is analogous to `gh connector info --dpath data/dockerImageTag` and uses 199 the same dpath evaluation internally. 200 """ 201 try: 202 metadata = load_raw_connector_metadata_from_github( 203 name, 204 owner=owner, 205 repo=repo, 206 ref=ref, 207 gh_token=gh_token or resolve_default_github_token(allow_none=True), 208 ) 209 version = get_connector_version_from_metadata(metadata) 210 except ( 211 ConnectorMetadataDpathError, 212 ConnectorMetadataDpathNotFoundError, 213 FileNotFoundError, 214 ValueError, 215 ) as e: 216 exit_with_error(str(e)) 217 sys.stdout.write(version + "\n") 218 219 220@connector_app.command(name="list") 221def connector_list( 222 modified_only: Annotated[ 223 bool, 224 Parameter(help="Include only modified connectors from the provided PR."), 225 ] = False, 226 pr: Annotated[ 227 str | None, 228 Parameter( 229 help="Pull request number or GitHub URL to inspect for changed connector files." 230 ), 231 ] = None, 232 owner: Annotated[ 233 str, 234 Parameter(help="Repository owner (e.g., airbytehq)."), 235 ] = AIRBYTE_REPO_OWNER, 236 repo: Annotated[ 237 str, 238 Parameter(help="Repository name (e.g., airbyte or airbyte-enterprise)."), 239 ] = AIRBYTE_REPO_NAME, 240 gh_token: Annotated[ 241 str | None, 242 Parameter( 243 help=("GitHub API token. If omitted, uses `resolve_default_github_token`.") 244 ), 245 ] = None, 246 output_format: Annotated[ 247 ConnectorListOutputFormat, 248 Parameter( 249 help=( 250 'Output format: "lines", "csv", "json", or "json-gh-matrix". ' 251 'The GitHub Actions matrix format is {"connector":["source-x"]} ' 252 'and returns {"connector":[""]} when no connectors changed.' 253 ) 254 ), 255 ] = "lines", 256) -> None: 257 """List connector names from GitHub without a local checkout.""" 258 if not modified_only: 259 exit_with_error("`--modified-only` is required for GitHub connector listing.") 260 if pr is None: 261 exit_with_error("`--pr` is required when `--modified-only` is set.") 262 263 pr_number, pr_owner, pr_repo = _parse_pr_details(pr) 264 try: 265 connectors = get_modified_connectors_from_github( 266 pr_number=pr_number, 267 pr_owner=pr_owner or owner, 268 pr_repo=pr_repo or repo, 269 gh_token=gh_token or resolve_default_github_token(), 270 ) 271 except (GitHubAPIError, ValueError) as e: 272 exit_with_error(str(e)) 273 _print_connector_list(connectors, output_format) 274 275 276@workflow_app.command(name="status") 277def workflow_status( 278 url: Annotated[ 279 str | None, 280 Parameter( 281 help="Full GitHub Actions workflow run URL " 282 "(e.g., 'https://github.com/owner/repo/actions/runs/12345')." 283 ), 284 ] = None, 285 owner: Annotated[ 286 str | None, 287 Parameter(help="Repository owner (e.g., 'airbytehq')."), 288 ] = None, 289 repo: Annotated[ 290 str | None, 291 Parameter(help="Repository name (e.g., 'airbyte')."), 292 ] = None, 293 run_id: Annotated[ 294 int | None, 295 Parameter(help="Workflow run ID."), 296 ] = None, 297) -> None: 298 """Check the status of a GitHub Actions workflow run. 299 300 Provide either --url OR all of (--owner, --repo, --run-id). 301 """ 302 # Validate input parameters 303 if url: 304 if owner or repo or run_id: 305 exit_with_error( 306 "Cannot specify --url together with --owner/--repo/--run-id. " 307 "Use either --url OR the component parts." 308 ) 309 elif not (owner and repo and run_id): 310 exit_with_error( 311 "Must provide either --url OR all of (--owner, --repo, --run-id)." 312 ) 313 314 result = check_ci_workflow_status( 315 workflow_url=url, 316 owner=owner, 317 repo=repo, 318 run_id=run_id, 319 ) 320 print_json(result.model_dump()) 321 322 323@workflow_app.command(name="trigger") 324def workflow_trigger( 325 owner: Annotated[ 326 str, 327 Parameter(help="Repository owner (e.g., 'airbytehq')."), 328 ], 329 repo: Annotated[ 330 str, 331 Parameter(help="Repository name (e.g., 'airbyte')."), 332 ], 333 workflow_file: Annotated[ 334 str, 335 Parameter(help="Workflow file name (e.g., 'connector-regression-test.yml')."), 336 ], 337 workflow_definition_ref: Annotated[ 338 str | None, 339 Parameter( 340 help="Branch name or PR number for the workflow definition to use. " 341 "If a PR number is provided, it resolves to the PR's head branch name. " 342 "Defaults to 'main' if not specified." 343 ), 344 ] = None, 345 inputs: Annotated[ 346 str | None, 347 Parameter( 348 help='Workflow inputs as a JSON string (e.g., \'{"key": "value"}\').' 349 ), 350 ] = None, 351 wait: Annotated[ 352 bool, 353 Parameter(help="Wait for the workflow to complete before returning."), 354 ] = False, 355 wait_seconds: Annotated[ 356 int, 357 Parameter( 358 help="Maximum seconds to wait for workflow completion (default: 600)." 359 ), 360 ] = 600, 361) -> None: 362 """Trigger a GitHub Actions CI workflow via workflow_dispatch. 363 364 This command triggers a workflow in any GitHub repository that has workflow_dispatch 365 enabled. It resolves PR numbers to branch names automatically. 366 """ 367 # Parse inputs JSON if provided 368 parsed_inputs: dict[str, str] | None = None 369 if inputs: 370 try: 371 parsed_inputs = json.loads(inputs) 372 except json.JSONDecodeError as e: 373 exit_with_error(f"Invalid JSON for --inputs: {e}") 374 375 if wait: 376 print( 377 f"Waiting for workflow to complete (timeout: {wait_seconds}s)...", 378 file=sys.stderr, 379 ) 380 381 result = trigger_ci_workflow( 382 owner=owner, 383 repo=repo, 384 workflow_file=workflow_file, 385 workflow_definition_ref=workflow_definition_ref, 386 inputs=parsed_inputs, 387 wait_for_completion=wait, 388 max_wait_seconds=wait_seconds, 389 ) 390 391 print_json(result.model_dump()) 392 if not result.success: 393 sys.exit(1)