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
@@ -4,12 +4,14 @@ require 'thor'
4
4
  require 'json'
5
5
 
6
6
  module Mnenv
7
+ autoload :SnapRepository, 'mnenv/snap_repository'
8
+ autoload :JsonFormatter, 'mnenv/json_formatter'
9
+ autoload :Snap, 'mnenv/snap'
10
+
7
11
  class SnapCommand < Thor
8
12
  desc 'list', 'List all Snap versions'
9
13
  method_option :format, type: :string, aliases: '-f', default: 'text'
10
14
  def list
11
- require_relative '../snap_repository'
12
- require_relative '../json_formatter'
13
15
  repo = SnapRepository.new
14
16
  versions = repo.all
15
17
 
@@ -25,8 +27,6 @@ module Mnenv
25
27
 
26
28
  desc 'refresh', 'Fetch and add new Snap versions (incremental)'
27
29
  def refresh
28
- require_relative '../snap/fetcher'
29
- require_relative '../snap_repository'
30
30
  fetcher = Snap::Fetcher.new
31
31
  repo = fetcher.repository
32
32
 
@@ -50,7 +50,6 @@ module Mnenv
50
50
 
51
51
  desc 'revamp', 'Re-fetch all Snap versions'
52
52
  def revamp
53
- require_relative '../snap/fetcher'
54
53
  fetcher = Snap::Fetcher.new
55
54
  versions = fetcher.fetch_and_save
56
55
  puts "Revamped #{versions.size} Snap versions"
@@ -58,14 +57,13 @@ module Mnenv
58
57
 
59
58
  desc 'update VERSION', 'Update a specific Snap version (all arch/channel combinations)'
60
59
  def update(version)
61
- require_relative '../snap/fetcher'
62
60
  fetcher = Snap::Fetcher.new
63
61
  versions = fetcher.fetch_all
64
62
  targets = versions.select { |v| v.version == version }
65
63
 
66
64
  if targets.empty?
67
65
  puts "Snap version #{version} not found in current channel-map"
68
- puts "Note: Historical versions no longer in Snap API cannot be updated"
66
+ puts 'Note: Historical versions no longer in Snap API cannot be updated'
69
67
  exit 1
70
68
  end
71
69
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty/prompt'
4
+ require_relative '../installer'
5
+
6
+ module Mnenv
7
+ class UninstallCommand < Thor
8
+ namespace :uninstall
9
+
10
+ class_option :force, type: :boolean, aliases: '-f', default: false,
11
+ desc: 'Force uninstallation without confirmation'
12
+
13
+ desc 'VERSION', 'Uninstall a specific Metanorma version'
14
+ method_option :force, type: :boolean, aliases: '-f', default: false
15
+ def uninstall(version)
16
+ version_dir = File.expand_path("~/.mnenv/versions/#{version}")
17
+
18
+ unless Dir.exist?(version_dir)
19
+ puts "Version #{version} is not installed."
20
+ return
21
+ end
22
+
23
+ unless options[:force]
24
+ prompt = TTY::Prompt.new
25
+ unless prompt.yes?("Uninstall Metanorma #{version}? This cannot be undone.")
26
+ puts 'Uninstallation cancelled.'
27
+ return
28
+ end
29
+ end
30
+
31
+ FileUtils.rm_rf(version_dir)
32
+
33
+ # Regenerate shims
34
+ ShimManager.new.regenerate_all
35
+
36
+ puts "Uninstalled Metanorma #{version}"
37
+ rescue StandardError => e
38
+ warn "Error: #{e.message}"
39
+ exit 1
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty/prompt'
4
+ require 'json'
5
+ require_relative '../installer'
6
+ require_relative '../binary_repository'
7
+
8
+ module Mnenv
9
+ class VersionCommand < Thor
10
+ namespace :version
11
+
12
+ class_option :source, type: :string, enum: %w[gemfile binary],
13
+ desc: 'Source type (gemfile or binary)'
14
+ class_option :interactive, type: :boolean, aliases: '-i', default: false,
15
+ desc: 'Interactive mode for version selection'
16
+
17
+ desc 'use VERSION', 'Set Metanorma version for current shell session'
18
+ method_option :source, type: :string, enum: %w[gemfile binary]
19
+ method_option :interactive, type: :boolean, aliases: '-i', default: false
20
+ def use(version = nil)
21
+ version, source = resolve_version_and_source(version, options[:source], options[:interactive])
22
+
23
+ puts "export METANORMA_VERSION=#{version}"
24
+ puts "export METANORMA_SOURCE=#{source}" if source
25
+ puts "# Run this in your shell, or use: eval \"$(mnenv use #{version}#{" --source #{source}" if source})\""
26
+ end
27
+
28
+ desc 'global VERSION', 'Set default Metanorma version globally'
29
+ method_option :source, type: :string, enum: %w[gemfile binary]
30
+ method_option :interactive, type: :boolean, aliases: '-i', default: false
31
+ def global(version = nil)
32
+ version, source = resolve_version_and_source(version, options[:source], options[:interactive])
33
+ verify_installed!(version, source)
34
+
35
+ File.write(Paths::VERSION_FILE, version)
36
+ File.write(Paths::SOURCE_FILE, source) if source
37
+ puts "Global Metanorma version set to #{version}#{source ? " (source: #{source})" : ''}"
38
+ rescue StandardError => e
39
+ warn "Error: #{e.message}"
40
+ exit 1
41
+ end
42
+
43
+ desc 'local VERSION', 'Set Metanorma version for current directory'
44
+ method_option :source, type: :string, enum: %w[gemfile binary]
45
+ method_option :interactive, type: :boolean, aliases: '-i', default: false
46
+ def local(version = nil)
47
+ version, source = resolve_version_and_source(version, options[:source], options[:interactive])
48
+ verify_installed!(version, source)
49
+
50
+ File.write('.metanorma-version', version)
51
+ File.write('.metanorma-source', source) if source
52
+ puts "Local Metanorma version set to #{version}#{source ? " (source: #{source})" : ''}"
53
+ puts "Created .metanorma-version#{source ? ' and .metanorma-source' : ''}"
54
+ rescue StandardError => e
55
+ warn "Error: #{e.message}"
56
+ exit 1
57
+ end
58
+
59
+ desc 'versions', 'List all installed Metanorma versions'
60
+ method_option :format, type: :string, aliases: '-f', default: 'text',
61
+ desc: 'Output format (text or json)'
62
+ def versions
63
+ installed = list_installed_versions
64
+
65
+ if installed.empty?
66
+ puts 'No versions installed.'
67
+ puts "\nRun: mnenv install --list"
68
+ return
69
+ end
70
+
71
+ current_version, current_source = resolve_current
72
+
73
+ case options[:format]
74
+ when 'json'
75
+ output = {
76
+ 'current_version' => current_version,
77
+ 'current_source' => current_source,
78
+ 'installed' => installed.map do |version, sources|
79
+ sources.map do |source|
80
+ {
81
+ 'version' => version,
82
+ 'source' => source,
83
+ 'current' => version == current_version && source == current_source
84
+ }
85
+ end
86
+ end.flatten
87
+ }
88
+ puts JSON.pretty_generate(output)
89
+ else
90
+ puts 'Installed Metanorma versions:'
91
+
92
+ installed.sort.reverse.each do |version, sources|
93
+ sources.sort.each do |source|
94
+ is_current = version == current_version && source == current_source
95
+ marker = is_current ? '* ' : ' '
96
+ puts " #{marker}#{version} (source: #{source})"
97
+ end
98
+ end
99
+
100
+ puts "\nCurrent version: #{current_version || 'none'}"
101
+ puts "Current source: #{current_source || 'none'}"
102
+ puts "\nTo see available versions, run: mnenv install --list"
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # List installed versions with their sources
109
+ # Uses Paths::INSTALLED_DIR with new naming convention: <version>-<source>
110
+ def list_installed_versions
111
+ return {} unless Dir.exist?(Paths::INSTALLED_DIR)
112
+
113
+ versions = Hash.new { |h, k| h[k] = [] }
114
+
115
+ Dir.glob(File.join(Paths::INSTALLED_DIR, '*')).each do |dir|
116
+ next unless File.directory?(dir)
117
+
118
+ dir_name = File.basename(dir)
119
+ version, source = Paths.parse_version_dir(dir_name)
120
+
121
+ # Only include valid version-source directories
122
+ next unless version && source
123
+
124
+ versions[version] << source
125
+ end
126
+
127
+ versions
128
+ end
129
+
130
+ def resolve_version_and_source(version, source, interactive)
131
+ version, source = select_version_interactive if interactive || version.nil?
132
+ source ||= default_source
133
+ [version, source]
134
+ end
135
+
136
+ def select_version_interactive
137
+ prompt = TTY::Prompt.new
138
+ installed = list_installed_versions
139
+
140
+ raise 'No versions installed. Run: mnenv install --list' if installed.empty?
141
+
142
+ choices = installed.flat_map do |version, sources|
143
+ sources.map do |source|
144
+ { name: "#{version} (#{source})", value: [version, source] }
145
+ end
146
+ end
147
+
148
+ version, source = prompt.select('Select a version:', choices)
149
+
150
+ [version, source]
151
+ end
152
+
153
+ def verify_installed!(version, source)
154
+ version_dir = Paths.version_install_dir(version, source)
155
+ return if Dir.exist?(version_dir)
156
+
157
+ raise "Version #{version} (source: #{source}) is not installed. Run: mnenv install #{version} --source #{source}"
158
+ end
159
+
160
+ def installed_versions
161
+ list_installed_versions.keys.sort
162
+ end
163
+
164
+ def default_source
165
+ if File.exist?(Paths::SOURCE_FILE)
166
+ File.read(Paths::SOURCE_FILE).strip
167
+ else
168
+ 'gemfile'
169
+ end
170
+ end
171
+
172
+ def resolve_version
173
+ return ENV['METANORMA_VERSION'] if ENV['METANORMA_VERSION']
174
+
175
+ dir = Dir.pwd
176
+ loop do
177
+ return File.read(File.join(dir, '.metanorma-version')).strip if File.exist?(File.join(dir,
178
+ '.metanorma-version'))
179
+
180
+ parent = File.dirname(dir)
181
+ break if parent == dir # Reached root
182
+
183
+ dir = parent
184
+ end
185
+
186
+ return File.read(Paths::VERSION_FILE).strip if File.exist?(Paths::VERSION_FILE)
187
+
188
+ nil
189
+ end
190
+
191
+ def resolve_source
192
+ return ENV['METANORMA_SOURCE'] if ENV['METANORMA_SOURCE']
193
+
194
+ dir = Dir.pwd
195
+ loop do
196
+ return File.read(File.join(dir, '.metanorma-source')).strip if File.exist?(File.join(dir, '.metanorma-source'))
197
+
198
+ parent = File.dirname(dir)
199
+ break if parent == dir # Reached root
200
+
201
+ dir = parent
202
+ end
203
+
204
+ return File.read(Paths::SOURCE_FILE).strip if File.exist?(Paths::SOURCE_FILE)
205
+
206
+ 'gemfile'
207
+ end
208
+
209
+ def resolve_current
210
+ [resolve_version, resolve_source]
211
+ end
212
+ end
213
+ end
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'commands/gemfile_command'
4
- require_relative 'commands/snap_command'
5
- require_relative 'commands/homebrew_command'
6
- require_relative 'commands/chocolatey_command'
3
+ module Mnenv
4
+ autoload :GemfileCommand, 'mnenv/commands/gemfile_command'
5
+ autoload :SnapCommand, 'mnenv/commands/snap_command'
6
+ autoload :HomebrewCommand, 'mnenv/commands/homebrew_command'
7
+ autoload :ChocolateyCommand, 'mnenv/commands/chocolatey_command'
8
+ autoload :InstallCommand, 'mnenv/commands/install_command'
9
+ autoload :VersionCommand, 'mnenv/commands/version_command'
10
+ autoload :UninstallCommand, 'mnenv/commands/uninstall_command'
11
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'English'
3
4
  require_relative '../logger'
4
5
  require_relative '../gemfile_repository'
5
6
  require_relative './fetcher'
@@ -36,7 +37,7 @@ module Mnenv
36
37
 
37
38
  def extract_incremental
38
39
  remote_versions = fetcher.fetch_all
39
- existing_versions = repository.all.select { |v| v.exists_locally? }.map(&:version)
40
+ existing_versions = repository.all.select(&:exists_locally?).map(&:version)
40
41
 
41
42
  missing = remote_versions.reject { |v| existing_versions.include?(v.version) }
42
43
 
@@ -74,12 +75,18 @@ module Mnenv
74
75
 
75
76
  cleanup_docker_image(version.version)
76
77
 
78
+ # Update version metadata after successful extraction
79
+ version.gemfile_exists = true
80
+ # Store relative paths (relative to data/gemfile directory)
81
+ version.gemfile_path = "v#{version.version}/Gemfile"
82
+ version.gemfile_lock_path = "v#{version.version}/Gemfile.lock.archived"
83
+
77
84
  repository.save(version)
78
85
  end
79
86
 
80
87
  def pull_docker_image(version_number)
81
88
  system('docker', 'pull', "#{Fetcher::DOCKER_IMAGE}:#{version_number}", out: File::NULL)
82
- raise 'Failed to pull Docker image' unless $?.success?
89
+ raise 'Failed to pull Docker image' unless $CHILD_STATUS.success?
83
90
  end
84
91
 
85
92
  def extract_gemfiles(version)
@@ -89,7 +96,7 @@ module Mnenv
89
96
  cmd = "docker run --rm --entrypoint sh #{Fetcher::DOCKER_IMAGE}:#{version.version} -c '#{script}'"
90
97
  output = `#{cmd}`
91
98
 
92
- raise "Extraction failed for #{version.version}" unless $?.success?
99
+ raise "Extraction failed for #{version.version}" unless $CHILD_STATUS.success?
93
100
 
94
101
  gemfile, gemfile_lock = parse_gemfile_output(output)
95
102
 
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ module Gemfile
5
+ autoload :Extractor, 'mnenv/gemfile/extractor'
6
+ autoload :Fetcher, 'mnenv/gemfile/fetcher'
7
+ end
8
+ end
@@ -7,7 +7,5 @@ module Mnenv
7
7
  class GemfileRepository < Repository
8
8
  def version_class = GemfileVersion
9
9
  def source_name = :gemfile
10
-
11
- def default_data_dir = File.join(__dir__, '..', '..', 'data', 'gemfile')
12
10
  end
13
11
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ module Homebrew
5
+ autoload :Fetcher, 'mnenv/homebrew/fetcher'
6
+ end
7
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ class Installer
5
+ class InstallationError < StandardError; end
6
+ class DevelopmentToolsMissing < InstallationError; end
7
+
8
+ attr_reader :version, :source
9
+
10
+ def initialize(version, source: nil, target_dir: nil)
11
+ @version = version
12
+ @source = source || default_source
13
+ @target_dir = target_dir || default_target_dir
14
+ end
15
+
16
+ def install
17
+ verify_prerequisites!
18
+ create_install_directory
19
+ perform_installation
20
+ regenerate_shims
21
+ end
22
+
23
+ def installed?
24
+ Dir.exist?(version_dir)
25
+ end
26
+
27
+ private
28
+
29
+ def default_target_dir
30
+ @default_target_dir ||= Paths.version_install_dir(version, source)
31
+ end
32
+
33
+ def version_dir
34
+ @target_dir
35
+ end
36
+
37
+ def verify_prerequisites!
38
+ raise NotImplementedError, "#{self.class} must implement verify_prerequisites!"
39
+ end
40
+
41
+ def perform_installation
42
+ raise NotImplementedError, "#{self.class} must implement perform_installation!"
43
+ end
44
+
45
+ def create_install_directory
46
+ FileUtils.mkdir_p(version_dir)
47
+ end
48
+
49
+ def regenerate_shims
50
+ ShimManager.new.regenerate_all
51
+ end
52
+
53
+ def default_source
54
+ # Try to read from ~/.mnenv/source, else default to gemfile
55
+ if File.exist?(Paths::SOURCE_FILE)
56
+ File.read(Paths::SOURCE_FILE).strip
57
+ else
58
+ 'gemfile' # Default: faster for devs
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ class InstallerFactory
5
+ def self.create(version, source:)
6
+ case source.to_s
7
+ when 'gemfile'
8
+ Installers::GemfileInstaller.new(version, source: source)
9
+ when 'binary'
10
+ Installers::BinaryInstaller.new(version, source: source)
11
+ else
12
+ raise Installer::InstallationError,
13
+ "Unknown source: #{source}. Use: gemfile or binary"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ autoload :Installer, 'mnenv/installer/base'
5
+ autoload :InstallerFactory, 'mnenv/installer/factory'
6
+ autoload :ShimManager, 'mnenv/shim_manager'
7
+
8
+ module Installers
9
+ autoload :GemfileInstaller, 'mnenv/installers/gemfile_installer'
10
+ autoload :BinaryInstaller, 'mnenv/installers/binary_installer'
11
+ end
12
+ end