|
12 | 12 | import os |
13 | 13 | import re |
14 | 14 | import shlex |
| 15 | +import shutil |
15 | 16 | import subprocess |
16 | 17 | import sys |
17 | 18 | import tomllib |
|
54 | 55 | RELEASE_WORKFLOW_URL = ( |
55 | 56 | "https://github.com/smorinlabs/envgen/actions/workflows/release.yml" |
56 | 57 | ) |
57 | | -LOCKFILE_SYNC_ARGS = ["cargo", "+1.88.0", "generate-lockfile"] |
| 58 | +PINNED_RUST_TOOLCHAIN = "1.88.0" |
58 | 59 |
|
59 | 60 |
|
60 | 61 | class BumpError(RuntimeError): |
@@ -441,18 +442,62 @@ def run_git_command(args: list[str], dry_run: bool) -> None: |
441 | 442 | fail(f"Command failed: {quoted}") |
442 | 443 |
|
443 | 444 |
|
| 445 | +def lockfile_sync_command_candidates() -> list[list[str]]: |
| 446 | + commands: list[list[str]] = [] |
| 447 | + if shutil.which("rustup"): |
| 448 | + commands.append( |
| 449 | + ["rustup", "run", PINNED_RUST_TOOLCHAIN, "cargo", "generate-lockfile"] |
| 450 | + ) |
| 451 | + |
| 452 | + if shutil.which("cargo"): |
| 453 | + commands.append(["cargo", f"+{PINNED_RUST_TOOLCHAIN}", "generate-lockfile"]) |
| 454 | + |
| 455 | + if not commands: |
| 456 | + fail( |
| 457 | + "Could not find rustup or cargo to synchronize Cargo.lock.\n" |
| 458 | + "Install Rust and rerun: make sync-lockfile" |
| 459 | + ) |
| 460 | + |
| 461 | + # Keep deterministic attempt order while removing accidental duplicates. |
| 462 | + deduped: list[list[str]] = [] |
| 463 | + seen: set[tuple[str, ...]] = set() |
| 464 | + for command in commands: |
| 465 | + key = tuple(command) |
| 466 | + if key in seen: |
| 467 | + continue |
| 468 | + seen.add(key) |
| 469 | + deduped.append(command) |
| 470 | + return deduped |
| 471 | + |
| 472 | + |
444 | 473 | def sync_cargo_lockfile(dry_run: bool) -> None: |
445 | | - command = shlex.join(LOCKFILE_SYNC_ARGS) |
| 474 | + commands = lockfile_sync_command_candidates() |
| 475 | + command = shlex.join(commands[0]) |
446 | 476 | if dry_run: |
447 | 477 | print(f"[dry-run] would run: {command}") |
| 478 | + if len(commands) > 1: |
| 479 | + print(f"[dry-run] fallback command: {shlex.join(commands[1])}") |
448 | 480 | return |
449 | 481 |
|
450 | | - result = subprocess.run(LOCKFILE_SYNC_ARGS, cwd=ROOT, check=False) |
451 | | - if result.returncode != 0: |
452 | | - fail( |
453 | | - "Failed to synchronize Cargo.lock after crate version bump.\n" |
454 | | - "Run: make sync-lockfile" |
455 | | - ) |
| 482 | + failures: list[str] = [] |
| 483 | + for index, args in enumerate(commands): |
| 484 | + quoted = shlex.join(args) |
| 485 | + if index > 0: |
| 486 | + print( |
| 487 | + f"WARNING: lockfile sync failed; retrying with fallback: {quoted}", |
| 488 | + file=sys.stderr, |
| 489 | + ) |
| 490 | + |
| 491 | + result = subprocess.run(args, cwd=ROOT, check=False) |
| 492 | + if result.returncode == 0: |
| 493 | + return |
| 494 | + failures.append(f"{quoted} (exit {result.returncode})") |
| 495 | + |
| 496 | + fail( |
| 497 | + "Failed to synchronize Cargo.lock after crate version bump.\n" |
| 498 | + f"Tried:\n - {'\n - '.join(failures)}\n" |
| 499 | + "Run: make sync-lockfile" |
| 500 | + ) |
456 | 501 |
|
457 | 502 |
|
458 | 503 | def create_tag(tag_name: str, message: str, dry_run: bool) -> None: |
|
0 commit comments