xref: /aosp_15_r20/tools/external_updater/tests/endtoend/treebuilder/fakerepo.py (revision 3c875a214f382db1236d28570d1304ce57138f32)
1*3c875a21SAndroid Build Coastguard Worker#
2*3c875a21SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
3*3c875a21SAndroid Build Coastguard Worker#
4*3c875a21SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*3c875a21SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*3c875a21SAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*3c875a21SAndroid Build Coastguard Worker#
8*3c875a21SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*3c875a21SAndroid Build Coastguard Worker#
10*3c875a21SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*3c875a21SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*3c875a21SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*3c875a21SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*3c875a21SAndroid Build Coastguard Worker# limitations under the License.
15*3c875a21SAndroid Build Coastguard Worker#
16*3c875a21SAndroid Build Coastguard Worker"""repo tree fakes for use in tests."""
17*3c875a21SAndroid Build Coastguard Workerimport contextlib
18*3c875a21SAndroid Build Coastguard Workerimport subprocess
19*3c875a21SAndroid Build Coastguard Workerfrom pathlib import Path
20*3c875a21SAndroid Build Coastguard Workerfrom xml.etree import ElementTree
21*3c875a21SAndroid Build Coastguard Worker
22*3c875a21SAndroid Build Coastguard Workerfrom .fakeproject import FakeProject
23*3c875a21SAndroid Build Coastguard Worker
24*3c875a21SAndroid Build Coastguard Worker
25*3c875a21SAndroid Build Coastguard Workerclass FakeRepo:
26*3c875a21SAndroid Build Coastguard Worker    """A repo tree for use in tests.
27*3c875a21SAndroid Build Coastguard Worker
28*3c875a21SAndroid Build Coastguard Worker    This shouldn't be used directly. Use the tree_builder fixture.
29*3c875a21SAndroid Build Coastguard Worker    """
30*3c875a21SAndroid Build Coastguard Worker
31*3c875a21SAndroid Build Coastguard Worker    def __init__(self, temp_dir: Path) -> None:
32*3c875a21SAndroid Build Coastguard Worker        self.root = temp_dir / "tree"
33*3c875a21SAndroid Build Coastguard Worker        self.mirror_dir = temp_dir / "mirrors"
34*3c875a21SAndroid Build Coastguard Worker        self.upstream_dir = temp_dir / "upstreams"
35*3c875a21SAndroid Build Coastguard Worker        self.manifest_repo = temp_dir / "manifest"
36*3c875a21SAndroid Build Coastguard Worker        self.projects: list[FakeProject] = []
37*3c875a21SAndroid Build Coastguard Worker        self._used_git_subpaths: set[str] = set()
38*3c875a21SAndroid Build Coastguard Worker        self._used_tree_subpaths: set[str] = set()
39*3c875a21SAndroid Build Coastguard Worker
40*3c875a21SAndroid Build Coastguard Worker    def project(self, git_subpath: str, tree_subpath: str) -> FakeProject:
41*3c875a21SAndroid Build Coastguard Worker        """Creates a new project in the repo."""
42*3c875a21SAndroid Build Coastguard Worker        if git_subpath in self._used_git_subpaths:
43*3c875a21SAndroid Build Coastguard Worker            raise KeyError(f"A project with git path {git_subpath} already exists")
44*3c875a21SAndroid Build Coastguard Worker        if tree_subpath in self._used_tree_subpaths:
45*3c875a21SAndroid Build Coastguard Worker            raise KeyError(f"A project with tree path {tree_subpath} already exists")
46*3c875a21SAndroid Build Coastguard Worker        project = FakeProject(
47*3c875a21SAndroid Build Coastguard Worker            self.root / tree_subpath,
48*3c875a21SAndroid Build Coastguard Worker            self.upstream_dir / tree_subpath,
49*3c875a21SAndroid Build Coastguard Worker            self.mirror_dir / git_subpath,
50*3c875a21SAndroid Build Coastguard Worker        )
51*3c875a21SAndroid Build Coastguard Worker        self.projects.append(project)
52*3c875a21SAndroid Build Coastguard Worker        self._used_git_subpaths.add(git_subpath)
53*3c875a21SAndroid Build Coastguard Worker        self._used_tree_subpaths.add(tree_subpath)
54*3c875a21SAndroid Build Coastguard Worker        return project
55*3c875a21SAndroid Build Coastguard Worker
56*3c875a21SAndroid Build Coastguard Worker    def init_and_sync(self) -> None:
57*3c875a21SAndroid Build Coastguard Worker        """Runs repo init and repo sync to clone the repo tree."""
58*3c875a21SAndroid Build Coastguard Worker        self.root.mkdir(parents=True)
59*3c875a21SAndroid Build Coastguard Worker        with contextlib.chdir(self.root):
60*3c875a21SAndroid Build Coastguard Worker            subprocess.run(
61*3c875a21SAndroid Build Coastguard Worker                ["repo", "init", "-c", "-u", str(self.manifest_repo), "-b", "main"],
62*3c875a21SAndroid Build Coastguard Worker                check=True,
63*3c875a21SAndroid Build Coastguard Worker            )
64*3c875a21SAndroid Build Coastguard Worker            subprocess.run(["repo", "sync", "-c"], check=True)
65*3c875a21SAndroid Build Coastguard Worker
66*3c875a21SAndroid Build Coastguard Worker    def create_manifest_repo(self) -> None:
67*3c875a21SAndroid Build Coastguard Worker        """Creates the git repo for the manifest, commits the manifest XML."""
68*3c875a21SAndroid Build Coastguard Worker        self.manifest_repo.mkdir(parents=True)
69*3c875a21SAndroid Build Coastguard Worker        with contextlib.chdir(self.manifest_repo):
70*3c875a21SAndroid Build Coastguard Worker            subprocess.run(["git", "init"], check=True)
71*3c875a21SAndroid Build Coastguard Worker            Path("default.xml").write_bytes(
72*3c875a21SAndroid Build Coastguard Worker                ElementTree.tostring(self._create_manifest_xml(), encoding="utf-8")
73*3c875a21SAndroid Build Coastguard Worker            )
74*3c875a21SAndroid Build Coastguard Worker            subprocess.run(["git", "add", "default.xml"], check=True)
75*3c875a21SAndroid Build Coastguard Worker            subprocess.run(["git", "commit", "-m", "Initial commit."], check=True)
76*3c875a21SAndroid Build Coastguard Worker
77*3c875a21SAndroid Build Coastguard Worker    def _create_manifest_xml(self) -> ElementTree.Element:
78*3c875a21SAndroid Build Coastguard Worker        # Example manifest:
79*3c875a21SAndroid Build Coastguard Worker        #
80*3c875a21SAndroid Build Coastguard Worker        # <manifest>
81*3c875a21SAndroid Build Coastguard Worker        #   <remote name="aosp" fetch="$URL" />
82*3c875a21SAndroid Build Coastguard Worker        #   <default revision="main" remote="aosp" />
83*3c875a21SAndroid Build Coastguard Worker        #
84*3c875a21SAndroid Build Coastguard Worker        #   <project path="external/project" name="platform/external/project"
85*3c875a21SAndroid Build Coastguard Worker        #            revision="master" remote="goog" />
86*3c875a21SAndroid Build Coastguard Worker        #   ...
87*3c875a21SAndroid Build Coastguard Worker        # </manifest>
88*3c875a21SAndroid Build Coastguard Worker        #
89*3c875a21SAndroid Build Coastguard Worker        # The revision and remote attributes of project are optional.
90*3c875a21SAndroid Build Coastguard Worker        root = ElementTree.Element("manifest")
91*3c875a21SAndroid Build Coastguard Worker        ElementTree.SubElement(
92*3c875a21SAndroid Build Coastguard Worker            root,
93*3c875a21SAndroid Build Coastguard Worker            "remote",
94*3c875a21SAndroid Build Coastguard Worker            {"name": "aosp", "fetch": self.mirror_dir.resolve().as_uri()},
95*3c875a21SAndroid Build Coastguard Worker        )
96*3c875a21SAndroid Build Coastguard Worker        ElementTree.SubElement(root, "default", {"revision": "main", "remote": "aosp"})
97*3c875a21SAndroid Build Coastguard Worker        for project in self.projects:
98*3c875a21SAndroid Build Coastguard Worker            ElementTree.SubElement(
99*3c875a21SAndroid Build Coastguard Worker                root,
100*3c875a21SAndroid Build Coastguard Worker                "project",
101*3c875a21SAndroid Build Coastguard Worker                {
102*3c875a21SAndroid Build Coastguard Worker                    "path": str(project.local.path.relative_to(self.root)),
103*3c875a21SAndroid Build Coastguard Worker                    "name": str(
104*3c875a21SAndroid Build Coastguard Worker                        project.android_mirror.path.relative_to(self.mirror_dir)
105*3c875a21SAndroid Build Coastguard Worker                    ),
106*3c875a21SAndroid Build Coastguard Worker                },
107*3c875a21SAndroid Build Coastguard Worker            )
108*3c875a21SAndroid Build Coastguard Worker        return root
109