lager-cli 0.28.0__tar.gz → 0.28.2__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 (221) hide show
  1. {lager_cli-0.28.0/lager_cli.egg-info → lager_cli-0.28.2}/PKG-INFO +1 -1
  2. {lager_cli-0.28.0 → lager_cli-0.28.2}/__init__.py +1 -1
  3. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/ssh.py +14 -1
  4. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/commands.py +13 -3
  5. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/service_client.py +7 -2
  6. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/devenv.py +354 -15
  7. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/exec_.py +38 -24
  8. {lager_cli-0.28.0 → lager_cli-0.28.2}/config.py +33 -0
  9. {lager_cli-0.28.0 → lager_cli-0.28.2/lager_cli.egg-info}/PKG-INFO +1 -1
  10. {lager_cli-0.28.0 → lager_cli-0.28.2}/LICENSE +0 -0
  11. {lager_cli-0.28.0 → lager_cli-0.28.2}/MANIFEST.in +0 -0
  12. {lager_cli-0.28.0 → lager_cli-0.28.2}/README.md +0 -0
  13. {lager_cli-0.28.0 → lager_cli-0.28.2}/__main__.py +0 -0
  14. {lager_cli-0.28.0 → lager_cli-0.28.2}/address_utils.py +0 -0
  15. {lager_cli-0.28.0 → lager_cli-0.28.2}/battery/__init__.py +0 -0
  16. {lager_cli-0.28.0 → lager_cli-0.28.2}/battery/battery_tui.py +0 -0
  17. {lager_cli-0.28.0 → lager_cli-0.28.2}/battery/websocket_client.py +0 -0
  18. {lager_cli-0.28.0 → lager_cli-0.28.2}/box_storage.py +0 -0
  19. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/__init__.py +0 -0
  20. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/__init__.py +0 -0
  21. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/_host_ops.py +0 -0
  22. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/_mount_prep.py +0 -0
  23. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/_pip_validation.py +0 -0
  24. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/_shim_verbs.py +0 -0
  25. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/_ssh.py +0 -0
  26. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/authorize.py +0 -0
  27. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/box_group.py +0 -0
  28. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/boxes.py +0 -0
  29. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/config.py +0 -0
  30. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/diagnose.py +0 -0
  31. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/dut.py +0 -0
  32. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/hello.py +0 -0
  33. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/instruments.py +0 -0
  34. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/labjack_pins.py +0 -0
  35. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/lock.py +0 -0
  36. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/net_tui.py +0 -0
  37. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/box/nets.py +0 -0
  38. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/__init__.py +0 -0
  39. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/ble.py +0 -0
  40. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/blufi.py +0 -0
  41. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/i2c.py +0 -0
  42. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/router.py +0 -0
  43. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/spi.py +0 -0
  44. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/uart.py +0 -0
  45. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/usb.py +0 -0
  46. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/websocket_client.py +0 -0
  47. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/communication/wifi.py +0 -0
  48. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/__init__.py +0 -0
  49. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/arm.py +0 -0
  50. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/__init__.py +0 -0
  51. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/gdb.py +0 -0
  52. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/net_cache.py +0 -0
  53. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/service_helper.py +0 -0
  54. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/debug/tunnel.py +0 -0
  55. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/development/python.py +0 -0
  56. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/__init__.py +0 -0
  57. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/adc.py +0 -0
  58. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/dac.py +0 -0
  59. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/energy.py +0 -0
  60. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/gpi.py +0 -0
  61. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/gpo.py +0 -0
  62. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/logic.py +0 -0
  63. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/scope.py +0 -0
  64. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/thermocouple.py +0 -0
  65. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/measurement/watt.py +0 -0
  66. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/power/__init__.py +0 -0
  67. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/power/battery.py +0 -0
  68. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/power/eload.py +0 -0
  69. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/power/solar.py +0 -0
  70. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/power/supply.py +0 -0
  71. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/__init__.py +0 -0
  72. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/binaries.py +0 -0
  73. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/defaults.py +0 -0
  74. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/install.py +0 -0
  75. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/install_wheel.py +0 -0
  76. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/logs.py +0 -0
  77. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/uninstall.py +0 -0
  78. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/update.py +0 -0
  79. {lager_cli-0.28.0 → lager_cli-0.28.2}/commands/utility/webcam.py +0 -0
  80. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/__init__.py +0 -0
  81. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/ci_detection.py +0 -0
  82. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/constants.py +0 -0
  83. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/core.py +0 -0
  84. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/error_handlers.py +0 -0
  85. {lager_cli-0.28.0 → lager_cli-0.28.2}/context/session.py +0 -0
  86. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/__init__.py +0 -0
  87. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/group_usage.py +0 -0
  88. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/matchers.py +0 -0
  89. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/net_group.py +0 -0
  90. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/net_helpers.py +0 -0
  91. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/net_storage.py +0 -0
  92. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/param_types.py +0 -0
  93. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/ssh_utils.py +0 -0
  94. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/utils.py +0 -0
  95. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/version_skew.py +0 -0
  96. {lager_cli-0.28.0 → lager_cli-0.28.2}/core/ws_diagnose.py +0 -0
  97. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/__init__.py +0 -0
  98. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/scripts/__init__.py +0 -0
  99. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/scripts/convert_to_sparse_checkout.sh +0 -0
  100. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/scripts/setup_and_deploy_box.sh +0 -0
  101. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/scripts/setup_ssh_key.sh +0 -0
  102. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/security/__init__.py +0 -0
  103. {lager_cli-0.28.0 → lager_cli-0.28.2}/deployment/security/secure_box_firewall.sh +0 -0
  104. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/__init__.py +0 -0
  105. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/common/__init__.py +0 -0
  106. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/common/construct_utils.py +0 -0
  107. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/common/exceptions.py +0 -0
  108. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/common/py3compat.py +0 -0
  109. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/common/utils.py +0 -0
  110. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/construct/__init__.py +0 -0
  111. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/construct/adapters.py +0 -0
  112. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/construct/core.py +0 -0
  113. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/construct/debug.py +0 -0
  114. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/construct/macros.py +0 -0
  115. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/__init__.py +0 -0
  116. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/abbrevtable.py +0 -0
  117. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/aranges.py +0 -0
  118. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/callframe.py +0 -0
  119. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/compileunit.py +0 -0
  120. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/constants.py +0 -0
  121. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/descriptions.py +0 -0
  122. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/die.py +0 -0
  123. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/dwarf_expr.py +0 -0
  124. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/dwarfinfo.py +0 -0
  125. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/enums.py +0 -0
  126. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/lineprogram.py +0 -0
  127. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/locationlists.py +0 -0
  128. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/namelut.py +0 -0
  129. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/ranges.py +0 -0
  130. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/dwarf/structs.py +0 -0
  131. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/ehabi/__init__.py +0 -0
  132. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/ehabi/constants.py +0 -0
  133. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/ehabi/decoder.py +0 -0
  134. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/ehabi/ehabiinfo.py +0 -0
  135. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/ehabi/structs.py +0 -0
  136. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/__init__.py +0 -0
  137. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/constants.py +0 -0
  138. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/descriptions.py +0 -0
  139. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/dynamic.py +0 -0
  140. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/elffile.py +0 -0
  141. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/enums.py +0 -0
  142. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/gnuversions.py +0 -0
  143. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/hash.py +0 -0
  144. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/notes.py +0 -0
  145. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/relocation.py +0 -0
  146. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/sections.py +0 -0
  147. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/segments.py +0 -0
  148. {lager_cli-0.28.0 → lager_cli-0.28.2}/elftools/elf/structs.py +0 -0
  149. {lager_cli-0.28.0 → lager_cli-0.28.2}/errors.py +0 -0
  150. {lager_cli-0.28.0 → lager_cli-0.28.2}/exceptions.py +0 -0
  151. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/__init__.py +0 -0
  152. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/box_config.py +0 -0
  153. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/__init__.py +0 -0
  154. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/ble.py +0 -0
  155. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/blufi.py +0 -0
  156. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/i2c.py +0 -0
  157. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/router.py +0 -0
  158. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/spi.py +0 -0
  159. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/uart.py +0 -0
  160. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/communication/wifi.py +0 -0
  161. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/custom_devices.py +0 -0
  162. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/device/__init__.py +0 -0
  163. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/device/arm.py +0 -0
  164. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/device/hello.py +0 -0
  165. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/device/usb.py +0 -0
  166. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/device/webcam.py +0 -0
  167. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/__init__.py +0 -0
  168. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/adc.py +0 -0
  169. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/dac.py +0 -0
  170. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/energy.py +0 -0
  171. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/gpio.py +0 -0
  172. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/scope.py +0 -0
  173. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/scope_stream.py +0 -0
  174. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/thermocouple.py +0 -0
  175. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/measurement/watt.py +0 -0
  176. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/net.py +0 -0
  177. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/__init__.py +0 -0
  178. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/battery.py +0 -0
  179. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/eload.py +0 -0
  180. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/enable_disable.py +0 -0
  181. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/solar.py +0 -0
  182. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/power/supply.py +0 -0
  183. {lager_cli-0.28.0 → lager_cli-0.28.2}/impl/query_instruments.py +0 -0
  184. {lager_cli-0.28.0 → lager_cli-0.28.2}/lager_cli.egg-info/SOURCES.txt +0 -0
  185. {lager_cli-0.28.0 → lager_cli-0.28.2}/lager_cli.egg-info/dependency_links.txt +0 -0
  186. {lager_cli-0.28.0 → lager_cli-0.28.2}/lager_cli.egg-info/entry_points.txt +0 -0
  187. {lager_cli-0.28.0 → lager_cli-0.28.2}/lager_cli.egg-info/requires.txt +0 -0
  188. {lager_cli-0.28.0 → lager_cli-0.28.2}/lager_cli.egg-info/top_level.txt +0 -0
  189. {lager_cli-0.28.0 → lager_cli-0.28.2}/main.py +0 -0
  190. {lager_cli-0.28.0 → lager_cli-0.28.2}/pyproject.toml +0 -0
  191. {lager_cli-0.28.0 → lager_cli-0.28.2}/safe_unpickle.py +0 -0
  192. {lager_cli-0.28.0 → lager_cli-0.28.2}/setup.cfg +0 -0
  193. {lager_cli-0.28.0 → lager_cli-0.28.2}/setup.py +0 -0
  194. {lager_cli-0.28.0 → lager_cli-0.28.2}/simple_hdlc.py +0 -0
  195. {lager_cli-0.28.0 → lager_cli-0.28.2}/sort_utils.py +0 -0
  196. {lager_cli-0.28.0 → lager_cli-0.28.2}/status.py +0 -0
  197. {lager_cli-0.28.0 → lager_cli-0.28.2}/supply/__init__.py +0 -0
  198. {lager_cli-0.28.0 → lager_cli-0.28.2}/supply/supply_tui.py +0 -0
  199. {lager_cli-0.28.0 → lager_cli-0.28.2}/supply/websocket_client.py +0 -0
  200. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/__init__.py +0 -0
  201. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/core/__init__.py +0 -0
  202. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/core/executor.py +0 -0
  203. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/main.py +0 -0
  204. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/__init__.py +0 -0
  205. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/completer.py +0 -0
  206. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/display.py +0 -0
  207. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/logo.py +0 -0
  208. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/repl.py +0 -0
  209. {lager_cli-0.28.0 → lager_cli-0.28.2}/terminal/ui/themes.py +0 -0
  210. {lager_cli-0.28.0 → lager_cli-0.28.2}/tests/test_box_lager_imports.py +0 -0
  211. {lager_cli-0.28.0 → lager_cli-0.28.2}/tests/test_box_storage.py +0 -0
  212. {lager_cli-0.28.0 → lager_cli-0.28.2}/tests/test_io_imports.py +0 -0
  213. {lager_cli-0.28.0 → lager_cli-0.28.2}/update_check.py +0 -0
  214. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRC16.py +0 -0
  215. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRC16DNP.py +0 -0
  216. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRC16Kermit.py +0 -0
  217. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRC16SICK.py +0 -0
  218. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRC32.py +0 -0
  219. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/CRCCCITT.py +0 -0
  220. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/PyCRC/__init__.py +0 -0
  221. {lager_cli-0.28.0 → lager_cli-0.28.2}/vendor/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: lager-cli
3
- Version: 0.28.0
3
+ Version: 0.28.2
4
4
  Summary: Lager CLI - Box and Docker connectivity
5
5
  Home-page: https://github.com/lagerdata/lager
6
6
  Author: Lager Data
@@ -7,4 +7,4 @@ Lager CLI
7
7
  A Command Line Interface for Lager Data
8
8
  """
9
9
 
10
- __version__ = '0.28.0'
10
+ __version__ = '0.28.2'
@@ -8,10 +8,12 @@
8
8
  """
9
9
  import click
10
10
  from click.exceptions import Exit
11
+ import os
11
12
  import subprocess
12
13
  import platform
13
14
  from ...box_storage import resolve_and_validate_box
14
15
  from ...context import get_default_box
16
+ from ._ssh import _LAGER_BOX_KEY
15
17
 
16
18
 
17
19
  def _get_ssh_install_hint() -> str:
@@ -62,10 +64,21 @@ def ssh(ctx, box):
62
64
  # Build SSH command
63
65
  ssh_host = f'{username}@{resolved_box}'
64
66
 
67
+ # Offer the dedicated lager_box key (installed by `lager authorize`) when
68
+ # present. It isn't one of ssh's default identity filenames, so without
69
+ # -i it's never tried and an authorized box still drops to a password
70
+ # prompt. -i appends to the identity list rather than replacing it (no
71
+ # IdentitiesOnly), so default keys and the password fallback still work
72
+ # for boxes that haven't been authorized.
73
+ ssh_cmd = ['ssh']
74
+ if os.path.exists(_LAGER_BOX_KEY):
75
+ ssh_cmd.extend(['-i', _LAGER_BOX_KEY])
76
+ ssh_cmd.append(ssh_host)
77
+
65
78
  try:
66
79
  # Run SSH as a child process (not exec) so Click ctx.call_on_close hooks run.
67
80
  try:
68
- ret = subprocess.call(['ssh', ssh_host])
81
+ ret = subprocess.call(ssh_cmd)
69
82
  except KeyboardInterrupt:
70
83
  ret = 130
71
84
  ctx.exit(ret if ret is not None else 0)
@@ -1274,8 +1274,18 @@ def reset(ctx, box, halt, force_reconnect):
1274
1274
  help='Halt the device during memory read (keeps debugger connected).')
1275
1275
  @click.option('--no-halt', 'no_halt', is_flag=True, default=False,
1276
1276
  help='Leave the target running; overrides auto-halt for DA1469 QSPI XIP.')
1277
- def memrd(ctx, start_addr, length, box, json_output, halt, no_halt):
1278
- """Read memory from target (DA1469x QSPI XIP: auto-halts unless --no-halt)."""
1277
+ @click.option('--no-reset', 'no_reset', is_flag=True, default=False,
1278
+ help='DA1469x only: skip the reset+halt the box performs before the read. '
1279
+ 'A running DA1469x has SWD disabled, so without reset the read fails; '
1280
+ 'use this only on a blank/awake part to avoid rebooting it.')
1281
+ def memrd(ctx, start_addr, length, box, json_output, halt, no_halt, no_reset):
1282
+ """Read memory from target.
1283
+
1284
+ DA1469x QSPI XIP auto-halts unless --no-halt. For any DA1469x device the box
1285
+ also resets+halts the target before reading (real firmware disables SWD and
1286
+ deep-sleeps, so a plain read fails) — this REBOOTS the DUT. Pass --no-reset
1287
+ to skip that step (only safe on a blank/awake part).
1288
+ """
1279
1289
 
1280
1290
  target_box = box
1281
1291
 
@@ -1361,7 +1371,7 @@ def memrd(ctx, start_addr, length, box, json_output, halt, no_halt):
1361
1371
  ctx.exit(0)
1362
1372
 
1363
1373
  try:
1364
- memory_data = client.read_memory(debug_net, start_addr, length)
1374
+ memory_data = client.read_memory(debug_net, start_addr, length, no_reset=no_reset)
1365
1375
  except Exception as e:
1366
1376
  error_msg = str(e)
1367
1377
  if "400" in error_msg or "No debugger connection" in error_msg:
@@ -196,13 +196,18 @@ class DebugServiceClient:
196
196
  return response.json()
197
197
 
198
198
  def read_memory(self, net: Dict[str, Any], start_addr: int,
199
- length: int = 256) -> bytes:
200
- """Read memory from target."""
199
+ length: int = 256, no_reset: bool = False) -> bytes:
200
+ """Read memory from target.
201
+
202
+ no_reset: DA1469x only — skip the box-side reset+halt before the read.
203
+ """
201
204
  data = {
202
205
  'net': net,
203
206
  'start_addr': start_addr,
204
207
  'length': length,
205
208
  }
209
+ if no_reset:
210
+ data['no_reset'] = True
206
211
 
207
212
  response = self.session.post(
208
213
  f'{self.base_url}/debug/memrd',
@@ -16,6 +16,7 @@ from pathlib import Path
16
16
  import click
17
17
 
18
18
  from ...core.group_usage import LagerGroup
19
+ from ...core.param_types import EnvVarType
19
20
  from ...sort_utils import natural_sort_key
20
21
  from ...config import (
21
22
  read_lager_json,
@@ -25,6 +26,8 @@ from ...config import (
25
26
  make_config_path,
26
27
  LAGER_CONFIG_FILE_NAME,
27
28
  get_global_config_file_path,
29
+ devenv_config_list,
30
+ expand_devenv_path,
28
31
  )
29
32
 
30
33
 
@@ -69,6 +72,50 @@ def _validate_docker_image_name(image):
69
72
  return True
70
73
 
71
74
 
75
+ def _print_terminal_info(config_path, args):
76
+ """Print the resolved docker command and a readable breakdown of it."""
77
+ def collect(*flags):
78
+ out = []
79
+ for i, tok in enumerate(args[:-1]):
80
+ if tok in flags:
81
+ out.append(args[i + 1])
82
+ return out
83
+
84
+ click.secho('Resolved docker command:', fg='green')
85
+ click.echo(f' {" ".join(args)}')
86
+ click.echo()
87
+ click.echo(f'Config: {config_path}')
88
+ click.echo(f'Image: {args[-1] if args else ""}')
89
+
90
+ mounts = collect('-v')
91
+ if mounts:
92
+ click.echo('Mounts:')
93
+ for item in mounts:
94
+ click.echo(f' {item}')
95
+
96
+ envs = collect('-e', '--env') + [t.split('=', 1)[1] for t in args if t.startswith('--env=')]
97
+ if envs:
98
+ click.echo('Environment:')
99
+ for item in envs:
100
+ click.echo(f' {item}')
101
+
102
+ ports = collect('-p')
103
+ if ports:
104
+ click.echo('Ports:')
105
+ for item in ports:
106
+ click.echo(f' {item}')
107
+
108
+ networks = [t.split('=', 1)[1] for t in args if t.startswith('--network=')]
109
+ if networks:
110
+ click.echo(f'Network: {networks[0]}')
111
+ platforms = collect('--platform')
112
+ if platforms:
113
+ click.echo(f'Platform: {platforms[0]}')
114
+
115
+ click.echo()
116
+ click.secho('(--info: nothing was launched)', fg='yellow')
117
+
118
+
72
119
  @devenv.command()
73
120
  @click.pass_context
74
121
  @click.option('--image', prompt='Docker image', default='lagerdata/devenv-cortexm', show_default=True, help='Docker image name')
@@ -143,7 +190,11 @@ def create(ctx, image, mount_dir, shell):
143
190
  @click.option('--platform', help='Platform', required=False)
144
191
  @click.option('--attach', '-a', 'attach_container', help='Attach to a running container by name', required=False, default=None)
145
192
  @click.option('--shell', '-s', help='Shell to use when attaching (default: config shell or /bin/bash)', required=False, default=None)
146
- def terminal(ctx, mount, user, group, name, detach, port, entrypoint, network, platform, attach_container, shell):
193
+ @click.option('--volume', '-v', 'volumes', help='Bind-mount a host path into the container (HOST:CONTAINER[:ro]). Repeatable.', required=False, multiple=True)
194
+ @click.option('--env', '-e', help='Set an environment variable in the container (FOO=BAR). Repeatable.', required=False, multiple=True, type=EnvVarType())
195
+ @click.option('--passenv', help='Pass an environment variable through from the current environment. Repeatable.', required=False, multiple=True)
196
+ @click.option('--info', is_flag=True, default=False, help='Show the resolved docker command and config, then exit without launching.')
197
+ def terminal(ctx, mount, user, group, name, detach, port, entrypoint, network, platform, attach_container, shell, volumes, env, passenv, info):
147
198
  """
148
199
  Start interactive terminal
149
200
  """
@@ -188,25 +239,27 @@ def terminal(ctx, mount, user, group, name, detach, port, entrypoint, network, p
188
239
 
189
240
  repo_root_relative_path = devenv_config.get('repo_root_relative_path')
190
241
 
191
- if 'user' in devenv_config:
192
- user = devenv_config['user']
193
- if 'group' in devenv_config:
194
- group = devenv_config['group']
195
- macaddr = None
196
- if 'macaddr' in devenv_config:
197
- macaddr = devenv_config['macaddr']
198
-
199
- hostname = None
200
- if 'hostname' in devenv_config:
201
- hostname = devenv_config['hostname']
242
+ # Precedence: CLI flag wins, else fall back to the .lager value.
243
+ if user is None:
244
+ user = devenv_config.get('user')
245
+ if group is None:
246
+ group = devenv_config.get('group')
247
+ macaddr = devenv_config.get('macaddr')
248
+ hostname = devenv_config.get('hostname')
202
249
 
250
+ entrypoint = entrypoint or devenv_config.get('entrypoint')
203
251
  if entrypoint:
204
252
  args.extend(['--entrypoint', entrypoint])
205
253
 
254
+ # docker run has no `--group` flag; user and group are combined into `--user user:group`
255
+ # (matching `lager exec`). A group with no user yields `--user :group`.
256
+ user_group = ''
206
257
  if user:
207
- args.extend(['--user', user])
258
+ user_group += user
208
259
  if group:
209
- args.extend(['--group', group])
260
+ user_group += f':{group}'
261
+ if user_group:
262
+ args.extend(['--user', user_group])
210
263
  if macaddr:
211
264
  args.extend(['--mac-address', macaddr])
212
265
  if hostname:
@@ -229,11 +282,16 @@ def terminal(ctx, mount, user, group, name, detach, port, entrypoint, network, p
229
282
  if name:
230
283
  args.extend(['--name', name])
231
284
 
285
+ # network / platform: CLI flag wins, else fall back to the .lager value.
286
+ network = network or devenv_config.get('network')
232
287
  if network:
233
288
  args.append(f'--network={network}')
234
289
 
235
- args.extend(itertools.chain(*zip(itertools.repeat('-p'), port)))
290
+ # ports: config-defined first, then any -p passed on the command line.
291
+ all_ports = [*devenv_config_list(devenv_config.get('ports')), *port]
292
+ args.extend(itertools.chain(*zip(itertools.repeat('-p'), all_ports)))
236
293
 
294
+ platform = platform or devenv_config.get('platform')
237
295
  if platform:
238
296
  args.extend(['--platform', platform])
239
297
 
@@ -250,10 +308,27 @@ def terminal(ctx, mount, user, group, name, detach, port, entrypoint, network, p
250
308
  f'{global_config_path}:/lager/{LAGER_CONFIG_FILE_NAME}'
251
309
  ])
252
310
 
311
+ # Custom bind mounts: config-defined first, then any passed on the command line.
312
+ # Specs may use ~, environment variables, and ${PROJECT_ROOT} for portability.
313
+ project_root = os.path.dirname(path)
314
+ for vol in (*devenv_config_list(devenv_config.get('volumes')), *volumes):
315
+ args.extend(['-v', expand_devenv_path(vol, project_root)])
316
+
317
+ # Environment variables: config-defined, then --env, then --passenv passthrough.
318
+ for var in (*devenv_config_list(devenv_config.get('environment')), *env):
319
+ args.extend(['--env', var])
320
+ for var_name in passenv:
321
+ if var_name in os.environ:
322
+ args.extend(['--env', f'{var_name}={os.environ[var_name]}'])
323
+
253
324
  args.extend(['-w', working_dir])
254
325
 
255
326
  args.append(image)
256
327
 
328
+ if info:
329
+ _print_terminal_info(path, args)
330
+ ctx.exit(0)
331
+
257
332
  try:
258
333
  proc = subprocess.run(args, check=False)
259
334
  except FileNotFoundError:
@@ -381,3 +456,267 @@ def commands():
381
456
  for name, command in sorted(cmds.items(), key=lambda x: natural_sort_key(x[0])):
382
457
  click.secho(name, fg='green', nl=False)
383
458
  click.echo(f': {command}')
459
+
460
+
461
+ def _load_devenv_section():
462
+ """Return (path, data, section) for the project DEVENV config, or exit."""
463
+ path, data = get_devenv_json()
464
+ if 'DEVENV' not in data:
465
+ click.secho(f'No devenv configuration found in {path}', fg='red', err=True)
466
+ click.echo('Please run `lager devenv create` first to set up your development environment.', err=True)
467
+ click.get_current_context().exit(1)
468
+ return path, data, data['DEVENV']
469
+
470
+
471
+ def _save_devenv(path, data):
472
+ """Write devenv config back to disk with consistent error handling."""
473
+ try:
474
+ write_lager_json(data, path)
475
+ except PermissionError:
476
+ click.secho(f'Error: Permission denied writing to {path}', fg='red', err=True)
477
+ click.echo('Make sure the file is writable.', err=True)
478
+ click.get_current_context().exit(1)
479
+ except Exception as e:
480
+ click.secho(f'Error writing to {path}: {e}', fg='red', err=True)
481
+ click.get_current_context().exit(1)
482
+
483
+
484
+ @devenv.group(name='mount')
485
+ def mount_group():
486
+ """
487
+ Manage persistent bind-mounts / volumes for the devenv container
488
+ """
489
+ pass
490
+
491
+
492
+ @mount_group.command(name='add')
493
+ @click.argument('spec')
494
+ def mount_add(spec):
495
+ """
496
+ Add a volume SPEC to the DEVENV `volumes` list.
497
+
498
+ SPEC is in Docker `-v` form: HOST:CONTAINER[:ro] for a host bind-mount,
499
+ or NAME:CONTAINER for a named volume.
500
+ """
501
+ spec = spec.strip()
502
+ if ':' not in spec:
503
+ click.secho(
504
+ f'Error: invalid volume "{spec}". Expected HOST:CONTAINER[:ro] '
505
+ '(e.g. /host/path:/container/path or myvol:/data).',
506
+ fg='red', err=True)
507
+ click.get_current_context().exit(1)
508
+
509
+ path, data, section = _load_devenv_section()
510
+ volumes = devenv_config_list(section.get('volumes'))
511
+ if spec in volumes:
512
+ click.echo(f'Volume already configured: {spec}')
513
+ return
514
+ volumes.append(spec)
515
+ section['volumes'] = volumes
516
+ _save_devenv(path, data)
517
+ click.echo(f'Added volume: {spec}')
518
+
519
+
520
+ @mount_group.command(name='remove')
521
+ @click.argument('spec')
522
+ def mount_remove(spec):
523
+ """
524
+ Remove a volume SPEC from the DEVENV `volumes` list.
525
+ """
526
+ spec = spec.strip()
527
+ path, data, section = _load_devenv_section()
528
+ volumes = devenv_config_list(section.get('volumes'))
529
+ if spec not in volumes:
530
+ click.secho(f'Volume not found: {spec}', fg='red', err=True)
531
+ click.get_current_context().exit(1)
532
+ volumes = [v for v in volumes if v != spec]
533
+ if volumes:
534
+ section['volumes'] = volumes
535
+ else:
536
+ section.pop('volumes', None)
537
+ _save_devenv(path, data)
538
+ click.echo(f'Removed volume: {spec}')
539
+
540
+
541
+ @mount_group.command(name='list')
542
+ def mount_list():
543
+ """
544
+ List configured volumes.
545
+ """
546
+ _, _, section = _load_devenv_section()
547
+ volumes = devenv_config_list(section.get('volumes'))
548
+ if not volumes:
549
+ click.echo('No volumes configured')
550
+ return
551
+ for vol in volumes:
552
+ click.echo(vol)
553
+
554
+
555
+ @devenv.group(name='env')
556
+ def env_group():
557
+ """
558
+ Manage persistent environment variables for the devenv container
559
+ """
560
+ pass
561
+
562
+
563
+ @env_group.command(name='set')
564
+ @click.argument('assignment', type=EnvVarType())
565
+ def env_set(assignment):
566
+ """
567
+ Set an environment variable ASSIGNMENT (FOO=BAR) in the DEVENV
568
+ `environment` list. Replaces any existing value for the same variable.
569
+ """
570
+ name = assignment.split('=', 1)[0]
571
+ path, data, section = _load_devenv_section()
572
+ environment = devenv_config_list(section.get('environment'))
573
+
574
+ new_env = []
575
+ replaced = False
576
+ for entry in environment:
577
+ if entry.split('=', 1)[0] == name:
578
+ if not replaced:
579
+ new_env.append(assignment)
580
+ replaced = True
581
+ # drop any duplicate entries for the same variable
582
+ else:
583
+ new_env.append(entry)
584
+ if not replaced:
585
+ new_env.append(assignment)
586
+
587
+ section['environment'] = new_env
588
+ _save_devenv(path, data)
589
+ click.echo(f'Set environment variable: {assignment}')
590
+
591
+
592
+ @env_group.command(name='unset')
593
+ @click.argument('name')
594
+ def env_unset(name):
595
+ """
596
+ Remove environment variable NAME from the DEVENV `environment` list.
597
+ """
598
+ path, data, section = _load_devenv_section()
599
+ environment = devenv_config_list(section.get('environment'))
600
+ remaining = [e for e in environment if e.split('=', 1)[0] != name]
601
+ if len(remaining) == len(environment):
602
+ click.secho(f'Environment variable not found: {name}', fg='red', err=True)
603
+ click.get_current_context().exit(1)
604
+ if remaining:
605
+ section['environment'] = remaining
606
+ else:
607
+ section.pop('environment', None)
608
+ _save_devenv(path, data)
609
+ click.echo(f'Unset environment variable: {name}')
610
+
611
+
612
+ @env_group.command(name='list')
613
+ def env_list():
614
+ """
615
+ List configured environment variables.
616
+ """
617
+ _, _, section = _load_devenv_section()
618
+ environment = devenv_config_list(section.get('environment'))
619
+ if not environment:
620
+ click.echo('No environment variables configured')
621
+ return
622
+ for entry in environment:
623
+ click.echo(entry)
624
+
625
+
626
+ # Scalar DEVENV keys settable via `lager devenv set` (replace semantics).
627
+ SETTABLE_SCALAR_KEYS = (
628
+ 'image', 'mount_dir', 'shell', 'user', 'group', 'entrypoint',
629
+ 'hostname', 'macaddr', 'network', 'platform', 'repo_root_relative_path',
630
+ )
631
+ # List keys with no dedicated editor; `set` appends to these.
632
+ SETTABLE_APPEND_KEYS = ('ports',)
633
+ # Convenience singular aliases accepted on the command line.
634
+ DEVENV_KEY_ALIASES = {'port': 'ports'}
635
+ # List keys that have richer dedicated editors.
636
+ LIST_KEYS_WITH_EDITORS = {
637
+ 'volumes': 'lager devenv mount',
638
+ 'environment': 'lager devenv env',
639
+ }
640
+
641
+
642
+ @devenv.command(name='set')
643
+ @click.argument('key')
644
+ @click.argument('value')
645
+ def set_(key, value):
646
+ """
647
+ Set a DEVENV configuration KEY to VALUE.
648
+
649
+ Scalar keys (image, mount_dir, shell, user, group, hostname, macaddr,
650
+ network, platform, repo_root_relative_path) are replaced. The `ports`
651
+ list key is appended to. Use `lager devenv mount` / `lager devenv env`
652
+ for volumes / environment.
653
+ """
654
+ ctx = click.get_current_context()
655
+ key = DEVENV_KEY_ALIASES.get(key, key)
656
+ if key in LIST_KEYS_WITH_EDITORS:
657
+ click.secho(
658
+ f'Error: edit `{key}` with `{LIST_KEYS_WITH_EDITORS[key]}`.',
659
+ fg='red', err=True)
660
+ ctx.exit(1)
661
+ if key not in SETTABLE_SCALAR_KEYS and key not in SETTABLE_APPEND_KEYS:
662
+ valid = ', '.join(SETTABLE_SCALAR_KEYS + SETTABLE_APPEND_KEYS)
663
+ click.secho(f'Error: unknown devenv key "{key}". Valid keys: {valid}.', fg='red', err=True)
664
+ ctx.exit(1)
665
+ if key == 'image' and not _validate_docker_image_name(value):
666
+ click.secho(f"Error: Invalid Docker image name: '{value}'", fg='red', err=True)
667
+ ctx.exit(1)
668
+
669
+ path, data, section = _load_devenv_section()
670
+ if key in SETTABLE_APPEND_KEYS:
671
+ values = devenv_config_list(section.get(key))
672
+ if value in values:
673
+ click.echo(f'{key} already contains: {value}')
674
+ return
675
+ values.append(value)
676
+ section[key] = values
677
+ _save_devenv(path, data)
678
+ click.echo(f'Added {key}: {value}')
679
+ else:
680
+ section[key] = value
681
+ _save_devenv(path, data)
682
+ click.echo(f'Set {key} = {value}')
683
+
684
+
685
+ @devenv.command(name='unset')
686
+ @click.argument('key')
687
+ def unset(key):
688
+ """
689
+ Remove a DEVENV configuration KEY entirely (scalar or list).
690
+ """
691
+ key = DEVENV_KEY_ALIASES.get(key, key)
692
+ path, data, section = _load_devenv_section()
693
+ if key not in section:
694
+ click.secho(f'devenv key not set: {key}', fg='red', err=True)
695
+ click.get_current_context().exit(1)
696
+ del section[key]
697
+ _save_devenv(path, data)
698
+ click.echo(f'Unset {key}')
699
+
700
+
701
+ @devenv.command(name='show')
702
+ def show():
703
+ """
704
+ Print the resolved DEVENV configuration.
705
+ """
706
+ path, _, section = _load_devenv_section()
707
+ scalars = {k: v for k, v in section.items()
708
+ if not k.startswith('cmd.') and not isinstance(v, (list, tuple))}
709
+ lists = {k: v for k, v in section.items() if isinstance(v, (list, tuple))}
710
+ cmds = {k.split('.', 1)[1]: v for k, v in section.items() if k.startswith('cmd.')}
711
+
712
+ click.secho(f'DEVENV ({path})', fg='green')
713
+ for key in sorted(scalars):
714
+ click.echo(f' {key}: {scalars[key]}')
715
+ for key in sorted(lists):
716
+ click.echo(f' {key}:')
717
+ for item in devenv_config_list(lists[key]):
718
+ click.echo(f' - {item}')
719
+ if cmds:
720
+ click.echo(' commands:')
721
+ for name in sorted(cmds, key=natural_sort_key):
722
+ click.echo(f' {name}: {cmds[name]}')
@@ -15,11 +15,11 @@ import subprocess
15
15
  import platform
16
16
  from pathlib import Path
17
17
  import click
18
- from ...config import get_devenv_json, write_lager_json, LAGER_CONFIG_FILE_NAME, get_global_config_file_path
18
+ from ...config import get_devenv_json, write_lager_json, LAGER_CONFIG_FILE_NAME, get_global_config_file_path, devenv_config_list, expand_devenv_path
19
19
  from ...core.param_types import EnvVarType
20
20
 
21
21
 
22
- def _run_command_local(section, path, cmd_to_run, mount, extra_args, debug, interactive, tty, user, group, env, passenv):
22
+ def _run_command_local(section, path, cmd_to_run, mount, extra_args, debug, interactive, tty, user, group, env, passenv, volumes=()):
23
23
  """
24
24
  Run a command locally in a Docker container
25
25
  """
@@ -94,8 +94,24 @@ def _run_command_local(section, path, cmd_to_run, mount, extra_args, debug, inte
94
94
  if hostname:
95
95
  base_command.append(f'--hostname={hostname}')
96
96
 
97
- # Add custom environment variables
98
- for env_var in env:
97
+ # Network / platform / ports from config (no CLI flags on exec for these).
98
+ network = section.get('network')
99
+ if network:
100
+ base_command.append(f'--network={network}')
101
+ for port in devenv_config_list(section.get('ports')):
102
+ base_command.extend(['-p', port])
103
+ platform = section.get('platform')
104
+ if platform:
105
+ base_command.extend(['--platform', platform])
106
+
107
+ # Custom bind mounts: config-defined first, then any passed on the command line.
108
+ # Specs may use ~, environment variables, and ${PROJECT_ROOT} for portability.
109
+ project_root = os.path.dirname(path)
110
+ for vol in (*devenv_config_list(section.get('volumes')), *volumes):
111
+ base_command.extend(['-v', expand_devenv_path(vol, project_root)])
112
+
113
+ # Add custom environment variables: config-defined first, then --env.
114
+ for env_var in (*devenv_config_list(section.get('environment')), *env):
99
115
  base_command.append(f'--env={env_var}')
100
116
 
101
117
  # Pass through environment variables
@@ -149,12 +165,13 @@ def _run_command_local(section, path, cmd_to_run, mount, extra_args, debug, inte
149
165
  '--passenv',
150
166
  multiple=True, help='Environment variable to inherit from current environment')
151
167
  @click.option('--mount', '-m', help='Name of volume to mount', required=False)
168
+ @click.option('--volume', 'volumes', help='Bind-mount a host path into the container (HOST:CONTAINER[:ro]). Repeatable.', required=False, multiple=True)
152
169
  @click.option('--interactive/--no-interactive', '-i', is_flag=True, help='Keep STDIN open even if not attached', default=True, show_default=True)
153
170
  @click.option('--tty/--no-tty', '-t', is_flag=True, help='Allocate a pseudo-TTY', default=True, show_default=True)
154
171
  @click.option('--user', '-u', help='User to run as in container', default=None)
155
172
  @click.option('--group', '-g', help='Group to run as in container', default=None)
156
173
  @click.option('--verbose', '-v', is_flag=True, help='Show verbose output including the full docker command')
157
- def exec_(ctx, cmd_name, extra_args, command, save_as, warn, env, passenv, mount, interactive, tty, user, group, verbose):
174
+ def exec_(ctx, cmd_name, extra_args, command, save_as, warn, env, passenv, mount, volumes, interactive, tty, user, group, verbose):
158
175
  """
159
176
  Execute COMMAND in a docker container locally. COMMAND is a named command which was previously saved using `--save-as`.
160
177
  If COMMAND is not provided, execute the command specified by --command. If --save-as is also provided,
@@ -165,19 +182,6 @@ def exec_(ctx, cmd_name, extra_args, command, save_as, warn, env, passenv, mount
165
182
  click.echo(exec_.get_help(ctx))
166
183
  ctx.exit(0)
167
184
 
168
- # Default user and group to current user if not specified
169
- if user is None:
170
- try:
171
- user = str(os.getuid())
172
- except AttributeError:
173
- pass
174
-
175
- if group is None:
176
- try:
177
- group = str(os.getgid())
178
- except AttributeError:
179
- pass
180
-
181
185
  # Get DEVENV configuration
182
186
  path, data = get_devenv_json()
183
187
 
@@ -188,11 +192,21 @@ def exec_(ctx, cmd_name, extra_args, command, save_as, warn, env, passenv, mount
188
192
 
189
193
  section = data['DEVENV']
190
194
 
191
- # Override user/group from config if specified
192
- if 'user' in section:
193
- user = section['user']
194
- if 'group' in section:
195
- group = section['group']
195
+ # Precedence: CLI flag wins, then .lager config, then the current uid/gid.
196
+ if user is None:
197
+ user = section.get('user')
198
+ if group is None:
199
+ group = section.get('group')
200
+ if user is None:
201
+ try:
202
+ user = str(os.getuid())
203
+ except AttributeError:
204
+ pass
205
+ if group is None:
206
+ try:
207
+ group = str(os.getgid())
208
+ except AttributeError:
209
+ pass
196
210
 
197
211
  # Check for both command name and command string
198
212
  if cmd_name and command:
@@ -237,6 +251,6 @@ def exec_(ctx, cmd_name, extra_args, command, save_as, warn, env, passenv, mount
237
251
  # Run the command locally in Docker
238
252
  returncode = _run_command_local(
239
253
  section, path, cmd_to_run, mount, extra_args,
240
- ctx.obj.debug or verbose, interactive, tty, user, group, env, passenv
254
+ ctx.obj.debug or verbose, interactive, tty, user, group, env, passenv, volumes
241
255
  )
242
256
  ctx.exit(returncode)