Skip to content

Commit 53bb663

Browse files
authored
Merge pull request #433 from lomnido/feat-switch_on_sync
'sync': introduce '--switch'
2 parents 96eb72e + 2087d57 commit 53bb663

11 files changed

+1849
-50
lines changed

tsrc/cli/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def resolve_repos(
173173
all_cloned: bool,
174174
include_regex: str = "",
175175
exclude_regex: str = "",
176+
do_switch: bool = False,
176177
ignore_if_group_not_found: bool = False,
177178
ignore_group_item: bool = False,
178179
) -> List[Repo]:
@@ -192,6 +193,8 @@ def resolve_repos(
192193
repos = manifest.get_repos(
193194
groups=groups, ignore_if_group_not_found=ignore_if_group_not_found
194195
)
196+
elif do_switch is True:
197+
repos = manifest.get_repos(groups, do_switch)
195198
elif all_cloned:
196199
repos = manifest.get_repos(all_=True)
197200
repos = [repo for repo in repos if (workspace.root_path / repo.dest).exists()]

tsrc/cli/sync.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" Entry point for `tsrc sync` """
22

33
import argparse
4-
from typing import List
4+
from typing import List, Union
55

66
import cli_ui as ui
77

@@ -50,6 +50,12 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
5050
dest="ignore_group_item",
5151
help="ignore group element if it is not found among Manifest's Repos. WARNING: If you end up in need of this option, you have to understand that you end up with useles Manifest. Warnings will be printed for each Group element that is missing, so it may be easier to fix that. Using this option is NOT RECOMMENDED for normal use", # noqa: E501
5252
)
53+
parser.add_argument(
54+
"--switch",
55+
action="store_true",
56+
dest="do_switch",
57+
help="change config in accordance to Manifest's switch section. this is pariculary usefull when we wish to change Manifest branch to some version that may have different Groups configured.", # noqa: E501
58+
)
5359
parser.add_argument(
5460
"--clean",
5561
action="store_true",
@@ -89,42 +95,50 @@ def run(args: argparse.Namespace) -> None:
8995
correct_branch = args.correct_branch
9096
workspace = get_workspace(args)
9197
num_jobs = get_num_jobs(args)
98+
do_switch = args.do_switch
9299
do_clean = args.do_clean
93100
do_hard_clean = args.do_hard_clean
94101

95102
ignore_if_group_not_found: bool = False
96-
report_update_repo_groups: bool = False
103+
report_update_repo_groups: Union[bool, None] = False
97104

98105
if update_manifest:
106+
repo_groups_0 = workspace.config.repo_groups.copy()
99107
ui.info_2("Updating manifest")
100108
workspace.update_manifest()
101109

102-
# check if groups needs to be ignored
110+
# check if groups needs to be updated on config
103111
found_groups: List[str] = []
104-
if groups and args.ignore_if_group_not_found is True:
112+
if groups:
105113
local_manifest = workspace.local_manifest.get_manifest()
106114
if local_manifest.group_list and local_manifest.group_list.groups:
107115
found_groups = list(
108116
set(groups).intersection(local_manifest.group_list.groups)
109117
)
110-
if update_config_repo_groups is True:
111-
workspace.update_config_repo_groups(
112-
groups=found_groups, ignore_group_item=args.ignore_group_item
113-
)
114-
report_update_repo_groups = True
115118

116-
if update_config_repo_groups is True:
117-
if args.ignore_if_group_not_found is True:
118-
ignore_if_group_not_found = True
119+
if do_switch is True:
120+
121+
report_update_repo_groups = workspace.update_config_on_switch(
122+
workspace.local_manifest.get_manifest(), found_groups, groups
123+
)
124+
if not isinstance(report_update_repo_groups, bool):
125+
ui.error("Provided Groups does not match any in the Manifest")
126+
return
127+
elif update_config_repo_groups is True:
119128
workspace.update_config_repo_groups(
120-
groups=found_groups, ignore_group_item=args.ignore_group_item
129+
groups=found_groups,
130+
ignore_group_item=args.ignore_group_item,
131+
want_groups=groups,
121132
)
122133
report_update_repo_groups = True
123134

124-
if report_update_repo_groups is True:
125-
ui.info_2("Updating repo_groups")
135+
if (
136+
report_update_repo_groups is True
137+
and repo_groups_0 != workspace.config.repo_groups # noqa: W503
138+
):
139+
ui.info_2("Updating Workspace Groups configuration")
126140
else:
127-
ui.info_2("Leaving repo_groups intact")
141+
ui.info_2("Leaving Workspace Groups configuration intact")
128142
else:
129143
ui.info_2("Not updating manifest")
130144
if args.ignore_if_group_not_found is True:
@@ -137,6 +151,7 @@ def run(args: argparse.Namespace) -> None:
137151
all_cloned=all_cloned,
138152
include_regex=include_regex,
139153
exclude_regex=exclude_regex,
154+
do_switch=do_switch,
140155
ignore_if_group_not_found=ignore_if_group_not_found,
141156
ignore_group_item=args.ignore_group_item,
142157
)

tsrc/errors.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ def __init__(self, mtod: ManifestsTypeOfData) -> None:
5050
super().__init__(msg)
5151

5252

53+
class LoadManifestSwitchConfigGroupsError(Error):
54+
def __init__(self) -> None:
55+
msg = "Manifest's Switch Config Groups does not match Groups"
56+
super().__init__(msg)
57+
58+
5359
class MissingRepoError(Error):
5460
def __init__(self, dest: str):
5561
super().__init__(f"No repo found in '{dest}'. Please run `tsrc sync`")

tsrc/manifest.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88
import schema
99

1010
from tsrc.config import parse_config
11-
from tsrc.errors import Error, InvalidConfigError, LoadManifestSchemaError
11+
from tsrc.errors import (
12+
Error,
13+
InvalidConfigError,
14+
LoadManifestSchemaError,
15+
LoadManifestSwitchConfigGroupsError,
16+
)
1217
from tsrc.file_system import Copy, FileSystemOperation, Link
1318
from tsrc.groups import GroupList
1419
from tsrc.manifest_common_data import ManifestsTypeOfData, mtod_can_ignore_remotes
1520
from tsrc.repo import Remote, Repo
21+
from tsrc.switch import Switch
1622

1723

1824
class RepoNotFound(Error):
@@ -29,6 +35,7 @@ class Manifest:
2935
def __init__(self) -> None:
3036
self._repos: List[Repo] = []
3137
self.group_list: Optional[GroupList[str]] = None
38+
self._switch: Optional[Switch] = None
3239

3340
def apply_config(
3441
self,
@@ -53,6 +60,9 @@ def apply_config(
5360
ignore_on_mtod=ignore_on_mtod,
5461
)
5562

63+
switch_config = config.get("switch")
64+
self._handle_switch(switch_config)
65+
5666
def _handle_repo(self, repo_config: Any) -> None:
5767
dest = repo_config["dest"]
5868
branch = orig_branch = repo_config.get("branch")
@@ -122,15 +132,33 @@ def _handle_groups(
122132
ignore_on_mtod=ignore_on_mtod,
123133
)
124134

135+
def _handle_switch(self, switch_config: Any) -> None:
136+
self._switch = Switch(switch_config)
137+
138+
# verify if groups in switch>config>groups are present in 'groups'
139+
if self.group_list and self.group_list.groups and self._switch._groups:
140+
switch_groups = list(self._switch._groups)
141+
groups_groups = self.group_list.groups.keys()
142+
if switch_groups and groups_groups:
143+
if set(switch_groups) != set(groups_groups).intersection(switch_groups):
144+
raise LoadManifestSwitchConfigGroupsError()
145+
elif self._switch._groups:
146+
# you cannot have 'swtich>config>groups' alone (without 'groups')
147+
raise LoadManifestSwitchConfigGroupsError()
148+
125149
def get_repos(
126150
self,
127151
groups: Optional[List[str]] = None,
152+
do_switch: bool = False,
128153
all_: bool = False,
129154
ignore_if_group_not_found: bool = False,
130155
) -> List[Repo]:
131156
if all_:
132157
return self._repos
133158

159+
if do_switch is True:
160+
return self._get_repos_on_switch(groups)
161+
134162
if not groups:
135163
if self._has_default_group():
136164
return self._get_repos_in_groups(["default"])
@@ -143,6 +171,13 @@ def _has_default_group(self) -> bool:
143171
assert self.group_list
144172
return self.group_list.get_group("default") is not None
145173

174+
def _get_repos_on_switch(self, groups: Optional[List[str]]) -> List[Repo]:
175+
if self._switch:
176+
if self._switch._groups:
177+
matched_groups = list(self._switch._groups)
178+
return self._get_repos_in_groups(matched_groups)
179+
return self._repos # all repos
180+
146181
def _get_repos_in_groups(
147182
self,
148183
groups: List[str],
@@ -220,6 +255,21 @@ def validate_repo_no_remote_required(data: Any) -> None:
220255
)
221256

222257

258+
def validate_switch_config(data: Any) -> None:
259+
switch_config_schema = schema.Schema(
260+
{
261+
schema.Optional("groups"): [str],
262+
}
263+
)
264+
switch_config_schema.validate(data)
265+
266+
267+
def validate_switch(data: Any) -> None:
268+
on_config_schema = schema.Use(validate_switch_config)
269+
switch_schema = schema.Schema({schema.Optional("config"): on_config_schema})
270+
switch_schema.validate(data)
271+
272+
223273
def load_manifest(manifest_path: Path) -> Manifest:
224274
"""Main entry point: return a manifest instance by parsing
225275
a `manifest.yml` file.
@@ -230,12 +280,14 @@ def load_manifest(manifest_path: Path) -> Manifest:
230280
group_schema = {"repos": [str], schema.Optional("includes"): [str]}
231281
# Note: gitlab and github_enterprise_url keys are ignored,
232282
# and kept here only for backward compatibility reasons
283+
on_switch_schema = schema.Use(validate_switch)
233284
manifest_schema = schema.Schema(
234285
{
235286
"repos": [repo_schema],
236287
schema.Optional("gitlab"): remote_git_server_schema,
237288
schema.Optional("github_enterprise"): remote_git_server_schema,
238289
schema.Optional("groups"): {str: group_schema},
290+
schema.Optional("switch"): on_switch_schema,
239291
}
240292
)
241293
parsed = parse_config(manifest_path, schema=manifest_schema)
@@ -262,12 +314,14 @@ def load_manifest_safe_mode(manifest_path: Path, mtod: ManifestsTypeOfData) -> M
262314
group_schema = {"repos": [str], schema.Optional("includes"): [str]}
263315
# Note: gitlab and github_enterprise_url keys are ignored,
264316
# and kept here only for backward compatibility reasons
317+
on_switch_schema = schema.Use(validate_switch)
265318
manifest_schema = schema.Schema(
266319
{
267320
"repos": [repo_schema],
268321
schema.Optional("gitlab"): remote_git_server_schema,
269322
schema.Optional("github_enterprise"): remote_git_server_schema,
270323
schema.Optional("groups"): {str: group_schema},
324+
schema.Optional("switch"): on_switch_schema,
271325
}
272326
)
273327
try:

tsrc/switch.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Support for switch in manifest
2+
3+
'switch' can hold part of new default configuration
4+
and such should only be activated by option '--switch'
5+
when activated:
6+
A) and present:
7+
it should completely overwrite current configuration.
8+
B) but not present:
9+
the default (empty) configuration should be used
10+
11+
this is particulary usefull when switching
12+
to new Manifest branch, no need to care about
13+
current configuration anymore.
14+
15+
NOTE: original handling of configuration
16+
should be adhered when no '--switch' is provided
17+
"""
18+
19+
from typing import Any, List, Optional
20+
21+
22+
class Switch:
23+
def __init__(self, switch_config: Any) -> None:
24+
self._config: Optional[Any] = None
25+
self._groups: Optional[List[Any]] = None
26+
if switch_config:
27+
self._config = switch_config.get("config")
28+
if self._config:
29+
self._groups = self._config.get("groups")

tsrc/test/cli/test_dump_manifest__filter_bo_manifest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ def test_only_manifest__on_workspace(
282282
raise Exception("There should be only Manifest")
283283
if count != 1:
284284
raise Exception("Manifest processing error")
285-
print("DEBUG PATH =", workspace_path)
286285

287286

288287
def test_only_manifest__on_raw(

0 commit comments

Comments
 (0)