summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
authorRahul Yadav <yadavrah@google.com>2026-10-01 16:34:47 +0000
committergerrit-scoped@luci-project-accounts.iam.gserviceaccount.com <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2026-10-14 03:30:36 -0700
commit3448df395478c2375d880e33bfc705a9ed36272b (patch)
tree4529203b698304d03297b800e28f0bf92436e4c4 /subcmds/sync.py
parenta110680ff3e0d3e8504edf4457dff759801ad011 (diff)
downloadgit-repo-3448df395478c2375d880e33bfc705a9ed36272b.tar.gz
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>
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py78
1 files changed, 71 insertions, 7 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 8d4896d0f..e83f42345 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -98,6 +98,69 @@ _BLOAT_SIZE_GARBAGE_THRESHOLD_KB = 1 * 1024 * 1024 # 1 GiB in KiB
98 98
99logger = RepoLogger(__file__) 99logger = RepoLogger(__file__)
100 100
101# Manifest server RPC that takes a single struct of named options. Servers
102# that do not implement it are called via the positional GetApprovedManifest.
103_GET_APPROVED_MANIFEST_V2 = "GetApprovedManifestV2"
104
105
106def _IsMethodNotFoundFault(fault: xmlrpc.client.Fault, method: str) -> bool:
107 """Whether |fault| means the server does not implement |method|."""
108 if fault.faultCode == xmlrpc.client.METHOD_NOT_FOUND:
109 return True
110 # Python's SimpleXMLRPCDispatcher uses faultCode 1 with this message.
111 return f'method "{method}" is not supported' in str(fault.faultString)
112
113
114def _GetApprovedManifest(
115 server: xmlrpc.client.ServerProxy,
116 branch: str,
117 target: Optional[str],
118 best_effort: bool,
119) -> Tuple[bool, str]:
120 """Requests the approved manifest from the manifest server.
121
122 Args:
123 server: The manifest server proxy.
124 branch: The manifest branch.
125 target: The build target, if any.
126 best_effort: The client can proceed without a manifest (e.g. by
127 falling back to ToT), so the server may decline the request.
128
129 Returns:
130 A (success, manifest XML or error message) tuple.
131 """
132 request = {"branch": branch}
133 if target:
134 request["target"] = target
135 if best_effort:
136 request["best_effort"] = True
137
138 try:
139 response = server.GetApprovedManifestV2(request)
140 except xmlrpc.client.Fault as e:
141 if not _IsMethodNotFoundFault(e, _GET_APPROVED_MANIFEST_V2):
142 raise
143 # Older server: |best_effort| has no equivalent and is dropped.
144 if target:
145 success, manifest_str = server.GetApprovedManifest(branch, target)
146 else:
147 success, manifest_str = server.GetApprovedManifest(branch)
148 return success, manifest_str
149
150 if not isinstance(response, dict):
151 raise SmartSyncError(
152 f"error: invalid {_GET_APPROVED_MANIFEST_V2} response: {response!r}"
153 )
154 if response.get("success"):
155 manifest_xml = response.get("manifest_xml")
156 if not isinstance(manifest_xml, str):
157 raise SmartSyncError(
158 f"error: {_GET_APPROVED_MANIFEST_V2} response has invalid "
159 f"manifest_xml: {manifest_xml!r}"
160 )
161 return True, manifest_xml
162 return False, response.get("error_message", "unknown error")
163
101 164
102def _SafeCheckoutOrder(checkouts: List[Project]) -> List[List[Project]]: 165def _SafeCheckoutOrder(checkouts: List[Project]) -> List[List[Project]]:
103 """Generate a sequence of checkouts that is safe to perform. 166 """Generate a sequence of checkouts that is safe to perform.
@@ -2094,6 +2157,7 @@ later is required to fix a server side protocol bug.
2094 opt: optparse.Values, 2157 opt: optparse.Values,
2095 smart_sync_manifest_path: str, 2158 smart_sync_manifest_path: str,
2096 manifest: XmlManifest, 2159 manifest: XmlManifest,
2160 best_effort: bool = False,
2097 ) -> str: 2161 ) -> str:
2098 if not manifest.manifest_server: 2162 if not manifest.manifest_server:
2099 raise SmartSyncError( 2163 raise SmartSyncError(
@@ -2140,12 +2204,9 @@ later is required to fix a server side protocol bug.
2140 os.environ["TARGET_BUILD_VARIANT"], 2204 os.environ["TARGET_BUILD_VARIANT"],
2141 ) 2205 )
2142 2206
2143 if target: 2207 success, manifest_str = _GetApprovedManifest(
2144 [success, manifest_str] = server.GetApprovedManifest( 2208 server, branch, target, best_effort
2145 branch, target 2209 )
2146 )
2147 else:
2148 [success, manifest_str] = server.GetApprovedManifest(branch)
2149 else: 2210 else:
2150 assert opt.smart_tag 2211 assert opt.smart_tag
2151 [success, manifest_str] = server.GetManifest(opt.smart_tag) 2212 [success, manifest_str] = server.GetManifest(opt.smart_tag)
@@ -2519,7 +2580,10 @@ later is required to fix a server side protocol bug.
2519 if opt.smart_sync or opt.smart_tag: 2580 if opt.smart_sync or opt.smart_tag:
2520 try: 2581 try:
2521 manifest_name = self._SmartSyncSetup( 2582 manifest_name = self._SmartSyncSetup(
2522 opt, smart_sync_manifest_path, manifest 2583 opt,
2584 smart_sync_manifest_path,
2585 manifest,
2586 best_effort=implicit_smart_sync,
2523 ) 2587 )
2524 except (SmartSyncError, ManifestParseError) as e: 2588 except (SmartSyncError, ManifestParseError) as e:
2525 if not implicit_smart_sync: 2589 if not implicit_smart_sync: