sync: Add GetApprovedManifestV2 with named request options

Smart sync calls GetApprovedManifest(branch[, target]) over XML-RPC,
which only supports positional arguments.  Adding new request options
that way does not scale.

Add a GetApprovedManifestV2 method that takes a single struct of named
options and returns a struct:

  request:  {branch, target?, best_effort?}
  response: {success, manifest_xml?, error_message?}

Servers must ignore unknown options, so new options (e.g. criteria for
selecting a good build) can be added without another method.

The first new option is `best_effort`.  repo sets it when smart sync was
enabled implicitly by the manifest's sync-smartsync="true" default
rather than by -s.  The server may decline a best_effort request (e.g.
for rollout or cohort gating) by returning success=false.  The client
still owns the fallback policy: declined implicit requests fall back to
a regular sync, while explicit -s requests fail as before.

If the server does not implement GetApprovedManifestV2, repo falls back
to the legacy GetApprovedManifest call.

Bug: 545137090
Test: ./run_tests
Change-Id: I9297b728df26516012cb93dffc91d39c1fb0458f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/636623
Reviewed-by: Mike Frysinger <vapier@google.com>
Commit-Queue: Rahul Yadav <yadavrah@google.com>
Tested-by: Rahul Yadav <yadavrah@google.com>
This commit is contained in:
Rahul Yadav authored and gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-10-14 03:30:36 -07:00
1 parent a110680ff3
commit 3448df3954
3 files changed
+274 -29

No files matched your search

+71 -7
View File
@@ -98,6 +98,69 @@ _BLOAT_SIZE_GARBAGE_THRESHOLD_KB = 1 * 1024 * 1024 # 1 GiB in KiB
logger = RepoLogger(__file__)
# Manifest server RPC that takes a single struct of named options. Servers
# that do not implement it are called via the positional GetApprovedManifest.
_GET_APPROVED_MANIFEST_V2 = "GetApprovedManifestV2"
def _IsMethodNotFoundFault(fault: xmlrpc.client.Fault, method: str) -> bool:
"""Whether |fault| means the server does not implement |method|."""
if fault.faultCode == xmlrpc.client.METHOD_NOT_FOUND:
return True
# Python's SimpleXMLRPCDispatcher uses faultCode 1 with this message.
return f'method "{method}" is not supported' in str(fault.faultString)
def _GetApprovedManifest(
server: xmlrpc.client.ServerProxy,
branch: str,
target: Optional[str],
best_effort: bool,
) -> Tuple[bool, str]:
"""Requests the approved manifest from the manifest server.
Args:
server: The manifest server proxy.
branch: The manifest branch.
target: The build target, if any.
best_effort: The client can proceed without a manifest (e.g. by
falling back to ToT), so the server may decline the request.
Returns:
A (success, manifest XML or error message) tuple.
"""
request = {"branch": branch}
if target:
request["target"] = target
if best_effort:
request["best_effort"] = True
try:
response = server.GetApprovedManifestV2(request)
except xmlrpc.client.Fault as e:
if not _IsMethodNotFoundFault(e, _GET_APPROVED_MANIFEST_V2):
raise
# Older server: |best_effort| has no equivalent and is dropped.
if target:
success, manifest_str = server.GetApprovedManifest(branch, target)
else:
success, manifest_str = server.GetApprovedManifest(branch)
return success, manifest_str
if not isinstance(response, dict):
raise SmartSyncError(
f"error: invalid {_GET_APPROVED_MANIFEST_V2} response: {response!r}"
)
if response.get("success"):
manifest_xml = response.get("manifest_xml")
if not isinstance(manifest_xml, str):
raise SmartSyncError(
f"error: {_GET_APPROVED_MANIFEST_V2} response has invalid "
f"manifest_xml: {manifest_xml!r}"
)
return True, manifest_xml
return False, response.get("error_message", "unknown error")
def _SafeCheckoutOrder(checkouts: List[Project]) -> List[List[Project]]:
"""Generate a sequence of checkouts that is safe to perform.
@@ -2094,6 +2157,7 @@ later is required to fix a server side protocol bug.
opt: optparse.Values,
smart_sync_manifest_path: str,
manifest: XmlManifest,
best_effort: bool = False,
) -> str:
if not manifest.manifest_server:
raise SmartSyncError(
@@ -2140,12 +2204,9 @@ later is required to fix a server side protocol bug.
os.environ["TARGET_BUILD_VARIANT"],
)
if target:
[success, manifest_str] = server.GetApprovedManifest(
branch, target
)
else:
[success, manifest_str] = server.GetApprovedManifest(branch)
success, manifest_str = _GetApprovedManifest(
server, branch, target, best_effort
)
else:
assert opt.smart_tag
[success, manifest_str] = server.GetManifest(opt.smart_tag)
@@ -2519,7 +2580,10 @@ later is required to fix a server side protocol bug.
if opt.smart_sync or opt.smart_tag:
try:
manifest_name = self._SmartSyncSetup(
opt, smart_sync_manifest_path, manifest
opt,
smart_sync_manifest_path,
manifest,
best_effort=implicit_smart_sync,
)
except (SmartSyncError, ManifestParseError) as e:
if not implicit_smart_sync: