Publish curated ITWorx_MCP_Hub source from 7fde2770ba344c684b7e7a6b74f763dc19e7c560

This commit is contained in:
Public Source Publisher committed 2026-10-14 00:05:58 +00:00
1 parent 18cf749bf5
commit 89703431af
11 files changed
+1247 -114

No files matched your search

+99 -16
View File
@@ -3,11 +3,13 @@
from __future__ import annotations
import base64
import copy
import ipaddress
import json
import re
import time
from collections.abc import Mapping
from datetime import UTC, datetime
from pathlib import Path
from threading import RLock
from typing import Any, Protocol, cast
@@ -331,6 +333,13 @@ class GiteaClient:
"""Small bounded Gitea API client with same-origin redirect enforcement."""
_path_part = re.compile(r"^[^/\\?#\x00-\x1f\x7f]+$")
_semver = re.compile(
r"v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"
r"(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?"
r"(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?"
)
_version_cache_seconds = 30
_supported_ranges = (">=1.14.0 <1.15.0", ">=1.25.0 <1.28.0", ">=28.0.0 <29.0.0")
def __init__(
self,
@@ -347,7 +356,8 @@ class GiteaClient:
follow_redirects=False,
transport=transport,
)
self._merge_strategy_key: str | None = None
self._version_lock = RLock()
self._version_cache: tuple[float, dict[str, Any]] | None = None
def close(self) -> None:
self._client.close()
@@ -813,27 +823,100 @@ class GiteaClient:
),
)
def _merge_strategy_field(self) -> str:
"""Resolve the versioned Gitea merge field before the single write attempt."""
def upstream_compatibility(self, *, force_refresh: bool = False) -> dict[str, Any]:
"""Read the upstream version, independent of this connector's build version.
if self._merge_strategy_key is not None:
return self._merge_strategy_key
payload = self._request(self._path("version"))
version = payload.get("version") if isinstance(payload, dict) else None
legacy_match = re.fullmatch(r"1\.(14|25|26|27)(?:\.\d+)?(?:[-+].*)?", str(version or ""))
modern_match = re.fullmatch(r"(28)(?:\.\d+){1,2}(?:[-+].*)?", str(version or ""))
if legacy_match is None and modern_match is None:
Diagnostics cache for 30 seconds; each merge negotiates again immediately
before its one write. Failed probes replace cached success and never fall
back to the connector version. Gitea 28 drops the historical ``1.`` prefix.
"""
with self._version_lock:
now = time.monotonic()
if (
not force_refresh
and self._version_cache is not None
and now - self._version_cache[0] < self._version_cache_seconds
):
return copy.deepcopy(self._version_cache[1])
diagnostics: dict[str, Any] = {
"source": "/api/v1/version",
"upstreamVersion": None,
"normalizedVersion": None,
"compatibility": "UNKNOWN",
"reason": "upstream_version_missing_or_malformed",
"mergeStrategyField": None,
"supportedRanges": list(self._supported_ranges),
"prereleasesSupported": False,
"checkedAt": datetime.now(UTC).isoformat(),
"cacheSeconds": self._version_cache_seconds,
"errorCode": None,
}
try:
payload = self._request(self._path("version"))
except GiteaError as exc:
diagnostics.update(
compatibility="UNAVAILABLE",
reason="upstream_version_probe_failed",
errorCode=exc.code,
)
else:
version = payload.get("version") if isinstance(payload, dict) else None
# Do not reflect arbitrary upstream data (or secret-like content).
if isinstance(version, str) and len(version) <= 128:
version = version.strip()
match = self._semver.fullmatch(version)
if match is not None:
release = tuple(int(match.group(index)) for index in (1, 2, 3))
prerelease = match.group(4)
invalid_prerelease = prerelease is not None and any(
part.isdigit() and len(part) > 1 and part.startswith("0")
for part in prerelease.split(".")
)
if not invalid_prerelease:
# Arbitrary SemVer suffixes can contain opaque credentials.
# Display only the numeric release; negotiate the full parsed version.
display_version = ("v" if version.startswith("v") else "") + ".".join(
str(part) for part in release
)
diagnostics.update(
upstreamVersion=display_version,
normalizedVersion=display_version.removeprefix("v"),
compatibility="UNSUPPORTED",
reason="upstream_release_outside_tested_ranges",
)
supported = (
(1, 14, 0) <= release < (1, 15, 0)
or (1, 25, 0) <= release < (1, 28, 0)
or (28, 0, 0) <= release < (29, 0, 0)
)
if prerelease is not None:
diagnostics["reason"] = "upstream_prerelease_not_tested"
elif supported:
diagnostics.update(
compatibility="SUPPORTED",
reason="upstream_release_in_tested_ranges",
mergeStrategyField=("Do" if release < (1, 26, 0) else "do"),
)
self._version_cache = (time.monotonic(), copy.deepcopy(diagnostics))
return diagnostics
def _merge_strategy_field(self) -> str:
"""Negotiate against the current upstream immediately before the merge write."""
diagnostics = self.upstream_compatibility(force_refresh=True)
if diagnostics["compatibility"] != "SUPPORTED":
raise GiteaError(
"GITEA_VERSION_UNSUPPORTED",
diagnostics["errorCode"] or "GITEA_VERSION_UNSUPPORTED",
(
"Gitea merge compatibility requires an identified "
"1.14, 1.25, 1.26, 1.27, or 28.x server"
"1.14, 1.25, 1.26, 1.27, or 28.x stable server; "
f"{diagnostics['reason']}"
),
category="compatibility",
expected_state="Stable upstream release in tested compatibility ranges",
actual_state=str(diagnostics["compatibility"]),
recovery_hint="Inspect gitea.capabilities.get upstreamGitea diagnostics",
)
self._merge_strategy_key = (
"Do" if legacy_match is not None and legacy_match.group(1) in {"14", "25"} else "do"
)
return self._merge_strategy_key
return str(diagnostics["mergeStrategyField"])
def update_pull_request_branch(
self,
+179 -4
View File
@@ -1,9 +1,9 @@
"""Compatibility hardening for Gitea validation discovery and request latency.
Some Gitea versions expose pull-request check results as commit statuses while
returning an empty branch-filtered Actions run wrapper. The base client treats
collections strictly; this subclass preserves that strictness while translating
a fully-green exact-head status set into one synthetic completed validation run.
Native Gitea PR runs can omit their head branch. This client reads actual
exact-head runs without a branch filter and checks available PR identity.
Older status-only adapters retain an explicitly labeled legacy fallback;
exact-head native reads never synthesize a workflow run or merge proof.
Large repositories can require longer than the historical 15-second client
budget to commit files, apply changesets, or materialize a server-side squash
@@ -15,6 +15,8 @@ base client and are not duplicated here.
from __future__ import annotations
import re
import time
from typing import Any
import httpx
@@ -40,6 +42,174 @@ class GiteaClient(BaseGiteaClient):
)
super().__init__(resilient_settings, token_provider, transport=transport)
@staticmethod
def matches_exact_head_action_run(
run: dict[str, Any],
*,
branch: str,
head_sha: str,
pull_request_number: int | None = None,
) -> bool:
if run.get("head_sha") != head_sha:
return False
head_branch = run.get("head_branch", "")
event = run.get("event")
if head_branch is None and event == "pull_request":
head_branch = ""
if event == "workflow_dispatch" and head_branch == f"refs/heads/{branch}":
head_branch = branch
if not isinstance(head_branch, str) or head_branch not in {"", branch}:
return False
if not head_branch and event != "pull_request":
return False
path = run.get("path")
path_number: int | None = None
if path is not None:
if not isinstance(path, str) or len(path) > 512 or path.count("@") > 1:
return False
if "@" in path:
ref = path.split("@", 1)[1]
match = re.fullmatch(r"refs/pull/([1-9][0-9]*)/head", ref)
if match:
path_number = int(match[1])
if event != "pull_request" or path_number > 2_147_483_647:
return False
elif ref != f"refs/heads/{branch}" or event == "pull_request":
return False
raw_references = run.get("pull_requests")
references = [] if raw_references is None else raw_references
if not isinstance(references, list):
return False
numbers: list[int] = []
for reference in references:
if not isinstance(reference, dict):
return False
number = reference.get("number")
if number is not None:
if type(number) is not int or not 1 <= number <= 2_147_483_647:
return False
numbers.append(number)
head = reference.get("head")
if head is not None:
if not isinstance(head, dict):
return False
if head.get("ref") not in (None, "", branch) or head.get("sha") not in (
None,
"",
head_sha,
):
return False
if event == "pull_request" and pull_request_number is not None:
if path_number not in {None, pull_request_number} or any(
number != pull_request_number for number in numbers
):
return False
# Legacy adapters identify PR runs by a real branch and SHA. Native
# blank-branch runs need affirmative PR number/ref evidence.
if path_number != pull_request_number and pull_request_number not in numbers:
return head_branch == branch and not path
return True
def exact_head_action_runs(
self,
repository: RegisteredRepository,
*,
branch: str,
head_sha: str,
pull_request_number: int | None = None,
) -> list[dict[str, Any]]:
"""Read bounded actual run evidence; never synthesize a native validation."""
if not re.fullmatch(r"(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})", head_sha):
raise GiteaError("GITEA_TARGET_INVALID", "exact Actions head SHA is invalid")
if pull_request_number is not None and (
type(pull_request_number) is not int or not 1 <= pull_request_number <= 2_147_483_647
):
raise GiteaError("GITEA_TARGET_INVALID", "exact Actions PR number is invalid")
output: list[dict[str, Any]] = []
seen: set[tuple[int, int]] = set()
expected_count: int | None = None
deadline = time.monotonic() + 20
for page in range(1, 11):
if time.monotonic() >= deadline:
raise GiteaError(
"GITEA_ACTION_RUNS_TRUNCATED", "exact Actions read exceeded its bound"
)
payload = self._request(
self._path("repos", repository.owner, repository.name, "actions", "runs"),
{"head_sha": head_sha, "page": page, "limit": 100},
timeout_seconds=min(10.0, max(0.1, deadline - time.monotonic())),
max_attempts=1,
)
count = payload.get("total_count") if isinstance(payload, dict) else None
if isinstance(payload, dict) and "total_count" in payload:
if type(count) is not int or not 0 <= count <= 1000:
raise GiteaError("GITEA_ACTION_RUNS_INVALID", "invalid exact Actions count")
if expected_count is not None and count != expected_count:
raise GiteaError("GITEA_ACTION_RUNS_TRUNCATED", "exact Actions count changed")
expected_count = count
elif expected_count is not None:
raise GiteaError("GITEA_ACTION_RUNS_TRUNCATED", "exact Actions count disappeared")
if isinstance(payload, dict):
payload = (
payload.get("workflow_runs")
if "workflow_runs" in payload
else payload.get("runs")
)
runs = self._as_list(payload)
if len(runs) > 100:
raise GiteaError("GITEA_ACTION_RUNS_INVALID", "excess exact Actions page")
for run in runs:
identifier, attempt = run.get("id"), run.get("run_attempt", 0)
number = run.get("run_number", identifier)
if (
type(identifier) is not int
or identifier < 1
or type(attempt) is not int
or attempt < 0
or ("run_attempt" in run and attempt == 0)
or type(number) is not int
or number < 1
or (identifier, attempt) in seen
):
raise GiteaError(
"GITEA_ACTION_RUNS_INVALID", "invalid or repeated exact Actions run"
)
seen.add((identifier, attempt))
output.append(run)
if expected_count is not None:
if len(output) > expected_count:
raise GiteaError("GITEA_ACTION_RUNS_INVALID", "excess exact Actions runs")
complete = len(output) == expected_count
else:
complete = len(runs) < 100
if complete:
if time.monotonic() >= deadline:
raise GiteaError(
"GITEA_ACTION_RUNS_TRUNCATED", "exact Actions read exceeded its bound"
)
matching = [
run
for run in output
if self.matches_exact_head_action_run(
run,
branch=branch,
head_sha=head_sha,
pull_request_number=pull_request_number,
)
]
return sorted(
matching,
key=lambda run: (
run.get("run_number", run["id"]),
run.get("run_attempt", 0),
run["id"],
),
reverse=True,
)
if not runs:
raise GiteaError("GITEA_ACTION_RUNS_TRUNCATED", "incomplete exact Actions page")
raise GiteaError("GITEA_ACTION_RUNS_TRUNCATED", "exact Actions pages exceeded their bound")
def action_runs(
self,
repository: RegisteredRepository,
@@ -78,6 +248,11 @@ class GiteaClient(BaseGiteaClient):
):
return []
native_runs = self.exact_head_action_runs(repository, branch=branch, head_sha=head_sha)
if native_runs:
offset = (page - 1) * limit
return native_runs[offset : offset + limit]
statuses = self.commit_statuses(repository, head_sha, 1, 100)
if not statuses:
return []