mnenv 0.1.0 → 0.1.1

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 (303) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/PROPOSAL.md +197 -0
  4. data/README.adoc +168 -461
  5. data/Rakefile +7 -4
  6. data/bin/Install-Mnenv.ps1 +145 -0
  7. data/bin/mnenv-installer +72 -0
  8. data/completions/bash +47 -0
  9. data/completions/fish +29 -0
  10. data/completions/powershell.ps1 +94 -0
  11. data/completions/zsh +43 -0
  12. data/lib/mnenv/binary_repository.rb +189 -0
  13. data/lib/mnenv/chocolatey.rb +7 -0
  14. data/lib/mnenv/cli.rb +110 -10
  15. data/lib/mnenv/commands/available_command.rb +169 -0
  16. data/lib/mnenv/commands/chocolatey_command.rb +4 -5
  17. data/lib/mnenv/commands/gemfile_command.rb +4 -5
  18. data/lib/mnenv/commands/homebrew_command.rb +4 -5
  19. data/lib/mnenv/commands/install_command.rb +234 -0
  20. data/lib/mnenv/commands/snap_command.rb +5 -7
  21. data/lib/mnenv/commands/uninstall_command.rb +42 -0
  22. data/lib/mnenv/commands/version_command.rb +213 -0
  23. data/lib/mnenv/commands.rb +9 -4
  24. data/lib/mnenv/gemfile/extractor.rb +10 -3
  25. data/lib/mnenv/gemfile.rb +8 -0
  26. data/lib/mnenv/gemfile_repository.rb +0 -2
  27. data/lib/mnenv/homebrew.rb +7 -0
  28. data/lib/mnenv/installer/base.rb +62 -0
  29. data/lib/mnenv/installer/factory.rb +17 -0
  30. data/lib/mnenv/installer.rb +12 -0
  31. data/lib/mnenv/installers/binary_installer.rb +242 -0
  32. data/lib/mnenv/installers/gemfile_installer.rb +76 -0
  33. data/lib/mnenv/json_formatter.rb +3 -13
  34. data/lib/mnenv/logger.rb +9 -1
  35. data/lib/mnenv/models/binary_version.rb +78 -0
  36. data/lib/mnenv/models/chocolatey_version.rb +7 -0
  37. data/lib/mnenv/models/gemfile_version.rb +19 -5
  38. data/lib/mnenv/models/homebrew_version.rb +7 -0
  39. data/lib/mnenv/models/snap_version.rb +8 -0
  40. data/lib/mnenv/models/version.rb +16 -0
  41. data/lib/mnenv/models.rb +7 -5
  42. data/lib/mnenv/paths.rb +69 -0
  43. data/lib/mnenv/repository.rb +50 -35
  44. data/lib/mnenv/resolver +72 -0
  45. data/lib/mnenv/shells/base.rb +32 -0
  46. data/lib/mnenv/shells/bash.rb +72 -0
  47. data/lib/mnenv/shells/cmd.rb +108 -0
  48. data/lib/mnenv/shells/factory.rb +82 -0
  49. data/lib/mnenv/shells/power_shell.rb +110 -0
  50. data/lib/mnenv/shim_manager.rb +121 -0
  51. data/lib/mnenv/snap.rb +7 -0
  52. data/lib/mnenv/snap_repository.rb +2 -19
  53. data/lib/mnenv/source_registry.rb +69 -0
  54. data/lib/mnenv/version.rb +1 -1
  55. data/lib/mnenv/versions_manager.rb +92 -0
  56. data/lib/mnenv.rb +3 -0
  57. data/mnenv.gemspec +4 -1
  58. data/scripts/cross-source-switching-test.sh +214 -0
  59. data/scripts/integration-test.sh +89 -0
  60. data/scripts/version-switching-test.sh +151 -0
  61. metadata +82 -247
  62. data/data/chocolatey/versions.yaml +0 -812
  63. data/data/gemfile/v1.1.6/Gemfile +0 -4
  64. data/data/gemfile/v1.1.6/Gemfile.lock.archived +0 -232
  65. data/data/gemfile/v1.1.7/Gemfile +0 -4
  66. data/data/gemfile/v1.1.7/Gemfile.lock.archived +0 -235
  67. data/data/gemfile/v1.1.8/Gemfile +0 -4
  68. data/data/gemfile/v1.1.8/Gemfile.lock.archived +0 -238
  69. data/data/gemfile/v1.10.0/Gemfile +0 -5
  70. data/data/gemfile/v1.10.0/Gemfile.lock.archived +0 -930
  71. data/data/gemfile/v1.10.1/Gemfile +0 -5
  72. data/data/gemfile/v1.10.1/Gemfile.lock.archived +0 -929
  73. data/data/gemfile/v1.10.10/Gemfile +0 -5
  74. data/data/gemfile/v1.10.10/Gemfile.lock.archived +0 -973
  75. data/data/gemfile/v1.10.11/Gemfile +0 -5
  76. data/data/gemfile/v1.10.11/Gemfile.lock.archived +0 -975
  77. data/data/gemfile/v1.10.2/Gemfile +0 -5
  78. data/data/gemfile/v1.10.2/Gemfile.lock.archived +0 -939
  79. data/data/gemfile/v1.10.3/Gemfile +0 -5
  80. data/data/gemfile/v1.10.3/Gemfile.lock.archived +0 -946
  81. data/data/gemfile/v1.10.5/Gemfile +0 -5
  82. data/data/gemfile/v1.10.5/Gemfile.lock.archived +0 -958
  83. data/data/gemfile/v1.10.6/Gemfile +0 -5
  84. data/data/gemfile/v1.10.6/Gemfile.lock.archived +0 -969
  85. data/data/gemfile/v1.10.7/Gemfile +0 -5
  86. data/data/gemfile/v1.10.7/Gemfile.lock.archived +0 -969
  87. data/data/gemfile/v1.10.8/Gemfile +0 -5
  88. data/data/gemfile/v1.10.8/Gemfile.lock.archived +0 -968
  89. data/data/gemfile/v1.10.9/Gemfile +0 -5
  90. data/data/gemfile/v1.10.9/Gemfile.lock.archived +0 -972
  91. data/data/gemfile/v1.11.0/Gemfile +0 -5
  92. data/data/gemfile/v1.11.0/Gemfile.lock.archived +0 -971
  93. data/data/gemfile/v1.11.1/Gemfile +0 -5
  94. data/data/gemfile/v1.11.1/Gemfile.lock.archived +0 -975
  95. data/data/gemfile/v1.11.4/Gemfile +0 -5
  96. data/data/gemfile/v1.11.4/Gemfile.lock.archived +0 -1046
  97. data/data/gemfile/v1.11.5/Gemfile +0 -5
  98. data/data/gemfile/v1.11.5/Gemfile.lock.archived +0 -1047
  99. data/data/gemfile/v1.12.10/Gemfile +0 -3
  100. data/data/gemfile/v1.12.10/Gemfile.lock.archived +0 -1073
  101. data/data/gemfile/v1.12.3/Gemfile +0 -3
  102. data/data/gemfile/v1.12.3/Gemfile.lock.archived +0 -1050
  103. data/data/gemfile/v1.12.4/Gemfile +0 -3
  104. data/data/gemfile/v1.12.4/Gemfile.lock.archived +0 -1056
  105. data/data/gemfile/v1.12.5/Gemfile +0 -3
  106. data/data/gemfile/v1.12.5/Gemfile.lock.archived +0 -1054
  107. data/data/gemfile/v1.12.6/Gemfile +0 -3
  108. data/data/gemfile/v1.12.6/Gemfile.lock.archived +0 -1056
  109. data/data/gemfile/v1.12.8/Gemfile +0 -3
  110. data/data/gemfile/v1.12.8/Gemfile.lock.archived +0 -1063
  111. data/data/gemfile/v1.13.0/Gemfile +0 -3
  112. data/data/gemfile/v1.13.0/Gemfile.lock.archived +0 -1074
  113. data/data/gemfile/v1.13.2/Gemfile +0 -3
  114. data/data/gemfile/v1.13.2/Gemfile.lock.archived +0 -899
  115. data/data/gemfile/v1.13.3/Gemfile +0 -3
  116. data/data/gemfile/v1.13.3/Gemfile.lock.archived +0 -938
  117. data/data/gemfile/v1.13.4/Gemfile +0 -3
  118. data/data/gemfile/v1.13.4/Gemfile.lock.archived +0 -938
  119. data/data/gemfile/v1.13.5/Gemfile +0 -3
  120. data/data/gemfile/v1.13.5/Gemfile.lock.archived +0 -944
  121. data/data/gemfile/v1.13.7/Gemfile +0 -3
  122. data/data/gemfile/v1.13.7/Gemfile.lock.archived +0 -944
  123. data/data/gemfile/v1.13.8/Gemfile +0 -3
  124. data/data/gemfile/v1.13.8/Gemfile.lock.archived +0 -944
  125. data/data/gemfile/v1.13.9/Gemfile +0 -3
  126. data/data/gemfile/v1.13.9/Gemfile.lock.archived +0 -956
  127. data/data/gemfile/v1.14.3/Gemfile +0 -3
  128. data/data/gemfile/v1.14.3/Gemfile.lock.archived +0 -950
  129. data/data/gemfile/v1.2.12/Gemfile +0 -3
  130. data/data/gemfile/v1.2.12/Gemfile.lock.archived +0 -283
  131. data/data/gemfile/v1.2.2/Gemfile +0 -4
  132. data/data/gemfile/v1.2.2/Gemfile.lock.archived +0 -224
  133. data/data/gemfile/v1.2.3/Gemfile +0 -4
  134. data/data/gemfile/v1.2.3/Gemfile.lock.archived +0 -231
  135. data/data/gemfile/v1.2.6/Gemfile +0 -4
  136. data/data/gemfile/v1.2.6/Gemfile.lock.archived +0 -239
  137. data/data/gemfile/v1.2.8/Gemfile +0 -4
  138. data/data/gemfile/v1.2.8/Gemfile.lock.archived +0 -233
  139. data/data/gemfile/v1.2.9/Gemfile +0 -4
  140. data/data/gemfile/v1.2.9/Gemfile.lock.archived +0 -245
  141. data/data/gemfile/v1.3.1/Gemfile +0 -3
  142. data/data/gemfile/v1.3.1/Gemfile.lock.archived +0 -296
  143. data/data/gemfile/v1.3.2/Gemfile +0 -3
  144. data/data/gemfile/v1.3.2/Gemfile.lock.archived +0 -296
  145. data/data/gemfile/v1.3.4/Gemfile +0 -3
  146. data/data/gemfile/v1.3.4/Gemfile.lock.archived +0 -284
  147. data/data/gemfile/v1.3.5/Gemfile +0 -3
  148. data/data/gemfile/v1.3.5/Gemfile.lock.archived +0 -284
  149. data/data/gemfile/v1.3.6/Gemfile +0 -3
  150. data/data/gemfile/v1.3.6/Gemfile.lock.archived +0 -286
  151. data/data/gemfile/v1.3.9/Gemfile +0 -3
  152. data/data/gemfile/v1.3.9/Gemfile.lock.archived +0 -334
  153. data/data/gemfile/v1.4.0/Gemfile +0 -3
  154. data/data/gemfile/v1.4.0/Gemfile.lock.archived +0 -330
  155. data/data/gemfile/v1.4.10/Gemfile +0 -4
  156. data/data/gemfile/v1.4.10/Gemfile.lock.archived +0 -461
  157. data/data/gemfile/v1.4.11/Gemfile +0 -4
  158. data/data/gemfile/v1.4.11/Gemfile.lock.archived +0 -452
  159. data/data/gemfile/v1.4.12/Gemfile +0 -4
  160. data/data/gemfile/v1.4.12/Gemfile.lock.archived +0 -452
  161. data/data/gemfile/v1.4.13/Gemfile +0 -4
  162. data/data/gemfile/v1.4.13/Gemfile.lock.archived +0 -455
  163. data/data/gemfile/v1.4.14/Gemfile +0 -4
  164. data/data/gemfile/v1.4.14/Gemfile.lock.archived +0 -456
  165. data/data/gemfile/v1.4.18/Gemfile +0 -3
  166. data/data/gemfile/v1.4.18/Gemfile.lock.archived +0 -486
  167. data/data/gemfile/v1.4.3/Gemfile +0 -3
  168. data/data/gemfile/v1.4.3/Gemfile.lock.archived +0 -339
  169. data/data/gemfile/v1.4.4/Gemfile +0 -3
  170. data/data/gemfile/v1.4.4/Gemfile.lock.archived +0 -339
  171. data/data/gemfile/v1.4.5/Gemfile +0 -3
  172. data/data/gemfile/v1.4.5/Gemfile.lock.archived +0 -348
  173. data/data/gemfile/v1.4.6/Gemfile +0 -3
  174. data/data/gemfile/v1.4.6/Gemfile.lock.archived +0 -357
  175. data/data/gemfile/v1.4.7/Gemfile +0 -3
  176. data/data/gemfile/v1.4.7/Gemfile.lock.archived +0 -391
  177. data/data/gemfile/v1.4.8/Gemfile +0 -3
  178. data/data/gemfile/v1.4.8/Gemfile.lock.archived +0 -445
  179. data/data/gemfile/v1.4.9/Gemfile +0 -3
  180. data/data/gemfile/v1.4.9/Gemfile.lock.archived +0 -448
  181. data/data/gemfile/v1.5.0/Gemfile +0 -3
  182. data/data/gemfile/v1.5.0/Gemfile.lock.archived +0 -478
  183. data/data/gemfile/v1.5.10/Gemfile +0 -3
  184. data/data/gemfile/v1.5.10/Gemfile.lock.archived +0 -668
  185. data/data/gemfile/v1.5.11/Gemfile +0 -3
  186. data/data/gemfile/v1.5.11/Gemfile.lock.archived +0 -668
  187. data/data/gemfile/v1.5.15/Gemfile +0 -3
  188. data/data/gemfile/v1.5.15/Gemfile.lock.archived +0 -686
  189. data/data/gemfile/v1.5.16/Gemfile +0 -3
  190. data/data/gemfile/v1.5.16/Gemfile.lock.archived +0 -684
  191. data/data/gemfile/v1.5.17/Gemfile +0 -3
  192. data/data/gemfile/v1.5.17/Gemfile.lock.archived +0 -684
  193. data/data/gemfile/v1.5.18/Gemfile +0 -5
  194. data/data/gemfile/v1.5.18/Gemfile.lock.archived +0 -691
  195. data/data/gemfile/v1.5.19/Gemfile +0 -5
  196. data/data/gemfile/v1.5.19/Gemfile.lock.archived +0 -703
  197. data/data/gemfile/v1.5.20/Gemfile +0 -5
  198. data/data/gemfile/v1.5.20/Gemfile.lock.archived +0 -703
  199. data/data/gemfile/v1.5.21/Gemfile +0 -5
  200. data/data/gemfile/v1.5.21/Gemfile.lock.archived +0 -707
  201. data/data/gemfile/v1.5.22/Gemfile +0 -5
  202. data/data/gemfile/v1.5.22/Gemfile.lock.archived +0 -707
  203. data/data/gemfile/v1.5.23/Gemfile +0 -5
  204. data/data/gemfile/v1.5.23/Gemfile.lock.archived +0 -711
  205. data/data/gemfile/v1.5.24/Gemfile +0 -5
  206. data/data/gemfile/v1.5.24/Gemfile.lock.archived +0 -711
  207. data/data/gemfile/v1.5.3/Gemfile +0 -3
  208. data/data/gemfile/v1.5.3/Gemfile.lock.archived +0 -651
  209. data/data/gemfile/v1.5.4/Gemfile +0 -3
  210. data/data/gemfile/v1.5.4/Gemfile.lock.archived +0 -657
  211. data/data/gemfile/v1.5.5/Gemfile +0 -3
  212. data/data/gemfile/v1.5.5/Gemfile.lock.archived +0 -657
  213. data/data/gemfile/v1.5.6/Gemfile +0 -3
  214. data/data/gemfile/v1.5.6/Gemfile.lock.archived +0 -657
  215. data/data/gemfile/v1.5.7/Gemfile +0 -3
  216. data/data/gemfile/v1.5.7/Gemfile.lock.archived +0 -657
  217. data/data/gemfile/v1.5.8/Gemfile +0 -3
  218. data/data/gemfile/v1.5.8/Gemfile.lock.archived +0 -655
  219. data/data/gemfile/v1.5.9/Gemfile +0 -3
  220. data/data/gemfile/v1.5.9/Gemfile.lock.archived +0 -656
  221. data/data/gemfile/v1.6.1/Gemfile +0 -5
  222. data/data/gemfile/v1.6.1/Gemfile.lock.archived +0 -721
  223. data/data/gemfile/v1.6.10/Gemfile +0 -5
  224. data/data/gemfile/v1.6.10/Gemfile.lock.archived +0 -744
  225. data/data/gemfile/v1.6.11/Gemfile +0 -5
  226. data/data/gemfile/v1.6.11/Gemfile.lock.archived +0 -744
  227. data/data/gemfile/v1.6.12/Gemfile +0 -5
  228. data/data/gemfile/v1.6.12/Gemfile.lock.archived +0 -745
  229. data/data/gemfile/v1.6.13/Gemfile +0 -5
  230. data/data/gemfile/v1.6.13/Gemfile.lock.archived +0 -745
  231. data/data/gemfile/v1.6.14/Gemfile +0 -5
  232. data/data/gemfile/v1.6.14/Gemfile.lock.archived +0 -754
  233. data/data/gemfile/v1.6.15/Gemfile +0 -5
  234. data/data/gemfile/v1.6.15/Gemfile.lock.archived +0 -757
  235. data/data/gemfile/v1.6.2/Gemfile +0 -5
  236. data/data/gemfile/v1.6.2/Gemfile.lock.archived +0 -718
  237. data/data/gemfile/v1.6.3/Gemfile +0 -5
  238. data/data/gemfile/v1.6.3/Gemfile.lock.archived +0 -728
  239. data/data/gemfile/v1.6.4/Gemfile +0 -5
  240. data/data/gemfile/v1.6.4/Gemfile.lock.archived +0 -730
  241. data/data/gemfile/v1.6.5/Gemfile +0 -5
  242. data/data/gemfile/v1.6.5/Gemfile.lock.archived +0 -733
  243. data/data/gemfile/v1.6.6/Gemfile +0 -5
  244. data/data/gemfile/v1.6.6/Gemfile.lock.archived +0 -733
  245. data/data/gemfile/v1.6.7/Gemfile +0 -5
  246. data/data/gemfile/v1.6.7/Gemfile.lock.archived +0 -733
  247. data/data/gemfile/v1.6.9/Gemfile +0 -5
  248. data/data/gemfile/v1.6.9/Gemfile.lock.archived +0 -744
  249. data/data/gemfile/v1.7.0/Gemfile +0 -5
  250. data/data/gemfile/v1.7.0/Gemfile.lock.archived +0 -750
  251. data/data/gemfile/v1.7.1/Gemfile +0 -5
  252. data/data/gemfile/v1.7.1/Gemfile.lock.archived +0 -750
  253. data/data/gemfile/v1.7.2/Gemfile +0 -5
  254. data/data/gemfile/v1.7.2/Gemfile.lock.archived +0 -747
  255. data/data/gemfile/v1.7.3/Gemfile +0 -5
  256. data/data/gemfile/v1.7.3/Gemfile.lock.archived +0 -755
  257. data/data/gemfile/v1.7.4/Gemfile +0 -5
  258. data/data/gemfile/v1.7.4/Gemfile.lock.archived +0 -756
  259. data/data/gemfile/v1.7.5/Gemfile +0 -5
  260. data/data/gemfile/v1.7.5/Gemfile.lock.archived +0 -759
  261. data/data/gemfile/v1.7.6/Gemfile +0 -5
  262. data/data/gemfile/v1.7.6/Gemfile.lock.archived +0 -768
  263. data/data/gemfile/v1.8.10/Gemfile +0 -5
  264. data/data/gemfile/v1.8.10/Gemfile.lock.archived +0 -792
  265. data/data/gemfile/v1.8.11/Gemfile +0 -5
  266. data/data/gemfile/v1.8.11/Gemfile.lock.archived +0 -862
  267. data/data/gemfile/v1.8.3/Gemfile +0 -5
  268. data/data/gemfile/v1.8.3/Gemfile.lock.archived +0 -773
  269. data/data/gemfile/v1.8.4/Gemfile +0 -5
  270. data/data/gemfile/v1.8.4/Gemfile.lock.archived +0 -768
  271. data/data/gemfile/v1.8.5/Gemfile +0 -5
  272. data/data/gemfile/v1.8.5/Gemfile.lock.archived +0 -768
  273. data/data/gemfile/v1.8.6/Gemfile +0 -5
  274. data/data/gemfile/v1.8.6/Gemfile.lock.archived +0 -777
  275. data/data/gemfile/v1.8.7/Gemfile +0 -5
  276. data/data/gemfile/v1.8.7/Gemfile.lock.archived +0 -777
  277. data/data/gemfile/v1.8.8/Gemfile +0 -5
  278. data/data/gemfile/v1.8.8/Gemfile.lock.archived +0 -778
  279. data/data/gemfile/v1.8.9/Gemfile +0 -5
  280. data/data/gemfile/v1.8.9/Gemfile.lock.archived +0 -775
  281. data/data/gemfile/v1.9.0/Gemfile +0 -5
  282. data/data/gemfile/v1.9.0/Gemfile.lock.archived +0 -871
  283. data/data/gemfile/v1.9.1/Gemfile +0 -5
  284. data/data/gemfile/v1.9.1/Gemfile.lock.archived +0 -906
  285. data/data/gemfile/v1.9.2/Gemfile +0 -5
  286. data/data/gemfile/v1.9.2/Gemfile.lock.archived +0 -898
  287. data/data/gemfile/v1.9.3/Gemfile +0 -5
  288. data/data/gemfile/v1.9.3/Gemfile.lock.archived +0 -898
  289. data/data/gemfile/v1.9.4/Gemfile +0 -5
  290. data/data/gemfile/v1.9.4/Gemfile.lock.archived +0 -901
  291. data/data/gemfile/v1.9.5/Gemfile +0 -5
  292. data/data/gemfile/v1.9.5/Gemfile.lock.archived +0 -903
  293. data/data/gemfile/v1.9.6/Gemfile +0 -5
  294. data/data/gemfile/v1.9.6/Gemfile.lock.archived +0 -900
  295. data/data/gemfile/v1.9.7/Gemfile +0 -5
  296. data/data/gemfile/v1.9.7/Gemfile.lock.archived +0 -922
  297. data/data/gemfile/v1.9.8/Gemfile +0 -5
  298. data/data/gemfile/v1.9.8/Gemfile.lock.archived +0 -933
  299. data/data/gemfile/versions.yaml +0 -751
  300. data/data/homebrew/versions.yaml +0 -567
  301. data/data/snap/github_tags.json +0 -42
  302. data/data/snap/versions.yaml +0 -589
  303. data/snapcraft-list-copied-from-site.md +0 -101
@@ -2,16 +2,32 @@
2
2
 
3
3
  require 'yaml'
4
4
  require 'fileutils'
5
+ require_relative 'versions_manager'
5
6
 
6
7
  module Mnenv
7
8
  class Repository
9
+ # Default versions manager (shared across all repositories)
10
+ class << self
11
+ def versions_manager
12
+ @versions_manager ||= VersionsManager.new
13
+ end
14
+
15
+ attr_writer :versions_manager
16
+
17
+ # Force update the versions data
18
+ def update_versions
19
+ versions_manager.update
20
+ end
21
+ end
22
+
8
23
  attr_reader :data_dir, :versions_file_path
9
24
 
10
- def initialize(data_dir: nil)
11
- @data_dir = data_dir || default_data_dir
25
+ def initialize(data_dir: nil, update: false)
26
+ @data_dir = data_dir || cli_data_dir || default_data_dir
12
27
  @versions_file_path = File.join(@data_dir, 'versions.yaml')
13
28
  @versions_cache = {}
14
- load if File.file?(versions_file_path)
29
+ @update = update
30
+ load
15
31
  end
16
32
 
17
33
  def find(version_number) = @versions_cache[version_number]
@@ -37,22 +53,29 @@ module Mnenv
37
53
  protected
38
54
 
39
55
  def load
40
- data = YAML.load_file(versions_file_path)
56
+ data = fetch_versions_data
41
57
  return if data.nil? || data['versions'].nil?
42
58
 
43
59
  data['versions'].each do |version_hash|
44
60
  version = version_class.new(version_hash)
45
- @versions_cache[version.version] = version
61
+ cache_version(version)
46
62
  end
47
63
  end
48
64
 
65
+ # Override in subclasses for custom caching (e.g., SnapRepository uses composite keys)
66
+ def cache_version(version)
67
+ @versions_cache[version.version] = version
68
+ end
69
+
49
70
  def persist
50
- FileUtils.mkdir_p(data_dir)
71
+ return unless @data_dir
72
+
73
+ FileUtils.mkdir_p(@data_dir)
51
74
  output = {
52
75
  'metadata' => metadata,
53
- 'versions' => @versions_cache.values.sort.map { |v| version_to_hash(v) }
76
+ 'versions' => @versions_cache.values.sort.map(&:to_hash)
54
77
  }
55
- File.write(versions_file_path, output.to_yaml)
78
+ File.write(@versions_file_path, output.to_yaml)
56
79
  end
57
80
 
58
81
  def metadata
@@ -68,38 +91,30 @@ module Mnenv
68
91
 
69
92
  def source_name = raise NotImplementedError
70
93
 
71
- def default_data_dir = File.join(__dir__, '..', '..', 'data', source_name.to_s)
72
-
73
94
  private
74
95
 
75
- def version_to_hash(version)
76
- hash = {
77
- 'version' => version.version,
78
- 'published_at' => format_timestamp(version.published_at),
79
- 'parsed_at' => format_timestamp(version.parsed_at)
80
- }
96
+ def cli_data_dir
97
+ # Check if a data directory was specified via CLI option
98
+ return nil unless defined?(Mnenv::Cli) && Mnenv::Cli.data_dir
81
99
 
82
- # Add subclass-specific fields
83
- case version
84
- when GemfileVersion
85
- hash['gemfile_exists'] = version.gemfile_exists
86
- hash['gemfile_path'] = version.gemfile_path
87
- hash['gemfile_lock_path'] = version.gemfile_lock_path
88
- when SnapVersion
89
- hash['revision'] = version.revision
90
- hash['arch'] = version.arch
91
- hash['channel'] = version.channel
92
- when HomebrewVersion
93
- hash['tag_name'] = version.tag_name
94
- hash['commit_sha'] = version.commit_sha
95
- when ChocolateyVersion
96
- hash['package_name'] = version.package_name
97
- hash['is_pre_release'] = version.is_pre_release
98
- end
100
+ source_dir = Mnenv::Cli.data_dir
101
+ # If the CLI data-dir is specified, use it directly with the source name
102
+ File.join(source_dir, source_name.to_s)
103
+ end
99
104
 
100
- hash
105
+ def default_data_dir
106
+ # Use the versions manager to ensure data is available
107
+ data_path = self.class.versions_manager.ensure_versions_data(update: @update)
108
+ File.join(data_path, source_name.to_s)
101
109
  end
102
110
 
103
- def format_timestamp(t) = t&.strftime('%Y-%m-%dT%H:%M:%SZ')
111
+ def fetch_versions_data
112
+ return unless File.file?(@versions_file_path)
113
+
114
+ YAML.safe_load(File.read(@versions_file_path), permitted_classes: [Time, Symbol, Date])
115
+ rescue StandardError => e
116
+ warn "Warning: Failed to load versions from #{@versions_file_path}: #{e.message}"
117
+ nil
118
+ end
104
119
  end
105
120
  end
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # Version and source resolver for mnenv shims
3
+ # Usage: ./resolver "version" | ./resolver "source"
4
+
5
+ set -e
6
+
7
+ export MNENV_ROOT="${MNENV_ROOT:-$HOME/.mnenv}"
8
+
9
+ resolve_version() {
10
+ # 1. Check METANORMA_VERSION environment variable
11
+ if [ -n "$METANORMA_VERSION" ]; then
12
+ echo "$METANORMA_VERSION"
13
+ return 0
14
+ fi
15
+
16
+ # 2. Check .metanorma-version file (walk up directory tree)
17
+ local dir="$PWD"
18
+ while [ -n "$dir" ] && [ "$dir" != "/" ]; do
19
+ if [ -f "$dir/.metanorma-version" ]; then
20
+ cat "$dir/.metanorma-version"
21
+ return 0
22
+ fi
23
+ dir="${dir%/*}"
24
+ done
25
+
26
+ # 3. Check ~/.mnenv/version (global default)
27
+ if [ -f "$MNENV_ROOT/version" ]; then
28
+ cat "$MNENV_ROOT/version"
29
+ return 0
30
+ fi
31
+
32
+ # 4. No version found
33
+ return 1
34
+ }
35
+
36
+ resolve_source() {
37
+ # 1. Check METANORMA_SOURCE environment variable
38
+ if [ -n "$METANORMA_SOURCE" ]; then
39
+ echo "$METANORMA_SOURCE"
40
+ return 0
41
+ fi
42
+
43
+ # 2. Check .metanorma-source file (walk up directory tree)
44
+ local dir="$PWD"
45
+ while [ -n "$dir" ] && [ "$dir" != "/" ]; do
46
+ if [ -f "$dir/.metanorma-source" ]; then
47
+ cat "$dir/.metanorma-source"
48
+ return 0
49
+ fi
50
+ dir="${dir%/*}"
51
+ done
52
+
53
+ # 3. Check ~/.mnenv/source (global default)
54
+ if [ -f "$MNENV_ROOT/source" ]; then
55
+ cat "$MNENV_ROOT/source"
56
+ return 0
57
+ fi
58
+
59
+ # 4. Default to gemfile (faster for devs)
60
+ echo "gemfile"
61
+ return 0
62
+ }
63
+
64
+ # Main dispatch
65
+ case "$1" in
66
+ version) resolve_version ;;
67
+ source) resolve_source ;;
68
+ *)
69
+ echo "Usage: $0 {version|source}" >&2
70
+ exit 1
71
+ ;;
72
+ esac
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ module Shells
5
+ # Base class for shell-specific behavior
6
+ class BaseShell
7
+ def name
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def shim_extension
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def shim_content(executable_name)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def use_output(version, source)
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def config_file
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def windows?
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Mnenv
6
+ module Shells
7
+ # Bash shell implementation (also works for sh, dash, zsh)
8
+ class BashShell < BaseShell
9
+ def name
10
+ 'bash'
11
+ end
12
+
13
+ def shim_extension
14
+ '' # No extension for bash scripts
15
+ end
16
+
17
+ def shim_content(executable_name)
18
+ <<~SHIM
19
+ #!/bin/bash
20
+ set -e
21
+
22
+ export MNENV_ROOT="${MNENV_ROOT:-$HOME/.mnenv}"
23
+
24
+ # Resolve version and source using mnenv resolver
25
+ VERSION="$("$MNENV_ROOT/lib/mnenv/resolver" "version")"
26
+ SOURCE="$("$MNENV_ROOT/lib/mnenv/resolver" "source")"
27
+
28
+ if [ -z "$VERSION" ]; then
29
+ echo "mnenv: version not set or invalid" >&2
30
+ echo "Set a version with: mnenv global <version> or mnenv local <version>" >&2
31
+ exit 1
32
+ fi
33
+
34
+ if [ -z "$SOURCE" ]; then
35
+ echo "mnenv: source not set or invalid" >&2
36
+ exit 1
37
+ fi
38
+
39
+ # Determine executable path based on source
40
+ # Installed versions are in $MNENV_ROOT/installed/<version>-<source>/
41
+ if [ "$SOURCE" = "binary" ]; then
42
+ # Binary: single self-contained binary
43
+ EXECUTABLE="$MNENV_ROOT/installed/$VERSION-$SOURCE/metanorma"
44
+ else
45
+ # Gemfile: binstub from bundle install
46
+ EXECUTABLE="$MNENV_ROOT/installed/$VERSION-$SOURCE/bin/#{executable_name}"
47
+ fi
48
+
49
+ if [ ! -f "$EXECUTABLE" ]; then
50
+ echo "mnenv: #{executable_name} not installed for version $VERSION (source: $SOURCE)" >&2
51
+ echo "Install it with: mnenv install $VERSION --source $SOURCE" >&2
52
+ exit 1
53
+ fi
54
+
55
+ exec "$EXECUTABLE" "$@"
56
+ SHIM
57
+ end
58
+
59
+ def use_output(version, source)
60
+ lines = []
61
+ lines << "export METANORMA_VERSION=#{version}"
62
+ lines << "export METANORMA_SOURCE=#{source}" if source
63
+ lines << "# Run this in your shell, or use: eval \"$(mnenv use #{version}#{" --source #{source}" if source})\""
64
+ lines.join("\n")
65
+ end
66
+
67
+ def config_file
68
+ File.expand_path('~/.bashrc')
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Mnenv
6
+ module Shells
7
+ # CMD/Batch shell implementation (Windows)
8
+ class CmdShell < BaseShell
9
+ def name
10
+ 'cmd'
11
+ end
12
+
13
+ def shim_extension
14
+ '.bat'
15
+ end
16
+
17
+ def shim_content(executable_name)
18
+ <<~SHIM
19
+ @echo off
20
+ rem mnenv shim for CMD
21
+
22
+ if defined MNENV_ROOT (
23
+ set "MNENV_ROOT=%MNENV_ROOT%"
24
+ ) else (
25
+ set "MNENV_ROOT=%USERPROFILE%\\.mnenv"
26
+ )
27
+
28
+ rem Resolve version and source
29
+ set "VERSION="
30
+ set "SOURCE="
31
+
32
+ rem Check environment variables first
33
+ if defined METANORMA_VERSION set "VERSION=%METANORMA_VERSION%"
34
+ if defined METANORMA_SOURCE set "SOURCE=%METANORMA_SOURCE%"
35
+
36
+ rem Check local .metanorma-version file
37
+ if not defined VERSION (
38
+ set "DIR=%CD%"
39
+ :loop
40
+ if exist "%DIR%\\.metanorma-version" (
41
+ set /p VERSION=<"%DIR%\\.metanorma-version"
42
+ goto :found_version
43
+ )
44
+ for %%I in ("%DIR%\\..") do set "PARENT=%%~fI"
45
+ if "%PARENT%"=="%DIR%" goto :check_global
46
+ set "DIR=%PARENT%"
47
+ goto :loop
48
+ )
49
+
50
+ :check_global
51
+ if not defined VERSION (
52
+ if exist "%MNENV_ROOT%\\version" (
53
+ set /p VERSION=<"%MNENV_ROOT%\\version"
54
+ )
55
+ )
56
+
57
+ :found_version
58
+ if not defined SOURCE (
59
+ if exist "%MNENV_ROOT%\\source" (
60
+ set /p SOURCE=<"%MNENV_ROOT%\\source"
61
+ )
62
+ )
63
+
64
+ rem Default source
65
+ if not defined SOURCE set "SOURCE=gemfile"
66
+
67
+ if not defined VERSION (
68
+ echo mnenv: version not set or invalid 1>&2
69
+ echo Set a version with: mnenv global ^<version^> or mnenv local ^<version^> 1>&2
70
+ exit /b 1
71
+ )
72
+
73
+ rem Determine executable path based on source
74
+ rem Installed versions are in %MNENV_ROOT%\\installed\\<version>-<source>\\
75
+ if "%SOURCE%"=="binary" (
76
+ set "EXECUTABLE=%MNENV_ROOT%\\installed\\%VERSION%-%SOURCE%\\metanorma.exe"
77
+ ) else (
78
+ set "EXECUTABLE=%MNENV_ROOT%\\installed\\%VERSION%-%SOURCE%\\bin\\#{executable_name}.cmd"
79
+ )
80
+
81
+ if not exist "%EXECUTABLE%" (
82
+ echo mnenv: #{executable_name} not installed for version %VERSION% ^(source: %SOURCE%^) 1>&2
83
+ echo Install it with: mnenv install %VERSION% --source %SOURCE% 1>&2
84
+ exit /b 1
85
+ )
86
+
87
+ call "%EXECUTABLE%" %*
88
+ SHIM
89
+ end
90
+
91
+ def use_output(version, source)
92
+ lines = []
93
+ lines << "set METANORMA_VERSION=#{version}"
94
+ lines << "set METANORMA_SOURCE=#{source}" if source
95
+ lines << 'rem Run this in your CMD session'
96
+ lines.join("\n")
97
+ end
98
+
99
+ def config_file
100
+ nil # CMD doesn't have a standard config file
101
+ end
102
+
103
+ def windows?
104
+ true
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'bash'
5
+ require_relative 'power_shell'
6
+ require_relative 'cmd'
7
+
8
+ module Mnenv
9
+ module Shells
10
+ # Factory for detecting and creating shell instances
11
+ class ShellFactory
12
+ SHELLS = {
13
+ 'bash' => BashShell,
14
+ 'sh' => BashShell,
15
+ 'dash' => BashShell,
16
+ 'zsh' => BashShell,
17
+ 'fish' => BashShell, # Fish can run bash scripts in compatibility mode
18
+ 'powershell' => PowerShellShell,
19
+ 'pwsh' => PowerShellShell,
20
+ 'cmd' => CmdShell
21
+ }.freeze
22
+
23
+ class << self
24
+ # Detect the current shell from environment
25
+ def detect
26
+ shell_env = ENV['SHELL'] || ENV['COMSPEC'] || ''
27
+
28
+ if windows?
29
+ detect_windows_shell(shell_env)
30
+ else
31
+ detect_unix_shell(shell_env)
32
+ end
33
+ end
34
+
35
+ # Get shell by name
36
+ def get(shell_name)
37
+ shell_class = SHELLS[shell_name.downcase]
38
+ raise ArgumentError, "Unknown shell: #{shell_name}" unless shell_class
39
+
40
+ shell_class.new
41
+ end
42
+
43
+ # Get all shells for the current platform
44
+ def platform_shells
45
+ if windows?
46
+ [PowerShellShell.new, CmdShell.new]
47
+ else
48
+ [BashShell.new]
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def windows?
55
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
56
+ end
57
+
58
+ def detect_unix_shell(shell_env)
59
+ # Extract shell name from path
60
+ shell_name = File.basename(shell_env) if shell_env
61
+
62
+ # Default to bash if we can't determine
63
+ shell_name ||= 'bash'
64
+
65
+ get(shell_name)
66
+ rescue ArgumentError
67
+ # Default to bash for unknown shells
68
+ BashShell.new
69
+ end
70
+
71
+ def detect_windows_shell(comspec)
72
+ if comspec =~ /powershell|pwsh/i
73
+ PowerShellShell.new
74
+ else
75
+ # Default to CMD on Windows, but also create PowerShell shim
76
+ CmdShell.new
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Mnenv
6
+ module Shells
7
+ # PowerShell shell implementation (Windows)
8
+ class PowerShellShell < BaseShell
9
+ def name
10
+ 'powershell'
11
+ end
12
+
13
+ def shim_extension
14
+ '.ps1'
15
+ end
16
+
17
+ def shim_content(executable_name)
18
+ <<~SHIM
19
+ # mnenv shim for PowerShell
20
+ $ErrorActionPreference = "Stop"
21
+
22
+ $MNENV_ROOT = if ($env:MNENV_ROOT) { $env:MNENV_ROOT } else { "$env:USERPROFILE\\.mnenv" }
23
+
24
+ # Resolve version and source using mnenv resolver
25
+ $versionFile = "$MNENV_ROOT\\version"
26
+ $sourceFile = "$MNENV_ROOT\\source"
27
+
28
+ # Check environment variables first
29
+ $VERSION = $env:METANORMA_VERSION
30
+ $SOURCE = $env:METANORMA_SOURCE
31
+
32
+ # Check local .metanorma-version file
33
+ if (-not $VERSION) {
34
+ $dir = (Get-Location).Path
35
+ $root = [System.IO.Path]::GetPathRoot($dir)
36
+ while ($dir -and $dir -ne $root) {
37
+ $localVersionFile = Join-Path $dir ".metanorma-version"
38
+ if (Test-Path $localVersionFile) {
39
+ $VERSION = Get-Content $localVersionFile -Raw
40
+ $VERSION = $VERSION.Trim()
41
+ break
42
+ }
43
+ $localSourceFile = Join-Path $dir ".metanorma-source"
44
+ if (-not $SOURCE -and (Test-Path $localSourceFile)) {
45
+ $SOURCE = Get-Content $localSourceFile -Raw
46
+ $SOURCE = $SOURCE.Trim()
47
+ }
48
+ $dir = Split-Path $dir -Parent
49
+ }
50
+ }
51
+
52
+ # Check global version file
53
+ if (-not $VERSION -and (Test-Path $versionFile)) {
54
+ $VERSION = Get-Content $versionFile -Raw
55
+ $VERSION = $VERSION.Trim()
56
+ }
57
+
58
+ if (-not $SOURCE -and (Test-Path $sourceFile)) {
59
+ $SOURCE = Get-Content $sourceFile -Raw
60
+ $SOURCE = $SOURCE.Trim()
61
+ }
62
+
63
+ # Default source
64
+ if (-not $SOURCE) {
65
+ $SOURCE = "gemfile"
66
+ }
67
+
68
+ if (-not $VERSION) {
69
+ Write-Error "mnenv: version not set or invalid"
70
+ Write-Error "Set a version with: mnenv global <version> or mnenv local <version>"
71
+ exit 1
72
+ }
73
+
74
+ # Determine executable path based on source
75
+ # Installed versions are in $MNENV_ROOT\\installed\\<version>-<source>\\
76
+ if ($SOURCE -eq "binary") {
77
+ $EXECUTABLE = "$MNENV_ROOT\\installed\\$VERSION-$SOURCE\\metanorma.exe"
78
+ } else {
79
+ # Bundler creates .cmd files on Windows, not .bat
80
+ $EXECUTABLE = "$MNENV_ROOT\\installed\\$VERSION-$SOURCE\\bin\\#{executable_name}.cmd"
81
+ }
82
+
83
+ if (-not (Test-Path $EXECUTABLE)) {
84
+ Write-Error "mnenv: #{executable_name} not installed for version $VERSION (source: $SOURCE)"
85
+ Write-Error "Install it with: mnenv install $VERSION --source $SOURCE"
86
+ exit 1
87
+ }
88
+
89
+ & $EXECUTABLE $args
90
+ SHIM
91
+ end
92
+
93
+ def use_output(version, source)
94
+ lines = []
95
+ lines << "$env:METANORMA_VERSION = '#{version}'"
96
+ lines << "$env:METANORMA_SOURCE = '#{source}'" if source
97
+ lines << '# Run this in your PowerShell session'
98
+ lines.join("\n")
99
+ end
100
+
101
+ def config_file
102
+ File.expand_path('~\Documents\PowerShell\Microsoft.PowerShell_profile.ps1')
103
+ end
104
+
105
+ def windows?
106
+ true
107
+ end
108
+ end
109
+ end
110
+ end