sync: Support SYNC_BRANCH to override the smart sync branch

`repo sync -s` always sends the manifest branch to the manifest server
as the `branch` argument of GetApprovedManifest. Some servers map one
manifest branch to several server-side (CI) branches, and the only way
to pick among them was indirectly through the target.

Honor a SYNC_BRANCH environment variable, mirroring SYNC_TARGET: if set
and non-empty, send it instead of the manifest branch. An empty value
is treated as unset. -t/--smart-tag is unaffected.

Bug: 568962967
Test: ./run_tests
Change-Id: I26700e8997c21487f83011258100fd91a5c87508
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/639123
Commit-Queue: Rahul Yadav <yadavrah@google.com>
Reviewed-by: Gavin Mak <gavinmak@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-09 07:00:47 -07:00
1 parent 0a4e68f9d2
commit 340b587e94
3 files changed
+87 -3

No files matched your search

+4 -1
View File
@@ -174,7 +174,10 @@ Explicitly enables Smart Sync. Repo will call
`GetApprovedManifest(branch[, target])`.
The `branch` is determined by the current manifest branch as specified by
`--manifest-branch=BRANCH` when running `repo init`.
`--manifest-branch=BRANCH` when running `repo init`. If `${SYNC_BRANCH}` is
defined and non-empty, its value is sent instead. This allows selecting a
branch other than the one the checkout tracks, e.g. a server-side (CI) branch
name that builds from the same manifest branch.
The `target` is defined by environment variables in the order below. If none
of them match, then `target` is omitted. These variables were decided as they
+12 -2
View File
@@ -2059,7 +2059,12 @@ later is required to fix a server side protocol bug.
return server_url, transport
def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
def _SmartSyncSetup(
self,
opt: optparse.Values,
smart_sync_manifest_path: str,
manifest: XmlManifest,
) -> str:
if not manifest.manifest_server:
raise SmartSyncError(
"error: cannot smart sync: no manifest server defined in "
@@ -2077,7 +2082,12 @@ later is required to fix a server side protocol bug.
try:
server = xmlrpc.client.Server(server_url, transport=transport)
if opt.smart_sync:
branch = self._GetBranch(manifest.manifestProject)
# SYNC_BRANCH overrides the branch sent to the manifest server
# (e.g. to select a specific CI branch that builds the same
# manifest branch). An empty value is treated as unset.
branch = os.environ.get("SYNC_BRANCH")
if not branch:
branch = self._GetBranch(manifest.manifestProject)
target = None
if "SYNC_TARGET" in os.environ:
target = os.environ["SYNC_TARGET"]
+71
View File
@@ -2982,3 +2982,74 @@ class TestSmartSyncSetupRemoteHelper(unittest.TestCase):
"Stderr was: debugging logs",
str(context.exception),
)
class TestSmartSyncSetupBranch(unittest.TestCase):
"""Tests for how _SmartSyncSetup picks the branch sent to the server."""
def setUp(self) -> None:
self.cmd = sync.Sync()
self.opt = mock.MagicMock()
self.opt.quiet = True
self.opt.smart_sync = True
self.opt.smart_tag = None
self.manifest = mock.MagicMock()
self.manifest.manifest_server = (
"http://android-smartsync.corp.google.com/manifestserver"
)
self.manifest.manifest_server_helper = None
self.smart_sync_manifest_path = "/fake/path/to/manifest.xml"
self.mock_server = mock.MagicMock()
self.mock_server.GetApprovedManifest.return_value = [
True,
"<manifest></manifest>",
]
self.cmd._GetBranch = mock.MagicMock(return_value="main")
self.cmd._ReloadManifest = mock.MagicMock()
self.cmd._ResolveManifestServerTransport = mock.MagicMock(
return_value=(self.manifest.manifest_server, mock.MagicMock())
)
def _RunSetup(self, env: Dict[str, str]) -> str:
with mock.patch.dict(os.environ, env, clear=True), mock.patch(
"xmlrpc.client.Server", return_value=self.mock_server
), mock.patch("builtins.open", mock.mock_open()):
return self.cmd._SmartSyncSetup(
self.opt, self.smart_sync_manifest_path, self.manifest
)
def test_default_branch(self) -> None:
"""Without SYNC_BRANCH the manifest branch is used."""
self._RunSetup({})
self.cmd._GetBranch.assert_called_once_with(
self.manifest.manifestProject
)
self.mock_server.GetApprovedManifest.assert_called_once_with("main")
def test_sync_branch(self) -> None:
"""SYNC_BRANCH replaces the manifest branch."""
self._RunSetup({"SYNC_BRANCH": "git_main-abfs"})
self.cmd._GetBranch.assert_not_called()
self.mock_server.GetApprovedManifest.assert_called_once_with(
"git_main-abfs"
)
def test_sync_branch_with_target(self) -> None:
"""SYNC_BRANCH and SYNC_TARGET are both forwarded."""
self._RunSetup(
{
"SYNC_BRANCH": "git_main-abfs",
"SYNC_TARGET": "cf_x86_64_phone-trunk_staging-eng",
}
)
self.mock_server.GetApprovedManifest.assert_called_once_with(
"git_main-abfs", "cf_x86_64_phone-trunk_staging-eng"
)
def test_empty_sync_branch(self) -> None:
"""An empty SYNC_BRANCH falls back to the manifest branch."""
self._RunSetup({"SYNC_BRANCH": ""})
self.cmd._GetBranch.assert_called_once_with(
self.manifest.manifestProject
)
self.mock_server.GetApprovedManifest.assert_called_once_with("main")