jrl-cmakemodules-scripts 2.3.0__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jrl-cmakemodules-scripts
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Release scripting tools for JRL CMake modules
5
5
  License: BSD-3-Clause
6
6
  Project-URL: homepage, https://github.com/jrl-umi3218/jrl-cmakemodules
@@ -79,12 +79,15 @@ jrl-release --bump patch --git-commit --git-tag
79
79
  | `package.xml` | `<version>` tag |
80
80
  | `pyproject.toml` | `project.version` |
81
81
  | `CHANGELOG.md` | First `## [X.Y.Z]` section (not Unreleased) |
82
- | `pixi.toml` | `[workspace] version` |
82
+ | `pixi.toml` | `[workspace] version` and `[package] version` (pixi-build) |
83
83
  | `pixi.lock` | Regenerated via `pixi list` |
84
+ | `Cargo.toml` | `[package] version` and `[workspace.package] version` |
85
+ | `Cargo.lock` | Regenerated via `cargo update --workspace` |
84
86
  | `CITATION.cff` | `version` key |
85
87
  | `CMakeLists.txt` | `project(... VERSION X.Y.Z ...)` |
86
88
 
87
- > Requires `pixi` CLI if `pixi.lock` exists in the project root.
89
+ > Requires `pixi` CLI if `pixi.lock` exists in the project root, and `cargo`
90
+ > if `Cargo.lock` does.
88
91
 
89
92
  ## Testing
90
93
 
@@ -61,12 +61,15 @@ jrl-release --bump patch --git-commit --git-tag
61
61
  | `package.xml` | `<version>` tag |
62
62
  | `pyproject.toml` | `project.version` |
63
63
  | `CHANGELOG.md` | First `## [X.Y.Z]` section (not Unreleased) |
64
- | `pixi.toml` | `[workspace] version` |
64
+ | `pixi.toml` | `[workspace] version` and `[package] version` (pixi-build) |
65
65
  | `pixi.lock` | Regenerated via `pixi list` |
66
+ | `Cargo.toml` | `[package] version` and `[workspace.package] version` |
67
+ | `Cargo.lock` | Regenerated via `cargo update --workspace` |
66
68
  | `CITATION.cff` | `version` key |
67
69
  | `CMakeLists.txt` | `project(... VERSION X.Y.Z ...)` |
68
70
 
69
- > Requires `pixi` CLI if `pixi.lock` exists in the project root.
71
+ > Requires `pixi` CLI if `pixi.lock` exists in the project root, and `cargo`
72
+ > if `Cargo.lock` does.
70
73
 
71
74
  ## Testing
72
75
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jrl-cmakemodules-scripts
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Release scripting tools for JRL CMake modules
5
5
  License: BSD-3-Clause
6
6
  Project-URL: homepage, https://github.com/jrl-umi3218/jrl-cmakemodules
@@ -79,12 +79,15 @@ jrl-release --bump patch --git-commit --git-tag
79
79
  | `package.xml` | `<version>` tag |
80
80
  | `pyproject.toml` | `project.version` |
81
81
  | `CHANGELOG.md` | First `## [X.Y.Z]` section (not Unreleased) |
82
- | `pixi.toml` | `[workspace] version` |
82
+ | `pixi.toml` | `[workspace] version` and `[package] version` (pixi-build) |
83
83
  | `pixi.lock` | Regenerated via `pixi list` |
84
+ | `Cargo.toml` | `[package] version` and `[workspace.package] version` |
85
+ | `Cargo.lock` | Regenerated via `cargo update --workspace` |
84
86
  | `CITATION.cff` | `version` key |
85
87
  | `CMakeLists.txt` | `project(... VERSION X.Y.Z ...)` |
86
88
 
87
- > Requires `pixi` CLI if `pixi.lock` exists in the project root.
89
+ > Requires `pixi` CLI if `pixi.lock` exists in the project root, and `cargo`
90
+ > if `Cargo.lock` does.
88
91
 
89
92
  ## Testing
90
93
 
@@ -71,21 +71,24 @@ uv run --no-project jrl_release.py --bump patch --git-commit --git-tag
71
71
  | `package.xml` | `<version> and <url>` tag |
72
72
  | `pyproject.toml` | `project.version` and `project.urls` |
73
73
  | `CHANGELOG.md` | First `## [X.Y.Z]` section (not Unreleased) |
74
- | `pixi.toml` | `[workspace] version` |
74
+ | `pixi.toml` | `[workspace] version` and `[package] version` (pixi-build) |
75
75
  | `pixi.lock` | Regenerated via `pixi list` |
76
+ | `Cargo.toml` | `[package] version` and `[workspace.package] version` |
77
+ | `Cargo.lock` | Regenerated via `cargo update --workspace` |
76
78
  | `CITATION.cff` | `version` key |
77
79
  | `CMakeLists.txt` | `project(... VERSION X.Y.Z ... HOMEPAGE_URL ...)` |
78
80
  | `debian/changelog` | `version = ...` |
79
81
  | `conanfile.py` | `version = ...` |
80
82
 
81
- > Requires `pixi` CLI if `pixi.lock` exists in the project root.
83
+ > Requires `pixi` CLI if `pixi.lock` exists in the project root, and `cargo`
84
+ > if `Cargo.lock` does.
82
85
 
83
86
  ## Meta-packages
84
87
 
85
88
  ROS meta-packages are handled automatically: every nested directory with a
86
89
  `package.xml` is treated like the repository root — the full set of
87
90
  supported files (`package.xml`, `pyproject.toml`, `CHANGELOG.md`, `pixi.toml`,
88
- `CITATION.cff`, `CMakeLists.txt`, `debian/changelog`) is checked there too,
91
+ `Cargo.toml`, `CITATION.cff`, `CMakeLists.txt`, `debian/changelog`) is checked there too,
89
92
  skipping any that don't exist in that nested directory.
90
93
  Discovery skips hidden directories (`.git`, `.pixi`, `.venv`, …), git submodules,
91
94
  and — honoring your `.gitignore` via `git check-ignore` — ignored dirs like
@@ -103,6 +106,7 @@ import json
103
106
  import subprocess
104
107
  import shutil
105
108
  import tempfile
109
+ from collections import Counter
106
110
  from pathlib import Path
107
111
  from abc import ABC, abstractmethod
108
112
  from typing import List, Optional, Tuple, Dict
@@ -168,6 +172,11 @@ class VersionExtractor(ABC):
168
172
  def check_file_exists(self) -> bool:
169
173
  return self.file_path.exists()
170
174
 
175
+ @property
176
+ def id(self) -> tuple:
177
+ """Unique identity of the tracked version field."""
178
+ return (self.file_path,)
179
+
171
180
  @property
172
181
  def name(self) -> str:
173
182
  return self.file_path.name
@@ -229,6 +238,11 @@ class TomlVersionExtractor(VersionExtractor):
229
238
  super().__init__(file_path)
230
239
  self.keys = keys
231
240
 
241
+ @property
242
+ def id(self) -> tuple:
243
+ # Several keys can be tracked in the same file (e.g. pixi.toml, Cargo.toml).
244
+ return (self.file_path, tuple(self.keys))
245
+
232
246
  def get_version(self) -> str:
233
247
  with open(self.file_path, "r", encoding="utf-8") as f:
234
248
  data = tomlkit.load(f)
@@ -242,6 +256,13 @@ class TomlVersionExtractor(VersionExtractor):
242
256
  f"Key '{'.'.join(self.keys)}' not found in {self.name}"
243
257
  )
244
258
 
259
+ # e.g. pixi-build `[package] version = { workspace = true }`,
260
+ # or Cargo `version.workspace = true`
261
+ if isinstance(value, dict):
262
+ raise VersionNotPresent(
263
+ f"Key '{'.'.join(self.keys)}' in {self.name} is not a version string"
264
+ )
265
+
245
266
  return str(value)
246
267
 
247
268
  def update_version(self, new_version: str) -> None:
@@ -782,6 +803,11 @@ def build_root_checks(root_dir: Path) -> List[VersionExtractor]:
782
803
  TomlVersionExtractor(root_dir / "pyproject.toml", ["project", "version"]),
783
804
  ChangelogVersionExtractor(root_dir / "CHANGELOG.md", r""),
784
805
  TomlVersionExtractor(root_dir / "pixi.toml", ["workspace", "version"]),
806
+ TomlVersionExtractor(root_dir / "pixi.toml", ["package", "version"]),
807
+ TomlVersionExtractor(root_dir / "Cargo.toml", ["package", "version"]),
808
+ TomlVersionExtractor(
809
+ root_dir / "Cargo.toml", ["workspace", "package", "version"]
810
+ ),
785
811
  YamlVersionExtractor(root_dir / "CITATION.cff", ["version"]),
786
812
  CMakeListsVersionExtractor(root_dir / "CMakeLists.txt"),
787
813
  DebianChangelogVersionExtractor(root_dir / "debian/changelog"),
@@ -797,21 +823,26 @@ def collect_version_checks(root_dir: Path) -> List[VersionExtractor]:
797
823
  package directory are simply skipped (VersionExtractor.check_file_exists()).
798
824
  """
799
825
  checks = list(build_root_checks(root_dir))
800
- seen_paths = {check.file_path for check in checks}
826
+ seen_ids = {check.id for check in checks}
801
827
 
802
828
  for package_root in discover_package_roots(root_dir):
803
829
  for check in build_root_checks(package_root):
804
- if check.file_path in seen_paths:
830
+ if check.id in seen_ids:
805
831
  continue
806
832
  checks.append(check)
807
- seen_paths.add(check.file_path)
833
+ seen_ids.add(check.id)
808
834
 
809
835
  # Label nested files by their relative path so identical basenames stay distinct.
836
+ checks_per_file = Counter(check.file_path for check in checks)
810
837
  for check in checks:
811
838
  try:
812
839
  check.label = str(check.file_path.relative_to(root_dir))
813
840
  except ValueError:
814
841
  check.label = check.file_path.name
842
+ # A file tracking several version fields (e.g. pixi.toml, Cargo.toml)
843
+ # gets its table appended so each field stays distinct.
844
+ if checks_per_file[check.file_path] > 1:
845
+ check.label += f" [{'.'.join(check.keys[:-1])}]"
815
846
 
816
847
  return checks
817
848
 
@@ -1242,63 +1273,113 @@ def gh_release(
1242
1273
  return False, e.stderr or ""
1243
1274
 
1244
1275
 
1245
- def update_pixi_lock(root_dir: Path, dry_run: bool = False) -> Optional[str]:
1246
- """Update pixi.lock file by running 'pixi list'.
1276
+ def update_lock_file(
1277
+ root_dir: Path,
1278
+ lock_name: str,
1279
+ command: List[str],
1280
+ install_url: str,
1281
+ dry_run: bool = False,
1282
+ backups: Optional[Dict[Path, Path]] = None,
1283
+ ) -> Optional[str]:
1284
+ """Regenerate a lock file by running `command` in root_dir.
1247
1285
 
1248
- Returns the path to pixi.lock if updated, None otherwise.
1286
+ The lock file is added to `backups` before being rewritten, so a later
1287
+ failure restores it. Returns the path to the lock file if it exists (and
1288
+ so is, or in dry run would be, regenerated), None otherwise.
1249
1289
  """
1250
- pixi_lock_path = root_dir / "pixi.lock"
1290
+ lock_path = root_dir / lock_name
1291
+ tool = command[0]
1292
+ cmd_str = " ".join(command)
1251
1293
 
1252
- if not pixi_lock_path.exists():
1294
+ if not lock_path.exists():
1253
1295
  return None
1254
1296
 
1255
1297
  if dry_run:
1256
- return None
1298
+ return str(lock_path)
1299
+
1300
+ if backups is not None:
1301
+ backup_file(backups, lock_path)
1257
1302
 
1258
1303
  console.print(
1259
- f"[{STYLE_INFO}]Running 'pixi list' to update pixi.lock...[/{STYLE_INFO}]"
1304
+ f"[{STYLE_INFO}]Running '{cmd_str}' to update {lock_name}...[/{STYLE_INFO}]"
1260
1305
  )
1261
1306
  try:
1262
1307
  result = subprocess.run(
1263
- ["pixi", "list"],
1308
+ command,
1264
1309
  cwd=root_dir,
1265
1310
  timeout=60,
1266
1311
  )
1267
1312
 
1268
1313
  if result.returncode != 0:
1269
1314
  console.print(
1270
- f"[{STYLE_ERROR}]Error: 'pixi list' returned non-zero exit code: {result.returncode}[/{STYLE_ERROR}]"
1315
+ f"[{STYLE_ERROR}]Error: '{cmd_str}' returned non-zero exit code: {result.returncode}[/{STYLE_ERROR}]"
1271
1316
  )
1272
1317
  console.print(
1273
- f"[{STYLE_ERROR}]Failed to update pixi.lock. Please ensure 'pixi' is installed.[/{STYLE_ERROR}]"
1318
+ f"[{STYLE_ERROR}]Failed to update {lock_name}. Please ensure '{tool}' is installed.[/{STYLE_ERROR}]"
1274
1319
  )
1275
- raise RuntimeError(f"'pixi list' failed with exit code {result.returncode}")
1320
+ raise RuntimeError(f"'{cmd_str}' failed with exit code {result.returncode}")
1276
1321
 
1277
1322
  except subprocess.TimeoutExpired:
1278
1323
  console.print(
1279
- f"[{STYLE_ERROR}]Error: 'pixi list' command timed out[/{STYLE_ERROR}]"
1324
+ f"[{STYLE_ERROR}]Error: '{cmd_str}' command timed out[/{STYLE_ERROR}]"
1280
1325
  )
1281
- raise RuntimeError("'pixi list' command timed out after 30 seconds")
1326
+ raise RuntimeError(f"'{cmd_str}' command timed out after 60 seconds")
1282
1327
  except FileNotFoundError:
1283
- console.print(f"[{STYLE_ERROR}]Error: 'pixi' command not found[/{STYLE_ERROR}]")
1284
1328
  console.print(
1285
- f"[{STYLE_ERROR}]pixi.lock exists but 'pixi' executable is not available.[/{STYLE_ERROR}]"
1329
+ f"[{STYLE_ERROR}]Error: '{tool}' command not found[/{STYLE_ERROR}]"
1286
1330
  )
1287
1331
  console.print(
1288
- f"[{STYLE_INFO}]Please install pixi: https://pixi.sh[/{STYLE_INFO}]"
1332
+ f"[{STYLE_ERROR}]{lock_name} exists but '{tool}' executable is not available.[/{STYLE_ERROR}]"
1289
1333
  )
1290
- raise RuntimeError("'pixi' executable not found. Install from https://pixi.sh")
1334
+ console.print(
1335
+ f"[{STYLE_INFO}]Please install {tool}: {install_url}[/{STYLE_INFO}]"
1336
+ )
1337
+ raise RuntimeError(f"'{tool}' executable not found. Install from {install_url}")
1291
1338
  except Exception as e:
1292
1339
  console.print(
1293
- f"[{STYLE_ERROR}]Error: Failed to run 'pixi list': {e}[/{STYLE_ERROR}]"
1340
+ f"[{STYLE_ERROR}]Error: Failed to run '{cmd_str}': {e}[/{STYLE_ERROR}]"
1294
1341
  )
1295
- raise RuntimeError(f"Failed to run 'pixi list': {e}") from e
1342
+ raise RuntimeError(f"Failed to run '{cmd_str}': {e}") from e
1296
1343
 
1297
1344
  console.print(
1298
- f"[{STYLE_SUCCESS}]✓ Updated pixi.lock via 'pixi list'[/{STYLE_SUCCESS}]"
1345
+ f"[{STYLE_SUCCESS}]✓ Updated {lock_name} via '{cmd_str}'[/{STYLE_SUCCESS}]"
1299
1346
  )
1300
1347
 
1301
- return str(pixi_lock_path)
1348
+ return str(lock_path)
1349
+
1350
+
1351
+ def update_pixi_lock(
1352
+ root_dir: Path,
1353
+ dry_run: bool = False,
1354
+ backups: Optional[Dict[Path, Path]] = None,
1355
+ ) -> Optional[str]:
1356
+ """Update pixi.lock file by running 'pixi list'.
1357
+
1358
+ Returns the path to pixi.lock if it exists, None otherwise.
1359
+ """
1360
+ return update_lock_file(
1361
+ root_dir, "pixi.lock", ["pixi", "list"], "https://pixi.sh", dry_run, backups
1362
+ )
1363
+
1364
+
1365
+ def update_cargo_lock(
1366
+ root_dir: Path,
1367
+ dry_run: bool = False,
1368
+ backups: Optional[Dict[Path, Path]] = None,
1369
+ ) -> Optional[str]:
1370
+ """Update Cargo.lock file by running 'cargo update --workspace'.
1371
+
1372
+ Only the workspace crates' own versions are refreshed, not dependencies.
1373
+ Returns the path to Cargo.lock if it exists, None otherwise.
1374
+ """
1375
+ return update_lock_file(
1376
+ root_dir,
1377
+ "Cargo.lock",
1378
+ ["cargo", "update", "--workspace"],
1379
+ "https://rustup.rs",
1380
+ dry_run,
1381
+ backups,
1382
+ )
1302
1383
 
1303
1384
 
1304
1385
  def create_backups(file_paths: List[Path]) -> Dict[Path, Path]:
@@ -1306,17 +1387,27 @@ def create_backups(file_paths: List[Path]) -> Dict[Path, Path]:
1306
1387
 
1307
1388
  Returns a mapping of original paths to backup paths.
1308
1389
  """
1309
- backups = {}
1310
- temp_dir = Path(tempfile.mkdtemp(prefix="release_backup_"))
1390
+ backups: Dict[Path, Path] = {}
1391
+ for file_path in file_paths:
1392
+ backup_file(backups, file_path)
1393
+ return backups
1311
1394
 
1312
- # Index-prefix the name so files sharing a basename don't collide.
1313
- for index, file_path in enumerate(file_paths):
1314
- if file_path.exists():
1315
- backup_path = temp_dir / f"{index:04d}_{file_path.name}"
1316
- shutil.copy2(file_path, backup_path)
1317
- backups[file_path] = backup_path
1318
1395
 
1319
- return backups
1396
+ def backup_file(backups: Dict[Path, Path], file_path: Path) -> None:
1397
+ """Add a backup copy of file_path to backups (no-op if missing or already there)."""
1398
+ # Deduplicate: a file may carry several tracked version fields.
1399
+ if file_path in backups or not file_path.exists():
1400
+ return
1401
+
1402
+ if backups:
1403
+ temp_dir = next(iter(backups.values())).parent
1404
+ else:
1405
+ temp_dir = Path(tempfile.mkdtemp(prefix="release_backup_"))
1406
+
1407
+ # Index-prefix the name so files sharing a basename don't collide.
1408
+ backup_path = temp_dir / f"{len(backups):04d}_{file_path.name}"
1409
+ shutil.copy2(file_path, backup_path)
1410
+ backups[file_path] = backup_path
1320
1411
 
1321
1412
 
1322
1413
  def restore_backups(backups: Dict[Path, Path]) -> None:
@@ -1548,13 +1639,14 @@ def perform_version_updates(
1548
1639
  check.update_version(target_version)
1549
1640
  dry_run_rows.append((check.label, old_version, target_version))
1550
1641
  line = Text()
1551
- line.append(f" {check.label:<28}", style="cyan")
1642
+ line.append(f" {check.label:<28} ", style="cyan")
1552
1643
  line.append(old_version, style=STYLE_OLD_VALUE)
1553
1644
  line.append(" → ", style="dim")
1554
1645
  line.append(target_version, style=STYLE_NEW_VALUE)
1555
1646
  console.print(line)
1556
1647
  updated_files.append(check.label)
1557
- updated_file_paths.append(str(check.file_path))
1648
+ if str(check.file_path) not in updated_file_paths:
1649
+ updated_file_paths.append(str(check.file_path))
1558
1650
  except VersionNotPresent:
1559
1651
  pass # file exists but has no version configured; skip
1560
1652
  except Exception as e:
@@ -1567,9 +1659,17 @@ def perform_version_updates(
1567
1659
  return updated_files, updated_file_paths, failed, dry_run_rows
1568
1660
 
1569
1661
 
1662
+ def print_lock_rows(lock_paths: List[str]) -> None:
1663
+ for lock_path in lock_paths:
1664
+ line = Text()
1665
+ line.append(f" {Path(lock_path).name:<28}", style="cyan")
1666
+ line.append("regenerated", style="dim")
1667
+ console.print(line)
1668
+
1669
+
1570
1670
  def show_dry_run_panel(
1571
1671
  dry_run_rows: List[Tuple[str, str, str]],
1572
- pixi_lock_would_update: bool,
1672
+ lock_paths: List[str],
1573
1673
  git_lines: List[str],
1574
1674
  ) -> None:
1575
1675
  """Display a unified dry-run preview."""
@@ -1587,11 +1687,7 @@ def show_dry_run_panel(
1587
1687
  line.append(" → ", style="dim")
1588
1688
  line.append(new, style=STYLE_NEW_VALUE)
1589
1689
  console.print(line)
1590
- if pixi_lock_would_update:
1591
- line = Text()
1592
- line.append(f" {'pixi.lock':<28}", style="cyan")
1593
- line.append("regenerated via pixi list", style="dim")
1594
- console.print(line)
1690
+ print_lock_rows(lock_paths)
1595
1691
 
1596
1692
  if git_lines:
1597
1693
  console.print()
@@ -1603,14 +1699,10 @@ def show_dry_run_panel(
1603
1699
 
1604
1700
 
1605
1701
  def show_result_panel(
1606
- pixi_lock_updated: bool,
1702
+ lock_paths: List[str],
1607
1703
  ) -> None:
1608
1704
  """Display a polished summary of completed version updates."""
1609
- if pixi_lock_updated:
1610
- line = Text()
1611
- line.append(f" {'pixi.lock':<28}", style="cyan")
1612
- line.append("regenerated via pixi list", style="dim")
1613
- console.print(line)
1705
+ print_lock_rows(lock_paths)
1614
1706
  console.print()
1615
1707
  console.print(
1616
1708
  f"[{STYLE_SUCCESS_STRONG}]✓ Version updated successfully[/{STYLE_SUCCESS_STRONG}]"
@@ -1899,13 +1991,17 @@ def main():
1899
1991
  sys.exit(1)
1900
1992
 
1901
1993
  try:
1902
- pixi_lock_path = update_pixi_lock(root_dir, args.dry_run)
1903
- if pixi_lock_path:
1904
- updated_files.append("pixi.lock")
1905
- updated_file_paths.append(pixi_lock_path)
1994
+ lock_paths: List[str] = []
1995
+ if lock_path := update_pixi_lock(root_dir, args.dry_run, backups):
1996
+ lock_paths.append(lock_path)
1997
+ if lock_path := update_cargo_lock(root_dir, args.dry_run, backups):
1998
+ lock_paths.append(lock_path)
1999
+ for lock_path in lock_paths:
2000
+ updated_files.append(Path(lock_path).name)
2001
+ updated_file_paths.append(lock_path)
1906
2002
  except RuntimeError as e:
1907
2003
  console.print(
1908
- f"[{STYLE_ERROR}]Pixi lock update failed: {e}[/{STYLE_ERROR}]"
2004
+ f"[{STYLE_ERROR}]Lock file update failed: {e}[/{STYLE_ERROR}]"
1909
2005
  )
1910
2006
  if backups:
1911
2007
  console.print(
@@ -1960,8 +2056,6 @@ def main():
1960
2056
  archive_name = f"{project_url.split('/')[-1]}-{target_version}.tar.gz"
1961
2057
 
1962
2058
  if args.dry_run:
1963
- pixi_lock_would_update = (root_dir / "pixi.lock").exists()
1964
-
1965
2059
  git_lines: List[str] = []
1966
2060
  if args.git_commit is not None:
1967
2061
  custom_message = None if args.git_commit is True else args.git_commit
@@ -2017,11 +2111,11 @@ def main():
2017
2111
  )
2018
2112
  git_lines.append(call)
2019
2113
 
2020
- show_dry_run_panel(dry_run_rows, pixi_lock_would_update, git_lines)
2114
+ show_dry_run_panel(dry_run_rows, lock_paths, git_lines)
2021
2115
  sys.exit(0)
2022
2116
  else:
2023
2117
  if not args.short and args.output_format == "text":
2024
- show_result_panel(pixi_lock_path is not None)
2118
+ show_result_panel(lock_paths)
2025
2119
 
2026
2120
  # Git operations - only perform if explicitly requested
2027
2121
  if args.git_tag is not None and args.git_commit is None:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jrl-cmakemodules-scripts"
7
- version = "2.3.0"
7
+ version = "2.4.0"
8
8
  description = "Release scripting tools for JRL CMake modules"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"