|
8 | 8 | from contextlib import contextmanager
|
9 | 9 | from pathlib import Path
|
10 | 10 | from tempfile import TemporaryDirectory
|
11 |
| -from typing import Dict, TextIO, List, Optional, Generator |
| 11 | +from typing import Dict, TextIO, List, Optional, Generator, Callable |
12 | 12 |
|
13 | 13 | import requests
|
14 | 14 |
|
|
17 | 17 | FAKE_BRANCHES = ['main', 'feature_1', 'feature_x']
|
18 | 18 | GITHUB_API_ROOT = 'https://api.github.com'
|
19 | 19 | MODE = 'plain'
|
| 20 | +TARGET_PATCH_LEVEL = 2 |
20 | 21 |
|
21 | 22 |
|
22 | 23 | def read_line(f: TextIO) -> Optional[str]:
|
@@ -170,46 +171,129 @@ def run_tests(conf: Dict[str, str], site_ids: List[str], branches: List[str], cl
|
170 | 171 | install_deps(branch, tmpp)
|
171 | 172 | with info('Testing branch "%s"' % branch):
|
172 | 173 | run_branch_tests(conf, tmpp, run_id, clone, site_id, branch)
|
| 174 | +# Patching routines |
173 | 175 |
|
174 | 176 |
|
175 |
| -OLD_REPO = 'ExaWorks/psi-j-python' |
176 |
| -NEW_REPO = 'ExaWorks/psij-python' |
| 177 | +def write_patch_level(level: int) -> None: |
| 178 | + with open('.ci.patchlevel', 'w') as f: |
| 179 | + f.write(str(level)) |
177 | 180 |
|
178 | 181 |
|
179 |
| -def patch_file(file_name: str) -> None: |
180 |
| - if os.path.exists(file_name + '.is_patched'): |
| 182 | +def current_patch_level() -> int: |
| 183 | + try: |
| 184 | + with open('.ci.patchlevel') as f: |
| 185 | + return int(f.read().strip()) |
| 186 | + except OSError: |
| 187 | + for fn in ['testing.conf', 'psij-ci-run']: |
| 188 | + if not Path(fn + '.is_patched').exists(): |
| 189 | + return 0 |
| 190 | + write_patch_level(1) |
| 191 | + return 1 |
| 192 | + |
| 193 | + |
| 194 | +def deploy_patch(level: int) -> None: |
| 195 | + if level == 1: |
| 196 | + l1_patch_repo() |
| 197 | + l1_update_origin() |
| 198 | + elif level == 2: |
| 199 | + l2_remove_patch_flag_files() |
| 200 | + l2_update_upload_url() |
| 201 | + else: |
| 202 | + raise Exception('Nothing to do for patch level %s' % level) |
| 203 | + write_patch_level(level) |
| 204 | + |
| 205 | + |
| 206 | +def try_patch(level: int) -> None: |
| 207 | + if level <= current_patch_level(): |
181 | 208 | return
|
| 209 | + else: |
| 210 | + deploy_patch(level) |
| 211 | + |
| 212 | + |
| 213 | +def deploy_patches() -> None: |
| 214 | + for level in range(1, TARGET_PATCH_LEVEL + 1): |
| 215 | + try_patch(level) |
| 216 | + |
182 | 217 |
|
| 218 | +def line_patcher(file_name: str, matcher: Callable[[str], bool], |
| 219 | + mutator: Callable[[str], str]) -> None: |
183 | 220 | with info('Patching %s' % file_name):
|
184 | 221 | with open(file_name) as inf:
|
185 | 222 | with open(file_name + '._new_', 'w') as outf:
|
186 | 223 | for line in inf:
|
187 | 224 | # strip new line
|
188 |
| - if line.find(OLD_REPO) != -1: |
| 225 | + if matcher(line): |
189 | 226 | # we're adding one space so that the line has the same length;
|
190 | 227 | # when invoking a subprocess, bash stores the location where
|
191 | 228 | # it's supposed to continue parsing from, so it's a good idea
|
192 | 229 | # to to not move things around
|
193 |
| - line = line.rstrip('\n').replace(OLD_REPO, NEW_REPO) + ' \n' |
| 230 | + line = mutator(line) |
194 | 231 | outf.write(line)
|
195 | 232 | os.chmod(file_name + '._new_', os.stat(file_name).st_mode)
|
196 | 233 | os.rename(file_name + '._new_', file_name)
|
197 |
| - Path(file_name + '.is_patched').touch() |
198 | 234 |
|
199 | 235 |
|
200 |
| -def patch_repo() -> None: |
201 |
| - patch_file('testing.conf') |
202 |
| - patch_file('psij-ci-run') |
| 236 | +# Patch 1 |
| 237 | +# Updates repositories in testing.conf and psij-ci-run. It also |
| 238 | +# updates the git origin to point to the new repo. This is done to |
| 239 | +# account for the fact that we renamed the repo from psi-j-python |
| 240 | +# to psij-python. |
| 241 | + |
| 242 | +OLD_REPO = 'ExaWorks/psi-j-python' |
| 243 | +NEW_REPO = 'ExaWorks/psij-python' |
| 244 | + |
| 245 | + |
| 246 | +def l1_patch_file(file_name: str) -> None: |
| 247 | + if os.path.exists(file_name + '.is_patched'): |
| 248 | + return |
| 249 | + line_patcher(file_name, |
| 250 | + lambda line: line.find(OLD_REPO) != -1, |
| 251 | + # The extra space before the newline is to not shift the content |
| 252 | + # of psij-ci-run that follows this line. Bash continues reading |
| 253 | + # the file after a command completes, but, if the content has |
| 254 | + # shifted, it might end up reading a partial line. |
| 255 | + lambda line: line.rstrip('\n').replace(OLD_REPO, NEW_REPO) + ' \n') |
203 | 256 |
|
204 | 257 |
|
205 |
| -def update_origin() -> None: |
| 258 | +def l1_patch_repo() -> None: |
| 259 | + l1_patch_file('testing.conf') |
| 260 | + l1_patch_file('psij-ci-run') |
| 261 | + |
| 262 | + |
| 263 | +def l1_update_origin() -> None: |
206 | 264 | old_url = run('git', 'config', '--get', 'remote.origin.url')
|
207 | 265 | new_url = old_url.strip().replace(OLD_REPO, NEW_REPO)
|
208 | 266 | if new_url != old_url:
|
209 | 267 | with info('Updating git url to %s' % new_url):
|
210 | 268 | run('git', 'remote', 'set-url', 'origin', new_url)
|
211 | 269 |
|
212 | 270 |
|
| 271 | +# Patch 2 |
| 272 | +# Updates the test upload url from either testing.exaworks.org or |
| 273 | +# psij.testing.exaworks.org to testing.psij.io |
| 274 | + |
| 275 | +OLD_UPLOAD_URLS = ['https://psij.testing.exaworks.org', 'https://testing.exaworks.org'] |
| 276 | +NEW_UPLOAD_URL = 'https://testing.psij.io' |
| 277 | + |
| 278 | + |
| 279 | +def l2_remove_patch_flag_files() -> None: |
| 280 | + # we're using a single patch level file now |
| 281 | + for fn in ['testing.conf', 'psij-ci-run']: |
| 282 | + f = Path(fn + '.is_patched') |
| 283 | + if f.exists(): |
| 284 | + f.unlink() |
| 285 | + |
| 286 | + |
| 287 | +def l2_update_upload_url() -> None: |
| 288 | + for old_url in OLD_UPLOAD_URLS: |
| 289 | + line_patcher('testing.conf', |
| 290 | + lambda line: line.find('server_url') != -1 |
| 291 | + and line.find(old_url) != -1, # noqa: E127 |
| 292 | + lambda line: line.rstrip('\n').replace(old_url, NEW_UPLOAD_URL) + '\n') |
| 293 | + |
| 294 | +# End of patches |
| 295 | + |
| 296 | + |
213 | 297 | @contextmanager
|
214 | 298 | def info(msg: str) -> Generator[bool, None, None]:
|
215 | 299 | print(msg + '... ', end='')
|
@@ -246,6 +330,5 @@ def info(msg: str) -> Generator[bool, None, None]:
|
246 | 330 | else:
|
247 | 331 | raise ValueError('Unrecognized value for scope: "%s"' % scope)
|
248 | 332 |
|
249 |
| - patch_repo() |
250 |
| - update_origin() |
| 333 | + deploy_patches() |
251 | 334 | run_tests(conf, site_ids, branches, clone)
|
0 commit comments