airbyte-internal-ops 0.2.2__py3-none-any.whl → 0.2.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airbyte-internal-ops
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: MCP and API interfaces that let the agents do the admin work
5
5
  Author-email: Aaron Steers <aj@airbyte.io>
6
6
  Keywords: admin,airbyte,api,mcp
@@ -2,7 +2,7 @@ airbyte_ops_mcp/__init__.py,sha256=tuzdlMkfnWBnsri5KGHM2M_xuNnzFk2u_aR79mmN7Yg,7
2
2
  airbyte_ops_mcp/_annotations.py,sha256=MO-SBDnbykxxHDESG7d8rviZZ4WlZgJKv0a8eBqcEzQ,1757
3
3
  airbyte_ops_mcp/constants.py,sha256=GeZ2_WWluMSrGkyqGvqUVFCy-5PD-lyzZbQ7eO-vyUo,5192
4
4
  airbyte_ops_mcp/gcp_auth.py,sha256=5k-k145ZoYhHLjyDES8nrA8f8BBihRI0ykrdD1IcfOs,3599
5
- airbyte_ops_mcp/github_actions.py,sha256=hcwwew98r0yetWsM7Qmdar3ATLBJQGIn3fJfJ_n59So,8599
5
+ airbyte_ops_mcp/github_actions.py,sha256=wKnuIVmF4u1gMYNdSoryD_PUmvMz5SaHgOvbU0dsolA,9957
6
6
  airbyte_ops_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  airbyte_ops_mcp/_legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  airbyte_ops_mcp/_legacy/airbyte_ci/README.md,sha256=qEYx4geDR8AEDjrcA303h7Nol-CMDLojxUyiGzQprM8,236
@@ -354,7 +354,7 @@ airbyte_ops_mcp/cli/_shared.py,sha256=jg-xMyGzTCGPqKd8VTfE_3kGPIyO_3Kx5sQbG4rPc0
354
354
  airbyte_ops_mcp/cli/app.py,sha256=SEdBpqFUG2O8zGV5ifwptxrLGFph_dLr66-MX9d69gQ,789
355
355
  airbyte_ops_mcp/cli/cloud.py,sha256=JJusGl67ca41rvoS8BPl5Kmb7Kyu7iMH-tvbaJsKsPs,41359
356
356
  airbyte_ops_mcp/cli/gh.py,sha256=91b1AxFXvHQCFyXhrrym-756ZjnMCqvxFdmwCtma1zI,2046
357
- airbyte_ops_mcp/cli/registry.py,sha256=-yiLJWSslV_qGi6ImXZYfXOJSE4oJBO7yICkyA_RiUo,5792
357
+ airbyte_ops_mcp/cli/registry.py,sha256=tcf_CDiUVJpSdBRNqlEL3zFKMqK53AhFpJjAETM4gLs,9781
358
358
  airbyte_ops_mcp/cli/repo.py,sha256=G1hoQpH0XYhUH3FFOsia9xabGB0LP9o3XcwBuqvFVo0,16331
359
359
  airbyte_ops_mcp/cloud_admin/__init__.py,sha256=cqE96Q10Kp6elhH9DAi6TVsIwSUy3sooDLLrxTaktGk,816
360
360
  airbyte_ops_mcp/cloud_admin/api_client.py,sha256=tx1kwGIKMPesibflQkFOlbNp0t0CfJD4Ab097ngsjHA,19126
@@ -375,7 +375,7 @@ airbyte_ops_mcp/mcp/connector_qa.py,sha256=aImpqdnqBPDrz10BS0owsV4kuIU2XdalzgbaG
375
375
  airbyte_ops_mcp/mcp/github.py,sha256=h3M3VJrq09y_F9ueQVCq3bUbVBNFuTNKprHtGU_ttio,8045
376
376
  airbyte_ops_mcp/mcp/github_repo_ops.py,sha256=PiERpt8abo20Gz4CfXhrDNlVM4o4FOt5sweZJND2a0s,5314
377
377
  airbyte_ops_mcp/mcp/metadata.py,sha256=fwGW97WknR5lfKcQnFtK6dU87aA6TmLj1NkKyqDAV9g,270
378
- airbyte_ops_mcp/mcp/prerelease.py,sha256=6G4zMo0KeCIYJPEIryHKHoZUiBHQMagPJU-uw-IzK94,8939
378
+ airbyte_ops_mcp/mcp/prerelease.py,sha256=nc6VU03ADVHWM3OjGKxbS5XqY4VoyRyrZNU_fyAtaOI,10465
379
379
  airbyte_ops_mcp/mcp/prod_db_queries.py,sha256=FfGoq3aEj6ZUT4ysBIs1w7LzzwBeRXTaRvPGEx62RzI,25474
380
380
  airbyte_ops_mcp/mcp/prompts.py,sha256=mJld9mdPECXYZffWXGSvNs4Xevx3rxqUGNlzGKVC2_s,1599
381
381
  airbyte_ops_mcp/mcp/registry.py,sha256=PW-VYUj42qx2pQ_apUkVaoUFq7VgB9zEU7-aGrkSCCw,290
@@ -410,7 +410,7 @@ airbyte_ops_mcp/regression_tests/regression/comparators.py,sha256=MJkLZEKHivgrG0
410
410
  airbyte_ops_mcp/regression_tests/validation/__init__.py,sha256=MBEwGOoNuqT4_oCahtoK62OKWIjUCfWa7vZTxNj_0Ek,1532
411
411
  airbyte_ops_mcp/regression_tests/validation/catalog_validators.py,sha256=jqqVAMOk0mtdPgwu4d0hA0ZEjtsNh5gapvGydRv3_qk,12553
412
412
  airbyte_ops_mcp/regression_tests/validation/record_validators.py,sha256=RjauAhKWNwxMBTu0eNS2hMFNQVs5CLbQU51kp6FOVDk,7432
413
- airbyte_internal_ops-0.2.2.dist-info/METADATA,sha256=1ah9ZGR3rZ1676mhAJzJvrbfqOQsV_fuedRitpJG9h8,5679
414
- airbyte_internal_ops-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
415
- airbyte_internal_ops-0.2.2.dist-info/entry_points.txt,sha256=WxP0l7bRFss4Cr5uQqVj9mTEKwnRKouNuphXQF0lotA,171
416
- airbyte_internal_ops-0.2.2.dist-info/RECORD,,
413
+ airbyte_internal_ops-0.2.3.dist-info/METADATA,sha256=c-kIcUr44n9G4r4yImy7oEQuSrLRueORjI4SeW-2ZNI,5679
414
+ airbyte_internal_ops-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
415
+ airbyte_internal_ops-0.2.3.dist-info/entry_points.txt,sha256=WxP0l7bRFss4Cr5uQqVj9mTEKwnRKouNuphXQF0lotA,171
416
+ airbyte_internal_ops-0.2.3.dist-info/RECORD,,
@@ -5,6 +5,7 @@ This module provides CLI wrappers for registry operations. The core logic
5
5
  lives in the `airbyte_ops_mcp.registry` capability module.
6
6
 
7
7
  Commands:
8
+ airbyte-ops registry connector compute-prerelease-tag - Compute prerelease version tag
8
9
  airbyte-ops registry connector publish-prerelease - Publish connector prerelease
9
10
  airbyte-ops registry connector publish - Publish connector (apply/rollback version override)
10
11
  airbyte-ops registry image inspect - Inspect Docker image on DockerHub
@@ -12,9 +13,12 @@ Commands:
12
13
 
13
14
  from __future__ import annotations
14
15
 
16
+ import contextlib
17
+ import sys
15
18
  from pathlib import Path
16
19
  from typing import Annotated
17
20
 
21
+ import yaml
18
22
  from cyclopts import App, Parameter
19
23
 
20
24
  from airbyte_ops_mcp.cli._base import app
@@ -24,8 +28,15 @@ from airbyte_ops_mcp.cli._shared import (
24
28
  print_json,
25
29
  print_success,
26
30
  )
31
+ from airbyte_ops_mcp.github_actions import (
32
+ get_file_contents_at_ref,
33
+ resolve_github_token,
34
+ )
27
35
  from airbyte_ops_mcp.mcp.github import get_docker_image_info
28
- from airbyte_ops_mcp.mcp.prerelease import publish_connector_to_airbyte_registry
36
+ from airbyte_ops_mcp.mcp.prerelease import (
37
+ compute_prerelease_docker_image_tag,
38
+ publish_connector_to_airbyte_registry,
39
+ )
29
40
  from airbyte_ops_mcp.registry import (
30
41
  ConnectorPublishResult,
31
42
  PublishAction,
@@ -47,6 +58,111 @@ image_app = App(name="image", help="Docker image operations.")
47
58
  registry_app.command(image_app)
48
59
 
49
60
 
61
+ AIRBYTE_REPO_OWNER = "airbytehq"
62
+ AIRBYTE_REPO_NAME = "airbyte"
63
+ CONNECTOR_PATH_PREFIX = "airbyte-integrations/connectors"
64
+
65
+
66
+ def _get_connector_version_from_github(
67
+ connector_name: str,
68
+ ref: str,
69
+ token: str | None = None,
70
+ ) -> str | None:
71
+ """Fetch connector version from metadata.yaml via GitHub API.
72
+
73
+ Args:
74
+ connector_name: Connector name (e.g., "source-github")
75
+ ref: Git ref (commit SHA, branch name, or tag)
76
+ token: GitHub API token (optional for public repos)
77
+
78
+ Returns:
79
+ Version string from metadata.yaml, or None if not found.
80
+ """
81
+ path = f"{CONNECTOR_PATH_PREFIX}/{connector_name}/metadata.yaml"
82
+ contents = get_file_contents_at_ref(
83
+ owner=AIRBYTE_REPO_OWNER,
84
+ repo=AIRBYTE_REPO_NAME,
85
+ path=path,
86
+ ref=ref,
87
+ token=token,
88
+ )
89
+ if contents is None:
90
+ return None
91
+
92
+ metadata = yaml.safe_load(contents)
93
+ return metadata.get("data", {}).get("dockerImageTag")
94
+
95
+
96
+ @connector_app.command(name="compute-prerelease-tag")
97
+ def compute_prerelease_tag(
98
+ connector_name: Annotated[
99
+ str,
100
+ Parameter(help="Connector name (e.g., 'source-github')."),
101
+ ],
102
+ sha: Annotated[
103
+ str,
104
+ Parameter(help="Git commit SHA (full or at least 7 characters)."),
105
+ ],
106
+ base_version: Annotated[
107
+ str | None,
108
+ Parameter(
109
+ help="Base version override. If not provided, fetched from metadata.yaml at the given SHA."
110
+ ),
111
+ ] = None,
112
+ ) -> None:
113
+ """Compute the pre-release docker image tag.
114
+
115
+ Outputs the version tag to stdout for easy capture in shell scripts.
116
+ This is the single source of truth for pre-release version format.
117
+
118
+ The command fetches the connector's metadata.yaml from GitHub at the given SHA
119
+ to determine the base version. It also compares against the master branch and
120
+ prints a warning to stderr if no version bump is detected.
121
+
122
+ If --base-version is provided, it is used directly instead of fetching from GitHub.
123
+
124
+ Example:
125
+ airbyte-ops registry connector compute-prerelease-tag --connector-name source-github --sha abcdef1234567
126
+ # Output: 1.2.3-preview.abcdef1
127
+
128
+ airbyte-ops registry connector compute-prerelease-tag --connector-name source-github --sha abcdef1234567 --base-version 1.2.3
129
+ # Output: 1.2.3-preview.abcdef1 (uses provided version, skips GitHub API)
130
+ """
131
+ # Try to get a GitHub token (optional, but helps avoid rate limiting)
132
+ # Token resolution may fail if no token is configured, which is fine for public repos
133
+ token: str | None = None
134
+ with contextlib.suppress(ValueError):
135
+ token = resolve_github_token()
136
+
137
+ # Determine base version
138
+ version: str
139
+ if base_version:
140
+ version = base_version
141
+ else:
142
+ # Fetch version from metadata.yaml at the given SHA
143
+ fetched_version = _get_connector_version_from_github(connector_name, sha, token)
144
+ if fetched_version is None:
145
+ print(
146
+ f"Error: Could not fetch metadata.yaml for {connector_name} at ref {sha}",
147
+ file=sys.stderr,
148
+ )
149
+ sys.exit(1)
150
+ version = fetched_version
151
+
152
+ # Compare with master branch version and warn if no bump detected
153
+ master_version = _get_connector_version_from_github(connector_name, "master", token)
154
+ if master_version and master_version == version:
155
+ print(
156
+ f"Warning: No version bump detected for {connector_name}. "
157
+ f"Version {version} matches master branch.",
158
+ file=sys.stderr,
159
+ )
160
+
161
+ # Compute and output the prerelease tag
162
+ tag = compute_prerelease_docker_image_tag(version, sha)
163
+ print(tag)
164
+
165
+
50
166
  @connector_app.command(name="publish-prerelease")
51
167
  def publish_prerelease(
52
168
  connector_name: Annotated[
@@ -106,6 +106,51 @@ class WorkflowJobInfo:
106
106
  """ISO 8601 timestamp when the job completed"""
107
107
 
108
108
 
109
+ def get_file_contents_at_ref(
110
+ owner: str,
111
+ repo: str,
112
+ path: str,
113
+ ref: str,
114
+ token: str | None = None,
115
+ ) -> str | None:
116
+ """Fetch file contents from GitHub at a specific ref.
117
+
118
+ Uses the GitHub Contents API to retrieve file contents at a specific
119
+ commit SHA, branch, or tag. This allows reading files without having
120
+ the repository checked out locally.
121
+
122
+ Args:
123
+ owner: Repository owner (e.g., "airbytehq")
124
+ repo: Repository name (e.g., "airbyte")
125
+ path: Path to the file within the repository
126
+ ref: Git ref (commit SHA, branch name, or tag)
127
+ token: GitHub API token (optional for public repos, but recommended
128
+ to avoid rate limiting)
129
+
130
+ Returns:
131
+ File contents as a string, or None if the file doesn't exist.
132
+
133
+ Raises:
134
+ requests.HTTPError: If API request fails (except 404).
135
+ """
136
+ url = f"{GITHUB_API_BASE}/repos/{owner}/{repo}/contents/{path}"
137
+ headers = {
138
+ "Accept": "application/vnd.github.raw+json",
139
+ "X-GitHub-Api-Version": "2022-11-28",
140
+ }
141
+ if token:
142
+ headers["Authorization"] = f"Bearer {token}"
143
+
144
+ params = {"ref": ref}
145
+
146
+ response = requests.get(url, headers=headers, params=params, timeout=30)
147
+ if response.status_code == 404:
148
+ return None
149
+ response.raise_for_status()
150
+
151
+ return response.text
152
+
153
+
109
154
  def get_workflow_jobs(
110
155
  owner: str,
111
156
  repo: str,
@@ -31,6 +31,45 @@ PRERELEASE_TOKEN_ENV_VARS = [
31
31
  "GITHUB_TOKEN",
32
32
  ]
33
33
 
34
+ # =============================================================================
35
+ # Pre-release Version Tag Constants
36
+ # =============================================================================
37
+
38
+ PRERELEASE_TAG_PREFIX = "preview"
39
+ """The prefix used for pre-release version tags (e.g., '1.2.3-preview.abcde12')."""
40
+
41
+ PRERELEASE_SHA_LENGTH = 7
42
+ """The number of characters from the git SHA to include in pre-release tags."""
43
+
44
+
45
+ def compute_prerelease_docker_image_tag(base_version: str, sha: str) -> str:
46
+ """Compute the pre-release docker image tag.
47
+
48
+ This is the SINGLE SOURCE OF TRUTH for pre-release version format.
49
+ All other code should receive this value as a parameter, not recompute it.
50
+
51
+ The format is: {base_version}-preview.{short_sha}
52
+
53
+ Where:
54
+ - base_version: The base version from metadata.yaml (e.g., "1.2.3")
55
+ - short_sha: The first 7 characters of the git commit SHA
56
+
57
+ Examples:
58
+ >>> compute_prerelease_docker_image_tag("1.2.3", "abcdef1234567890")
59
+ '1.2.3-preview.abcdef1'
60
+ >>> compute_prerelease_docker_image_tag("0.1.0", "1234567")
61
+ '0.1.0-preview.1234567'
62
+
63
+ Args:
64
+ base_version: The base version from metadata.yaml (e.g., "1.2.3")
65
+ sha: The full git commit SHA (or at least 7 characters)
66
+
67
+ Returns:
68
+ Pre-release version tag (e.g., "1.2.3-preview.abcde12")
69
+ """
70
+ short_sha = sha[:PRERELEASE_SHA_LENGTH]
71
+ return f"{base_version}-{PRERELEASE_TAG_PREFIX}.{short_sha}"
72
+
34
73
 
35
74
  class PRHeadInfo(BaseModel):
36
75
  """Information about a PR's head commit."""
@@ -268,7 +307,9 @@ def publish_connector_to_airbyte_registry(
268
307
  docker_image = data.get("dockerRepository")
269
308
  base_version = data.get("dockerImageTag")
270
309
  if base_version:
271
- docker_image_tag = f"{base_version}-preview.{head_info.short_sha}"
310
+ docker_image_tag = compute_prerelease_docker_image_tag(
311
+ base_version, head_info.sha
312
+ )
272
313
 
273
314
  return PrereleaseWorkflowResult(
274
315
  success=True,