LbAPLocal 0.9.7__tar.gz → 0.9.9__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.
Files changed (139) hide show
  1. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/PKG-INFO +2 -1
  2. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/pyproject.toml +1 -0
  3. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cli/main.py +161 -2
  4. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/PKG-INFO +2 -1
  5. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/requires.txt +1 -0
  6. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/.coveragerc +0 -0
  7. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/.gitattributes +0 -0
  8. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/.gitignore +0 -0
  9. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/.gitlab-ci.yml +0 -0
  10. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/.pre-commit-config.yaml +0 -0
  11. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/CONTRIBUTING.md +0 -0
  12. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/LICENSE +0 -0
  13. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/README.md +0 -0
  14. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/example.json +0 -0
  15. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/setup.cfg +0 -0
  16. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/__init__.py +0 -0
  17. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cli/__init__.py +0 -0
  18. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cli/legacy.py +0 -0
  19. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cli/textual_ui.py +0 -0
  20. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cli/wizards.py +0 -0
  21. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cwl/__init__.py +0 -0
  22. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cwl/generate_replica_map.py +0 -0
  23. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/cwl/rewrite.py +0 -0
  24. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/job_slice.py +0 -0
  25. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/log_parsing.py +0 -0
  26. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/testing.py +0 -0
  27. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal/utils.py +0 -0
  28. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/SOURCES.txt +0 -0
  29. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/dependency_links.txt +0 -0
  30. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/entry_points.txt +0 -0
  31. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/src/LbAPLocal.egg-info/top_level.txt +0 -0
  32. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/capture_fixtures.py +0 -0
  33. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/cassettes/test_execute/test_run_test[2016_MagDown_PromptMC_D02KK-2444].yaml +0 -0
  34. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/cassettes/test_execute/test_run_test[2016_MagDown_PromptMC_D02KK_noautoconf-2444].yaml +0 -0
  35. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/cassettes/test_execute/test_run_test[2016_MagUp_PromptMC_D02KK-2393].yaml +0 -0
  36. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/cassettes/test_execute/test_run_test[2016_MagUp_PromptMC_D02KK_noautoconf-2393].yaml +0 -0
  37. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/conftest.py +0 -0
  38. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/.gitignore +0 -0
  39. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/.gitlab-ci.yml +0 -0
  40. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/AnalysisProductions.xenv +0 -0
  41. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bc2Bspi2JpsiPhi.py +0 -0
  42. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bd2JpsiKstar.py +0 -0
  43. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bd2JpsiKstar_stripping.py +0 -0
  44. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bs2DsPi.py +0 -0
  45. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bs2JpsiPhi.py +0 -0
  46. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bs2JpsiPhi_Prompt.py +0 -0
  47. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bs2JpsiPhi_Prompt_PVMixer.py +0 -0
  48. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bs2JpsiPhi_Swave.py +0 -0
  49. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bu2JpsiKplus.py +0 -0
  50. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/Bu2JpsiKplus_stripping.py +0 -0
  51. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/LbJpsipK.py +0 -0
  52. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/README.md +0 -0
  53. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/helpers/__init__.py +0 -0
  54. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/helpers/stripping.py +0 -0
  55. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/helpers/tuple_maker.py +0 -0
  56. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/info.yaml +0 -0
  57. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/BsToJpsiPhi/sequence_setup.py +0 -0
  58. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/COPYING +0 -0
  59. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/README.md +0 -0
  60. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/Run1_Signal.py +0 -0
  61. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/Run2_Signal.py +0 -0
  62. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/helpers/__init__.py +0 -0
  63. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/helpers/signal_tuple_maker.py +0 -0
  64. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/RDs/info.yaml +0 -0
  65. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/README.md +0 -0
  66. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/__init__.py +0 -0
  67. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2011_datatype.py +0 -0
  68. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2012_datatype.py +0 -0
  69. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2015_datatype.py +0 -0
  70. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2016_datatype.py +0 -0
  71. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2017_datatype.py +0 -0
  72. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/2018_datatype.py +0 -0
  73. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/B0ToK0ee.py +0 -0
  74. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/B0ToK0mumu.py +0 -0
  75. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/BpToKee.py +0 -0
  76. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/BpToKmumu.py +0 -0
  77. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0ee.py +0 -0
  78. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0eeSS.py +0 -0
  79. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0emu.py +0 -0
  80. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0emuSS.py +0 -0
  81. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0mumu.py +0 -0
  82. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/LbToL0mumuSS.py +0 -0
  83. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/README.md +0 -0
  84. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/__init__.py +0 -0
  85. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/general_options.py +0 -0
  86. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/MC_tuple_maker.py +0 -0
  87. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/__init__.py +0 -0
  88. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/decay_descriptors.py +0 -0
  89. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/locations.py +0 -0
  90. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/related_info.py +0 -0
  91. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/stripping.py +0 -0
  92. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/tags.py +0 -0
  93. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/helpers/tuple_maker.py +0 -0
  94. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/info.yaml +0 -0
  95. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_general_options_DST.py +0 -0
  96. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_general_options_FDST.py +0 -0
  97. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_general_options_FMDST.py +0 -0
  98. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_general_options_MDST.py +0 -0
  99. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2JpsiKs_ee.py +0 -0
  100. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2JpsiKs_mm.py +0 -0
  101. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2Ksee.py +0 -0
  102. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2Ksmm.py +0 -0
  103. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2PsiKs_ee.py +0 -0
  104. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bd2PsiKs_mm.py +0 -0
  105. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bu2JpsiK_ee.py +0 -0
  106. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Bu2JpsiK_mm.py +0 -0
  107. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2JpsiL_ee.py +0 -0
  108. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2JpsiL_mm.py +0 -0
  109. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2Lee.py +0 -0
  110. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2Lem.py +0 -0
  111. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2Lmm.py +0 -0
  112. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2PsiL_ee.py +0 -0
  113. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/mc_samples/MC_Lb2PsiL_mm.py +0 -0
  114. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/sequence_setup.py +0 -0
  115. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/cbtesting/stripping_seq.py +0 -0
  116. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/executabletests/2016_MagDown_PromptMC_D02KK_autoconf.py +0 -0
  117. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/executabletests/2016_MagUp_PromptMC_D02KK_autoconf.py +0 -0
  118. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/executabletests/info.yaml +0 -0
  119. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/data-pkg-repo/executabletests/ntuple_options.py +0 -0
  120. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/error-failed-to-read-file.log +0 -0
  121. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/error-illegal-instruction.log +0 -0
  122. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/error-missing-shared-library.log +0 -0
  123. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/error-platform-unsupported.log +0 -0
  124. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/error-related-info-missing.log +0 -0
  125. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/good-DaVinci_00110296_00000038_1.log +0 -0
  126. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/good-DaVinci_00110296_00000194_1.log +0 -0
  127. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/data/example-logs/good-Gauss_00104988_00000011_1.log +0 -0
  128. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/fixture_loader.py +0 -0
  129. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/fixtures/executabletests/2016_MagDown_PromptMC_D02KK/query_result.json +0 -0
  130. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/fixtures/executabletests/2016_MagUp_PromptMC_D02KK/query_result.json +0 -0
  131. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/fixtures/executabletests/manifest.yaml +0 -0
  132. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/reslice_fixture.py +0 -0
  133. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test-input-dst/00070767_00000711_7.AllStreams_slice.dst +0 -0
  134. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test-input-dst/00070793_00000685_7.AllStreams_slice.dst +0 -0
  135. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test_cli.py +0 -0
  136. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test_cwl_cli.py +0 -0
  137. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test_execute.py +0 -0
  138. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test_local_datapkg.py +0 -0
  139. {lbaplocal-0.9.7 → lbaplocal-0.9.9}/tests/test_log_parse.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LbAPLocal
3
- Version: 0.9.7
3
+ Version: 0.9.9
4
4
  Summary: Tool to locally run tests for AnalysisProductions
5
5
  Author: LHCb
6
6
  Project-URL: Homepage, https://gitlab.cern.ch/lhcb-dpa/analysis-productions/lbaplocal
@@ -24,6 +24,7 @@ Requires-Dist: consolemd
24
24
  Requires-Dist: mplhep
25
25
  Requires-Dist: requests
26
26
  Requires-Dist: setuptools
27
+ Requires-Dist: packaging
27
28
  Requires-Dist: typer
28
29
  Requires-Dist: rich
29
30
  Requires-Dist: textual
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "mplhep",
31
31
  "requests",
32
32
  "setuptools",
33
+ "packaging",
33
34
  "typer",
34
35
  "rich",
35
36
  "textual",
@@ -17,6 +17,7 @@ and integration with CWL-based workflows.
17
17
  """
18
18
 
19
19
  import importlib.metadata
20
+ import json
20
21
  import os
21
22
  import random
22
23
  import shlex
@@ -30,6 +31,7 @@ from typing import Annotated, Optional
30
31
  import typer
31
32
  import yaml
32
33
  from LbAPCommon import parse_yaml, render_yaml
34
+ from packaging.version import InvalidVersion, Version
33
35
  from rich.console import Console
34
36
  from rich.live import Live
35
37
  from rich.panel import Panel
@@ -42,6 +44,7 @@ from rich.tree import Tree
42
44
  from LbAPLocal.job_slice import run_minimal_reproducer
43
45
 
44
46
  from ..utils import (
47
+ LBAPI_HOST,
45
48
  available_productions,
46
49
  check_production,
47
50
  inside_ap_datapkg,
@@ -66,6 +69,9 @@ _BANNER_STAMP = _CACHE_DIR / ".banner_stamp"
66
69
  _ANIMATION_STAMP = _CACHE_DIR / ".animation_stamp"
67
70
  _BANNER_COOLDOWN_SECS = 3600 # 1 hour
68
71
  _ANIMATION_COOLDOWN_SECS = 1800 # 30 minutes
72
+ _VERSION_CHECK_STAMP = _CACHE_DIR / ".version_check"
73
+ _VERSION_CHECK_CACHE = _CACHE_DIR / "version_compat.json"
74
+ _VERSION_CHECK_COOLDOWN_SECS = 7200 # 2 hours
69
75
 
70
76
  # Commands that always show the banner regardless of cooldown
71
77
  _ALWAYS_BANNER_COMMANDS = {"test"}
@@ -158,6 +164,114 @@ def _check_dirac_proxy() -> bool:
158
164
  return False
159
165
 
160
166
 
167
+ def _fetch_version_compatibility() -> dict | None:
168
+ """Fetch CLI version compatibility info from LbAPI.
169
+
170
+ Returns the parsed JSON response, or None if the request fails.
171
+ """
172
+ import requests
173
+
174
+ try:
175
+ resp = requests.get(f"{LBAPI_HOST}/cli/compatibility", timeout=3)
176
+ resp.raise_for_status()
177
+ data = resp.json()
178
+ # Cache for future use
179
+ _CACHE_DIR.mkdir(parents=True, exist_ok=True)
180
+ _VERSION_CHECK_CACHE.write_text(json.dumps(data))
181
+ _touch_stamp(_VERSION_CHECK_STAMP)
182
+ return data
183
+ except Exception:
184
+ return None
185
+
186
+
187
+ def _check_version_compatibility(version_str: str) -> None:
188
+ """Check if the installed version is allowed by the server.
189
+
190
+ Exits with an error if the version is blocked or below the minimum.
191
+ Silently passes if the server is unreachable and no cache exists.
192
+ """
193
+ if version_str == "dev":
194
+ return
195
+
196
+ try:
197
+ installed = Version(version_str)
198
+ except InvalidVersion:
199
+ return
200
+
201
+ # Use cache if fresh, otherwise fetch
202
+ if not _stamp_expired(_VERSION_CHECK_STAMP, _VERSION_CHECK_COOLDOWN_SECS):
203
+ try:
204
+ data = json.loads(_VERSION_CHECK_CACHE.read_text())
205
+ except Exception:
206
+ data = _fetch_version_compatibility()
207
+ else:
208
+ data = _fetch_version_compatibility()
209
+
210
+ if data is None:
211
+ return # Offline / unreachable — don't block
212
+
213
+ try:
214
+ minimum = Version(data["minimum_version"])
215
+ except (KeyError, InvalidVersion):
216
+ return
217
+
218
+ blocked_versions = data.get("blocked_versions", [])
219
+ message = data.get("message")
220
+
221
+ reason = None
222
+ if version_str in blocked_versions:
223
+ reason = f"Version [bold red]{version_str}[/bold red] has been blocked due to a known issue."
224
+ elif installed < minimum:
225
+ reason = (
226
+ f"Version [bold red]{version_str}[/bold red] is below the minimum"
227
+ f" required version [bold cyan]{data['minimum_version']}[/bold cyan]."
228
+ )
229
+
230
+ if reason:
231
+ parts = [reason]
232
+ if message:
233
+ parts.append(f"\n{message}")
234
+ parts.append(
235
+ "\n\nTo upgrade:"
236
+ "\n • If using LbEnv from CVMFS, close and reopen your session to pick up the latest version"
237
+ "\n • Otherwise: [bold]pip install --upgrade LbAPLocal[/bold] or"
238
+ " [bold]conda update lbaplocal[/bold]"
239
+ )
240
+ console.print(
241
+ Panel(
242
+ "\n".join(parts),
243
+ title="⚠ Update Required",
244
+ border_style="yellow",
245
+ padding=(1, 2),
246
+ )
247
+ )
248
+ if not Confirm.ask("[yellow]Continue anyway?[/yellow]", default=False):
249
+ raise typer.Exit(code=1)
250
+
251
+
252
+ def _check_git_ssh() -> bool:
253
+ """Check if SSH access to gitlab.cern.ch (port 7999) works."""
254
+ try:
255
+ result = subprocess.run(
256
+ [
257
+ "ssh",
258
+ "-T",
259
+ "-o",
260
+ "ConnectTimeout=3",
261
+ "-o",
262
+ "StrictHostKeyChecking=accept-new",
263
+ "-p",
264
+ "7999",
265
+ "git@gitlab.cern.ch",
266
+ ],
267
+ capture_output=True,
268
+ timeout=5,
269
+ )
270
+ return result.returncode == 0
271
+ except Exception:
272
+ return False
273
+
274
+
161
275
  def _build_banner_content(version: str) -> str:
162
276
  """Build the full banner content with logo, tip, links, and context."""
163
277
  parts = [BANNER.format(version=version)]
@@ -189,6 +303,13 @@ def _build_banner_content(version: str) -> str:
189
303
  else "[dim]Proxy:[/dim] [red]✗[/red]"
190
304
  )
191
305
 
306
+ ssh = _check_git_ssh()
307
+ ctx_items.append(
308
+ "[dim]Git SSH:[/dim] [green]✓[/green]"
309
+ if ssh
310
+ else "[dim]Git SSH:[/dim] [red]✗[/red]"
311
+ )
312
+
192
313
  cwd = Path.cwd()
193
314
  try:
194
315
  display_dir = f"~/{cwd.relative_to(Path.home())}"
@@ -328,7 +449,14 @@ def check_kerberos_ticket() -> bool:
328
449
 
329
450
 
330
451
  def get_default_clone_type() -> CloneType:
331
- """Determine default clone type based on environment"""
452
+ """Determine default clone type based on environment.
453
+
454
+ Prefers SSH, then Kerberos, then prompts the user.
455
+ """
456
+ if _check_git_ssh():
457
+ console.print("[green]✓ Using SSH authentication (key detected)[/green]")
458
+ return CloneType.SSH
459
+
332
460
  if check_kerberos_ticket():
333
461
  console.print(
334
462
  "[green]✓ Using Kerberos authentication (ticket detected)[/green]"
@@ -336,7 +464,7 @@ def get_default_clone_type() -> CloneType:
336
464
  return CloneType.KRB5
337
465
 
338
466
  # Prompt user for preferred method
339
- console.print("\n[yellow]No Kerberos ticket detected[/yellow]")
467
+ console.print("\n[yellow]No SSH key or Kerberos ticket detected[/yellow]")
340
468
  console.print("Available clone methods:")
341
469
  console.print(" 1. SSH (requires SSH key configured)")
342
470
  console.print(" 2. HTTPS (requires username/password)")
@@ -472,6 +600,9 @@ def main(
472
600
  except importlib.metadata.PackageNotFoundError:
473
601
  lbaplocal_version = "dev"
474
602
 
603
+ # Check version compatibility (may exit if blocked)
604
+ _check_version_compatibility(lbaplocal_version)
605
+
475
606
  # Banner: always for "test", otherwise respect 1-hour cooldown
476
607
  command = ctx.invoked_subcommand
477
608
  show_banner = command in _ALWAYS_BANNER_COMMANDS or _stamp_expired(
@@ -3545,6 +3676,21 @@ def cwl_test(
3545
3676
  cmd.append("--verbose")
3546
3677
  cmd.extend(["--preserve-environment", "CMAKE_PREFIX_PATH"])
3547
3678
  cmd.extend(["--preserve-environment", "ANALYSIS_PRODUCTIONS_DYNAMIC"])
3679
+ # Preserve X509/XRootD authentication variables so that grid
3680
+ # authentication
3681
+ for var in [
3682
+ "X509_USER_PROXY",
3683
+ "X509_USER_CERT",
3684
+ "X509_USER_KEY",
3685
+ "X509_CERT_DIR",
3686
+ "X509_VOMS_DIR",
3687
+ "BEARER_TOKEN",
3688
+ "BEARER_TOKEN_FILE",
3689
+ "XrdSecPROTOCOL",
3690
+ ]:
3691
+ if var in os.environ:
3692
+ cmd.extend(["--preserve-environment", var])
3693
+
3548
3694
  if replica_map_path:
3549
3695
  cmd.extend(["--replica-map", str(replica_map_path)])
3550
3696
 
@@ -3563,6 +3709,19 @@ def cwl_test(
3563
3709
  console.print(
3564
3710
  f"[dim] ANALYSIS_PRODUCTIONS_DYNAMIC={custom_env.get('ANALYSIS_PRODUCTIONS_DYNAMIC', 'NOT SET')}[/dim]"
3565
3711
  )
3712
+ for var in [
3713
+ "X509_USER_PROXY",
3714
+ "X509_USER_CERT",
3715
+ "X509_USER_KEY",
3716
+ "X509_CERT_DIR",
3717
+ "X509_VOMS_DIR",
3718
+ "BEARER_TOKEN",
3719
+ "BEARER_TOKEN_FILE",
3720
+ "XrdSecPROTOCOL",
3721
+ ]:
3722
+ val = custom_env.get(var)
3723
+ if val:
3724
+ console.print(f"[dim] {var}={val}[/dim]")
3566
3725
 
3567
3726
  # Verify the fake siteroot structure was created
3568
3727
  ap_link = fake_siteroot / "DBASE" / "AnalysisProductions" / "v999999999999"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LbAPLocal
3
- Version: 0.9.7
3
+ Version: 0.9.9
4
4
  Summary: Tool to locally run tests for AnalysisProductions
5
5
  Author: LHCb
6
6
  Project-URL: Homepage, https://gitlab.cern.ch/lhcb-dpa/analysis-productions/lbaplocal
@@ -24,6 +24,7 @@ Requires-Dist: consolemd
24
24
  Requires-Dist: mplhep
25
25
  Requires-Dist: requests
26
26
  Requires-Dist: setuptools
27
+ Requires-Dist: packaging
27
28
  Requires-Dist: typer
28
29
  Requires-Dist: rich
29
30
  Requires-Dist: textual
@@ -9,6 +9,7 @@ consolemd
9
9
  mplhep
10
10
  requests
11
11
  setuptools
12
+ packaging
12
13
  typer
13
14
  rich
14
15
  textual
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes