mnenv 0.1.0

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 (276) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +17 -0
  3. data/README.adoc +605 -0
  4. data/Rakefile +8 -0
  5. data/data/chocolatey/versions.yaml +812 -0
  6. data/data/gemfile/v1.1.6/Gemfile +4 -0
  7. data/data/gemfile/v1.1.6/Gemfile.lock.archived +232 -0
  8. data/data/gemfile/v1.1.7/Gemfile +4 -0
  9. data/data/gemfile/v1.1.7/Gemfile.lock.archived +235 -0
  10. data/data/gemfile/v1.1.8/Gemfile +4 -0
  11. data/data/gemfile/v1.1.8/Gemfile.lock.archived +238 -0
  12. data/data/gemfile/v1.10.0/Gemfile +5 -0
  13. data/data/gemfile/v1.10.0/Gemfile.lock.archived +930 -0
  14. data/data/gemfile/v1.10.1/Gemfile +5 -0
  15. data/data/gemfile/v1.10.1/Gemfile.lock.archived +929 -0
  16. data/data/gemfile/v1.10.10/Gemfile +5 -0
  17. data/data/gemfile/v1.10.10/Gemfile.lock.archived +973 -0
  18. data/data/gemfile/v1.10.11/Gemfile +5 -0
  19. data/data/gemfile/v1.10.11/Gemfile.lock.archived +975 -0
  20. data/data/gemfile/v1.10.2/Gemfile +5 -0
  21. data/data/gemfile/v1.10.2/Gemfile.lock.archived +939 -0
  22. data/data/gemfile/v1.10.3/Gemfile +5 -0
  23. data/data/gemfile/v1.10.3/Gemfile.lock.archived +946 -0
  24. data/data/gemfile/v1.10.5/Gemfile +5 -0
  25. data/data/gemfile/v1.10.5/Gemfile.lock.archived +958 -0
  26. data/data/gemfile/v1.10.6/Gemfile +5 -0
  27. data/data/gemfile/v1.10.6/Gemfile.lock.archived +969 -0
  28. data/data/gemfile/v1.10.7/Gemfile +5 -0
  29. data/data/gemfile/v1.10.7/Gemfile.lock.archived +969 -0
  30. data/data/gemfile/v1.10.8/Gemfile +5 -0
  31. data/data/gemfile/v1.10.8/Gemfile.lock.archived +968 -0
  32. data/data/gemfile/v1.10.9/Gemfile +5 -0
  33. data/data/gemfile/v1.10.9/Gemfile.lock.archived +972 -0
  34. data/data/gemfile/v1.11.0/Gemfile +5 -0
  35. data/data/gemfile/v1.11.0/Gemfile.lock.archived +971 -0
  36. data/data/gemfile/v1.11.1/Gemfile +5 -0
  37. data/data/gemfile/v1.11.1/Gemfile.lock.archived +975 -0
  38. data/data/gemfile/v1.11.4/Gemfile +5 -0
  39. data/data/gemfile/v1.11.4/Gemfile.lock.archived +1046 -0
  40. data/data/gemfile/v1.11.5/Gemfile +5 -0
  41. data/data/gemfile/v1.11.5/Gemfile.lock.archived +1047 -0
  42. data/data/gemfile/v1.12.10/Gemfile +3 -0
  43. data/data/gemfile/v1.12.10/Gemfile.lock.archived +1073 -0
  44. data/data/gemfile/v1.12.3/Gemfile +3 -0
  45. data/data/gemfile/v1.12.3/Gemfile.lock.archived +1050 -0
  46. data/data/gemfile/v1.12.4/Gemfile +3 -0
  47. data/data/gemfile/v1.12.4/Gemfile.lock.archived +1056 -0
  48. data/data/gemfile/v1.12.5/Gemfile +3 -0
  49. data/data/gemfile/v1.12.5/Gemfile.lock.archived +1054 -0
  50. data/data/gemfile/v1.12.6/Gemfile +3 -0
  51. data/data/gemfile/v1.12.6/Gemfile.lock.archived +1056 -0
  52. data/data/gemfile/v1.12.8/Gemfile +3 -0
  53. data/data/gemfile/v1.12.8/Gemfile.lock.archived +1063 -0
  54. data/data/gemfile/v1.13.0/Gemfile +3 -0
  55. data/data/gemfile/v1.13.0/Gemfile.lock.archived +1074 -0
  56. data/data/gemfile/v1.13.2/Gemfile +3 -0
  57. data/data/gemfile/v1.13.2/Gemfile.lock.archived +899 -0
  58. data/data/gemfile/v1.13.3/Gemfile +3 -0
  59. data/data/gemfile/v1.13.3/Gemfile.lock.archived +938 -0
  60. data/data/gemfile/v1.13.4/Gemfile +3 -0
  61. data/data/gemfile/v1.13.4/Gemfile.lock.archived +938 -0
  62. data/data/gemfile/v1.13.5/Gemfile +3 -0
  63. data/data/gemfile/v1.13.5/Gemfile.lock.archived +944 -0
  64. data/data/gemfile/v1.13.7/Gemfile +3 -0
  65. data/data/gemfile/v1.13.7/Gemfile.lock.archived +944 -0
  66. data/data/gemfile/v1.13.8/Gemfile +3 -0
  67. data/data/gemfile/v1.13.8/Gemfile.lock.archived +944 -0
  68. data/data/gemfile/v1.13.9/Gemfile +3 -0
  69. data/data/gemfile/v1.13.9/Gemfile.lock.archived +956 -0
  70. data/data/gemfile/v1.14.3/Gemfile +3 -0
  71. data/data/gemfile/v1.14.3/Gemfile.lock.archived +950 -0
  72. data/data/gemfile/v1.2.12/Gemfile +3 -0
  73. data/data/gemfile/v1.2.12/Gemfile.lock.archived +283 -0
  74. data/data/gemfile/v1.2.2/Gemfile +4 -0
  75. data/data/gemfile/v1.2.2/Gemfile.lock.archived +224 -0
  76. data/data/gemfile/v1.2.3/Gemfile +4 -0
  77. data/data/gemfile/v1.2.3/Gemfile.lock.archived +231 -0
  78. data/data/gemfile/v1.2.6/Gemfile +4 -0
  79. data/data/gemfile/v1.2.6/Gemfile.lock.archived +239 -0
  80. data/data/gemfile/v1.2.8/Gemfile +4 -0
  81. data/data/gemfile/v1.2.8/Gemfile.lock.archived +233 -0
  82. data/data/gemfile/v1.2.9/Gemfile +4 -0
  83. data/data/gemfile/v1.2.9/Gemfile.lock.archived +245 -0
  84. data/data/gemfile/v1.3.1/Gemfile +3 -0
  85. data/data/gemfile/v1.3.1/Gemfile.lock.archived +296 -0
  86. data/data/gemfile/v1.3.2/Gemfile +3 -0
  87. data/data/gemfile/v1.3.2/Gemfile.lock.archived +296 -0
  88. data/data/gemfile/v1.3.4/Gemfile +3 -0
  89. data/data/gemfile/v1.3.4/Gemfile.lock.archived +284 -0
  90. data/data/gemfile/v1.3.5/Gemfile +3 -0
  91. data/data/gemfile/v1.3.5/Gemfile.lock.archived +284 -0
  92. data/data/gemfile/v1.3.6/Gemfile +3 -0
  93. data/data/gemfile/v1.3.6/Gemfile.lock.archived +286 -0
  94. data/data/gemfile/v1.3.9/Gemfile +3 -0
  95. data/data/gemfile/v1.3.9/Gemfile.lock.archived +334 -0
  96. data/data/gemfile/v1.4.0/Gemfile +3 -0
  97. data/data/gemfile/v1.4.0/Gemfile.lock.archived +330 -0
  98. data/data/gemfile/v1.4.10/Gemfile +4 -0
  99. data/data/gemfile/v1.4.10/Gemfile.lock.archived +461 -0
  100. data/data/gemfile/v1.4.11/Gemfile +4 -0
  101. data/data/gemfile/v1.4.11/Gemfile.lock.archived +452 -0
  102. data/data/gemfile/v1.4.12/Gemfile +4 -0
  103. data/data/gemfile/v1.4.12/Gemfile.lock.archived +452 -0
  104. data/data/gemfile/v1.4.13/Gemfile +4 -0
  105. data/data/gemfile/v1.4.13/Gemfile.lock.archived +455 -0
  106. data/data/gemfile/v1.4.14/Gemfile +4 -0
  107. data/data/gemfile/v1.4.14/Gemfile.lock.archived +456 -0
  108. data/data/gemfile/v1.4.18/Gemfile +3 -0
  109. data/data/gemfile/v1.4.18/Gemfile.lock.archived +486 -0
  110. data/data/gemfile/v1.4.3/Gemfile +3 -0
  111. data/data/gemfile/v1.4.3/Gemfile.lock.archived +339 -0
  112. data/data/gemfile/v1.4.4/Gemfile +3 -0
  113. data/data/gemfile/v1.4.4/Gemfile.lock.archived +339 -0
  114. data/data/gemfile/v1.4.5/Gemfile +3 -0
  115. data/data/gemfile/v1.4.5/Gemfile.lock.archived +348 -0
  116. data/data/gemfile/v1.4.6/Gemfile +3 -0
  117. data/data/gemfile/v1.4.6/Gemfile.lock.archived +357 -0
  118. data/data/gemfile/v1.4.7/Gemfile +3 -0
  119. data/data/gemfile/v1.4.7/Gemfile.lock.archived +391 -0
  120. data/data/gemfile/v1.4.8/Gemfile +3 -0
  121. data/data/gemfile/v1.4.8/Gemfile.lock.archived +445 -0
  122. data/data/gemfile/v1.4.9/Gemfile +3 -0
  123. data/data/gemfile/v1.4.9/Gemfile.lock.archived +448 -0
  124. data/data/gemfile/v1.5.0/Gemfile +3 -0
  125. data/data/gemfile/v1.5.0/Gemfile.lock.archived +478 -0
  126. data/data/gemfile/v1.5.10/Gemfile +3 -0
  127. data/data/gemfile/v1.5.10/Gemfile.lock.archived +668 -0
  128. data/data/gemfile/v1.5.11/Gemfile +3 -0
  129. data/data/gemfile/v1.5.11/Gemfile.lock.archived +668 -0
  130. data/data/gemfile/v1.5.15/Gemfile +3 -0
  131. data/data/gemfile/v1.5.15/Gemfile.lock.archived +686 -0
  132. data/data/gemfile/v1.5.16/Gemfile +3 -0
  133. data/data/gemfile/v1.5.16/Gemfile.lock.archived +684 -0
  134. data/data/gemfile/v1.5.17/Gemfile +3 -0
  135. data/data/gemfile/v1.5.17/Gemfile.lock.archived +684 -0
  136. data/data/gemfile/v1.5.18/Gemfile +5 -0
  137. data/data/gemfile/v1.5.18/Gemfile.lock.archived +691 -0
  138. data/data/gemfile/v1.5.19/Gemfile +5 -0
  139. data/data/gemfile/v1.5.19/Gemfile.lock.archived +703 -0
  140. data/data/gemfile/v1.5.20/Gemfile +5 -0
  141. data/data/gemfile/v1.5.20/Gemfile.lock.archived +703 -0
  142. data/data/gemfile/v1.5.21/Gemfile +5 -0
  143. data/data/gemfile/v1.5.21/Gemfile.lock.archived +707 -0
  144. data/data/gemfile/v1.5.22/Gemfile +5 -0
  145. data/data/gemfile/v1.5.22/Gemfile.lock.archived +707 -0
  146. data/data/gemfile/v1.5.23/Gemfile +5 -0
  147. data/data/gemfile/v1.5.23/Gemfile.lock.archived +711 -0
  148. data/data/gemfile/v1.5.24/Gemfile +5 -0
  149. data/data/gemfile/v1.5.24/Gemfile.lock.archived +711 -0
  150. data/data/gemfile/v1.5.3/Gemfile +3 -0
  151. data/data/gemfile/v1.5.3/Gemfile.lock.archived +651 -0
  152. data/data/gemfile/v1.5.4/Gemfile +3 -0
  153. data/data/gemfile/v1.5.4/Gemfile.lock.archived +657 -0
  154. data/data/gemfile/v1.5.5/Gemfile +3 -0
  155. data/data/gemfile/v1.5.5/Gemfile.lock.archived +657 -0
  156. data/data/gemfile/v1.5.6/Gemfile +3 -0
  157. data/data/gemfile/v1.5.6/Gemfile.lock.archived +657 -0
  158. data/data/gemfile/v1.5.7/Gemfile +3 -0
  159. data/data/gemfile/v1.5.7/Gemfile.lock.archived +657 -0
  160. data/data/gemfile/v1.5.8/Gemfile +3 -0
  161. data/data/gemfile/v1.5.8/Gemfile.lock.archived +655 -0
  162. data/data/gemfile/v1.5.9/Gemfile +3 -0
  163. data/data/gemfile/v1.5.9/Gemfile.lock.archived +656 -0
  164. data/data/gemfile/v1.6.1/Gemfile +5 -0
  165. data/data/gemfile/v1.6.1/Gemfile.lock.archived +721 -0
  166. data/data/gemfile/v1.6.10/Gemfile +5 -0
  167. data/data/gemfile/v1.6.10/Gemfile.lock.archived +744 -0
  168. data/data/gemfile/v1.6.11/Gemfile +5 -0
  169. data/data/gemfile/v1.6.11/Gemfile.lock.archived +744 -0
  170. data/data/gemfile/v1.6.12/Gemfile +5 -0
  171. data/data/gemfile/v1.6.12/Gemfile.lock.archived +745 -0
  172. data/data/gemfile/v1.6.13/Gemfile +5 -0
  173. data/data/gemfile/v1.6.13/Gemfile.lock.archived +745 -0
  174. data/data/gemfile/v1.6.14/Gemfile +5 -0
  175. data/data/gemfile/v1.6.14/Gemfile.lock.archived +754 -0
  176. data/data/gemfile/v1.6.15/Gemfile +5 -0
  177. data/data/gemfile/v1.6.15/Gemfile.lock.archived +757 -0
  178. data/data/gemfile/v1.6.2/Gemfile +5 -0
  179. data/data/gemfile/v1.6.2/Gemfile.lock.archived +718 -0
  180. data/data/gemfile/v1.6.3/Gemfile +5 -0
  181. data/data/gemfile/v1.6.3/Gemfile.lock.archived +728 -0
  182. data/data/gemfile/v1.6.4/Gemfile +5 -0
  183. data/data/gemfile/v1.6.4/Gemfile.lock.archived +730 -0
  184. data/data/gemfile/v1.6.5/Gemfile +5 -0
  185. data/data/gemfile/v1.6.5/Gemfile.lock.archived +733 -0
  186. data/data/gemfile/v1.6.6/Gemfile +5 -0
  187. data/data/gemfile/v1.6.6/Gemfile.lock.archived +733 -0
  188. data/data/gemfile/v1.6.7/Gemfile +5 -0
  189. data/data/gemfile/v1.6.7/Gemfile.lock.archived +733 -0
  190. data/data/gemfile/v1.6.9/Gemfile +5 -0
  191. data/data/gemfile/v1.6.9/Gemfile.lock.archived +744 -0
  192. data/data/gemfile/v1.7.0/Gemfile +5 -0
  193. data/data/gemfile/v1.7.0/Gemfile.lock.archived +750 -0
  194. data/data/gemfile/v1.7.1/Gemfile +5 -0
  195. data/data/gemfile/v1.7.1/Gemfile.lock.archived +750 -0
  196. data/data/gemfile/v1.7.2/Gemfile +5 -0
  197. data/data/gemfile/v1.7.2/Gemfile.lock.archived +747 -0
  198. data/data/gemfile/v1.7.3/Gemfile +5 -0
  199. data/data/gemfile/v1.7.3/Gemfile.lock.archived +755 -0
  200. data/data/gemfile/v1.7.4/Gemfile +5 -0
  201. data/data/gemfile/v1.7.4/Gemfile.lock.archived +756 -0
  202. data/data/gemfile/v1.7.5/Gemfile +5 -0
  203. data/data/gemfile/v1.7.5/Gemfile.lock.archived +759 -0
  204. data/data/gemfile/v1.7.6/Gemfile +5 -0
  205. data/data/gemfile/v1.7.6/Gemfile.lock.archived +768 -0
  206. data/data/gemfile/v1.8.10/Gemfile +5 -0
  207. data/data/gemfile/v1.8.10/Gemfile.lock.archived +792 -0
  208. data/data/gemfile/v1.8.11/Gemfile +5 -0
  209. data/data/gemfile/v1.8.11/Gemfile.lock.archived +862 -0
  210. data/data/gemfile/v1.8.3/Gemfile +5 -0
  211. data/data/gemfile/v1.8.3/Gemfile.lock.archived +773 -0
  212. data/data/gemfile/v1.8.4/Gemfile +5 -0
  213. data/data/gemfile/v1.8.4/Gemfile.lock.archived +768 -0
  214. data/data/gemfile/v1.8.5/Gemfile +5 -0
  215. data/data/gemfile/v1.8.5/Gemfile.lock.archived +768 -0
  216. data/data/gemfile/v1.8.6/Gemfile +5 -0
  217. data/data/gemfile/v1.8.6/Gemfile.lock.archived +777 -0
  218. data/data/gemfile/v1.8.7/Gemfile +5 -0
  219. data/data/gemfile/v1.8.7/Gemfile.lock.archived +777 -0
  220. data/data/gemfile/v1.8.8/Gemfile +5 -0
  221. data/data/gemfile/v1.8.8/Gemfile.lock.archived +778 -0
  222. data/data/gemfile/v1.8.9/Gemfile +5 -0
  223. data/data/gemfile/v1.8.9/Gemfile.lock.archived +775 -0
  224. data/data/gemfile/v1.9.0/Gemfile +5 -0
  225. data/data/gemfile/v1.9.0/Gemfile.lock.archived +871 -0
  226. data/data/gemfile/v1.9.1/Gemfile +5 -0
  227. data/data/gemfile/v1.9.1/Gemfile.lock.archived +906 -0
  228. data/data/gemfile/v1.9.2/Gemfile +5 -0
  229. data/data/gemfile/v1.9.2/Gemfile.lock.archived +898 -0
  230. data/data/gemfile/v1.9.3/Gemfile +5 -0
  231. data/data/gemfile/v1.9.3/Gemfile.lock.archived +898 -0
  232. data/data/gemfile/v1.9.4/Gemfile +5 -0
  233. data/data/gemfile/v1.9.4/Gemfile.lock.archived +901 -0
  234. data/data/gemfile/v1.9.5/Gemfile +5 -0
  235. data/data/gemfile/v1.9.5/Gemfile.lock.archived +903 -0
  236. data/data/gemfile/v1.9.6/Gemfile +5 -0
  237. data/data/gemfile/v1.9.6/Gemfile.lock.archived +900 -0
  238. data/data/gemfile/v1.9.7/Gemfile +5 -0
  239. data/data/gemfile/v1.9.7/Gemfile.lock.archived +922 -0
  240. data/data/gemfile/v1.9.8/Gemfile +5 -0
  241. data/data/gemfile/v1.9.8/Gemfile.lock.archived +933 -0
  242. data/data/gemfile/versions.yaml +751 -0
  243. data/data/homebrew/versions.yaml +567 -0
  244. data/data/snap/github_tags.json +42 -0
  245. data/data/snap/versions.yaml +589 -0
  246. data/exe/mnenv +10 -0
  247. data/lib/mnenv/chocolatey/fetcher.rb +69 -0
  248. data/lib/mnenv/chocolatey_repository.rb +11 -0
  249. data/lib/mnenv/cli.rb +124 -0
  250. data/lib/mnenv/commands/chocolatey_command.rb +76 -0
  251. data/lib/mnenv/commands/gemfile_command.rb +57 -0
  252. data/lib/mnenv/commands/homebrew_command.rb +76 -0
  253. data/lib/mnenv/commands/snap_command.rb +89 -0
  254. data/lib/mnenv/commands.rb +6 -0
  255. data/lib/mnenv/fetcher.rb +30 -0
  256. data/lib/mnenv/gemfile/extractor.rb +140 -0
  257. data/lib/mnenv/gemfile/fetcher.rb +43 -0
  258. data/lib/mnenv/gemfile_repository.rb +13 -0
  259. data/lib/mnenv/homebrew/fetcher.rb +44 -0
  260. data/lib/mnenv/homebrew_repository.rb +11 -0
  261. data/lib/mnenv/json_formatter.rb +43 -0
  262. data/lib/mnenv/logger.rb +63 -0
  263. data/lib/mnenv/models/chocolatey_version.rb +18 -0
  264. data/lib/mnenv/models/gemfile_version.rb +40 -0
  265. data/lib/mnenv/models/homebrew_version.rb +23 -0
  266. data/lib/mnenv/models/snap_version.rb +22 -0
  267. data/lib/mnenv/models/version.rb +23 -0
  268. data/lib/mnenv/models.rb +9 -0
  269. data/lib/mnenv/repository.rb +105 -0
  270. data/lib/mnenv/snap/fetcher.rb +109 -0
  271. data/lib/mnenv/snap_repository.rb +70 -0
  272. data/lib/mnenv/version.rb +5 -0
  273. data/lib/mnenv.rb +8 -0
  274. data/mnenv.gemspec +43 -0
  275. data/snapcraft-list-copied-from-site.md +101 -0
  276. metadata +421 -0
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../fetcher'
4
+ require_relative '../homebrew_repository'
5
+ require_relative '../models/homebrew_version'
6
+
7
+ module Mnenv
8
+ module Homebrew
9
+ class Fetcher < Mnenv::Fetcher
10
+ GITHUB_REPO = 'metanorma/homebrew-metanorma'
11
+ API_URL = "https://api.github.com/repos/#{GITHUB_REPO}/tags".freeze
12
+
13
+ def fetch_all
14
+ uri = URI("#{API_URL}?per_page=100")
15
+ versions = []
16
+ page = 0
17
+
18
+ loop do
19
+ data = fetch_json(uri)
20
+ data.each do |tag|
21
+ name = tag['name']
22
+ next unless name.match?(/^v\d+\.\d+\.\d+$/)
23
+
24
+ versions << HomebrewVersion.new(
25
+ version: name.sub(/^v/, ''),
26
+ tag_name: name,
27
+ commit_sha: tag['commit']['sha']
28
+ )
29
+ end
30
+
31
+ page += 1
32
+ uri = URI("#{API_URL}?per_page=100&page=#{page}")
33
+ break if data.empty?
34
+ end
35
+
36
+ versions.sort
37
+ end
38
+
39
+ private
40
+
41
+ def default_repository = @default_repository ||= HomebrewRepository.new
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'repository'
4
+ require_relative 'models/homebrew_version'
5
+
6
+ module Mnenv
7
+ class HomebrewRepository < Repository
8
+ def version_class = HomebrewVersion
9
+ def source_name = :homebrew
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Mnenv
6
+ class JsonFormatter
7
+ def self.format_version(version)
8
+ {
9
+ 'version' => version.version,
10
+ 'published_at' => format_timestamp(version.published_at),
11
+ 'parsed_at' => format_timestamp(version.parsed_at),
12
+ 'display_name' => version.display_name
13
+ }.merge(version_specific_fields(version))
14
+ end
15
+
16
+ def self.format_versions(versions)
17
+ {
18
+ 'count' => versions.size,
19
+ 'latest' => versions.last&.version,
20
+ 'versions' => versions.map { |v| format_version(v) }
21
+ }
22
+ end
23
+
24
+ class << self
25
+ def format_timestamp(t) = t&.strftime('%Y-%m-%dT%H:%M:%SZ')
26
+
27
+ def version_specific_fields(version)
28
+ case version
29
+ when GemfileVersion
30
+ { 'gemfile_exists' => version.exists_locally? }
31
+ when SnapVersion
32
+ { 'revision' => version.revision, 'channel' => version.channel }
33
+ when HomebrewVersion
34
+ { 'tag_name' => version.tag_name, 'commit_sha' => version.commit_sha }
35
+ when ChocolateyVersion
36
+ { 'package_name' => version.package_name, 'is_pre_release' => version.is_pre_release }
37
+ else
38
+ {}
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'paint'
4
+
5
+ module Mnenv
6
+ class Logger
7
+ # Emojis for different log levels
8
+ EMOJIS = {
9
+ info: 'ℹ️',
10
+ success: '✅',
11
+ warning: '⚠️',
12
+ error: '❌',
13
+ pulling: '📥',
14
+ extracting: '📦',
15
+ skipping: '⏭️',
16
+ cleaning: '🧹'
17
+ }.freeze
18
+
19
+ class << self
20
+ def info(message)
21
+ puts Paint["#{EMOJIS[:info]} #{message}", :cyan]
22
+ end
23
+
24
+ def success(message)
25
+ puts Paint["#{EMOJIS[:success]} #{message}", :green]
26
+ end
27
+
28
+ def warning(message)
29
+ warn Paint["#{EMOJIS[:warning]} WARNING: #{message}", :yellow]
30
+ end
31
+
32
+ def error(message)
33
+ warn Paint["#{EMOJIS[:error]} ERROR: #{message}", :red, :bold]
34
+ end
35
+
36
+ def pulling(version)
37
+ puts Paint["#{EMOJIS[:pulling]} Pulling metanorma/metanorma:#{version}...", :blue]
38
+ end
39
+
40
+ def extracted(version, from: nil)
41
+ message = " #{EMOJIS[:extracting]} Extracted to v#{version}/"
42
+ message += " (from #{from})" if from
43
+ puts Paint[message, :green]
44
+ end
45
+
46
+ def skipping(version)
47
+ puts Paint[" #{EMOJIS[:skipping]} Skipping v#{version}/ (already exists)", :yellow]
48
+ end
49
+
50
+ def header(message)
51
+ puts "\n" + Paint["=== #{message} ===", :bold, :white] + "\n"
52
+ end
53
+
54
+ def section(message)
55
+ puts Paint["▸ #{message}", :cyan]
56
+ end
57
+
58
+ def sub(message)
59
+ puts Paint[" • #{message}", :gray]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+
5
+ module Mnenv
6
+ class ChocolateyVersion < ArtifactVersion
7
+ attribute :package_name, :string, default: 'metanorma'
8
+ attribute :is_pre_release, :boolean, default: false
9
+
10
+ key_value do
11
+ map 'version', to: :version
12
+ map 'published_at', to: :published_at
13
+ map 'parsed_at', to: :parsed_at
14
+ map 'package_name', to: :package_name
15
+ map 'is_pre_release', to: :is_pre_release
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+
5
+ module Mnenv
6
+ class GemfileVersion < ArtifactVersion
7
+ attribute :gemfile_exists, :boolean, default: false
8
+ attribute :gemfile_path, :string
9
+ attribute :gemfile_lock_path, :string
10
+
11
+ key_value do
12
+ map 'version', to: :version
13
+ map 'published_at', to: :published_at
14
+ map 'parsed_at', to: :parsed_at
15
+ map 'gemfile_exists', to: :gemfile_exists
16
+ map 'gemfile_path', to: :gemfile_path
17
+ map 'gemfile_lock_path', to: :gemfile_lock_path
18
+ end
19
+
20
+ def data_dir = @data_dir ||= default_data_dir
21
+
22
+ def directory_path = File.join(data_dir, "v#{version}")
23
+
24
+ def gemfile_path_calc = File.join(directory_path, 'Gemfile')
25
+
26
+ def gemfile_lock_path_calc = File.join(directory_path, 'Gemfile.lock.archived')
27
+
28
+ def exists_locally?
29
+ File.directory?(directory_path) &&
30
+ File.file?(gemfile_path_calc) &&
31
+ File.file?(gemfile_lock_path_calc)
32
+ end
33
+
34
+ private
35
+
36
+ def default_data_dir
37
+ @default_data_dir ||= File.join(__dir__, '..', '..', '..', 'data', 'gemfile')
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+
5
+ module Mnenv
6
+ class HomebrewVersion < ArtifactVersion
7
+ attribute :tag_name, :string
8
+ attribute :commit_sha, :string
9
+
10
+ key_value do
11
+ map 'version', to: :version
12
+ map 'published_at', to: :published_at
13
+ map 'parsed_at', to: :parsed_at
14
+ map 'tag_name', to: :tag_name
15
+ map 'commit_sha', to: :commit_sha
16
+ end
17
+
18
+ def initialize(*args)
19
+ super
20
+ @tag_name ||= "v#{version}" if version
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+
5
+ module Mnenv
6
+ class SnapVersion < ArtifactVersion
7
+ attribute :revision, :integer
8
+ attribute :arch, :string, default: 'amd64'
9
+ attribute :channel, :string, default: 'stable'
10
+
11
+ key_value do
12
+ map 'version', to: :version
13
+ map 'published_at', to: :published_at
14
+ map 'parsed_at', to: :parsed_at
15
+ map 'revision', to: :revision
16
+ map 'arch', to: :arch
17
+ map 'channel', to: :channel
18
+ end
19
+
20
+ def display_name = revision ? "#{version}-#{revision}" : "v#{version}"
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ class ArtifactVersion < Lutaml::Model::Serializable
5
+ attribute :version, :string
6
+ attribute :published_at, :date_time
7
+ attribute :parsed_at, :date_time
8
+
9
+ key_value do
10
+ map 'version', to: :version
11
+ map 'published_at', to: :published_at
12
+ map 'parsed_at', to: :parsed_at
13
+ end
14
+
15
+ def <=>(other) = version_parts <=> other.version_parts
16
+
17
+ def display_name = "v#{version}"
18
+
19
+ protected
20
+
21
+ def version_parts = version.split('.').map(&:to_i)
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+
5
+ require_relative 'models/version'
6
+ require_relative 'models/gemfile_version'
7
+ require_relative 'models/snap_version'
8
+ require_relative 'models/homebrew_version'
9
+ require_relative 'models/chocolatey_version'
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ module Mnenv
7
+ class Repository
8
+ attr_reader :data_dir, :versions_file_path
9
+
10
+ def initialize(data_dir: nil)
11
+ @data_dir = data_dir || default_data_dir
12
+ @versions_file_path = File.join(@data_dir, 'versions.yaml')
13
+ @versions_cache = {}
14
+ load if File.file?(versions_file_path)
15
+ end
16
+
17
+ def find(version_number) = @versions_cache[version_number]
18
+
19
+ def all = @versions_cache.values.sort
20
+
21
+ def latest = all.last
22
+
23
+ def count = @versions_cache.size
24
+
25
+ def exists?(version_number) = @versions_cache.key?(version_number)
26
+
27
+ def save(version)
28
+ @versions_cache[version.version] = version
29
+ persist
30
+ end
31
+
32
+ def save_all(versions)
33
+ versions.each { |v| @versions_cache[v.version] = v }
34
+ persist
35
+ end
36
+
37
+ protected
38
+
39
+ def load
40
+ data = YAML.load_file(versions_file_path)
41
+ return if data.nil? || data['versions'].nil?
42
+
43
+ data['versions'].each do |version_hash|
44
+ version = version_class.new(version_hash)
45
+ @versions_cache[version.version] = version
46
+ end
47
+ end
48
+
49
+ def persist
50
+ FileUtils.mkdir_p(data_dir)
51
+ output = {
52
+ 'metadata' => metadata,
53
+ 'versions' => @versions_cache.values.sort.map { |v| version_to_hash(v) }
54
+ }
55
+ File.write(versions_file_path, output.to_yaml)
56
+ end
57
+
58
+ def metadata
59
+ {
60
+ 'generated_at' => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
61
+ 'source' => source_name,
62
+ 'count' => count,
63
+ 'latest_version' => latest&.version
64
+ }
65
+ end
66
+
67
+ def version_class = raise NotImplementedError
68
+
69
+ def source_name = raise NotImplementedError
70
+
71
+ def default_data_dir = File.join(__dir__, '..', '..', 'data', source_name.to_s)
72
+
73
+ private
74
+
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
+ }
81
+
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
99
+
100
+ hash
101
+ end
102
+
103
+ def format_timestamp(t) = t&.strftime('%Y-%m-%dT%H:%M:%SZ')
104
+ end
105
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../fetcher'
4
+ require_relative '../snap_repository'
5
+ require_relative '../models/snap_version'
6
+ require_relative '../logger'
7
+ require 'uri'
8
+ require 'json'
9
+ require 'fileutils'
10
+ require 'net/http'
11
+
12
+ module Mnenv
13
+ module Snap
14
+ # Fetches Snap versions from Snapcraft API and merges with historical YAML data.
15
+ # The YAML file (data/snap/versions.yaml) is the single source of truth.
16
+ # Fetcher loads existing YAML, merges with API data, saves back to YAML.
17
+ # Snap cannot support "revamp" as it would lose historical data.
18
+ class Fetcher < Mnenv::Fetcher
19
+ SNAP_NAME = 'metanorma'
20
+ SNAP_ID = 'QkvhpBkFKaDwHMR2LTS3S9Bm0Ek6io11'
21
+ METADATA_API_URL = 'https://api.snapcraft.io/api/v1/snaps/metadata'
22
+
23
+ CHANNELS = %w[stable candidate beta edge].freeze
24
+ ARCHITECTURES = %w[amd64 arm64].freeze
25
+
26
+ def fetch_all
27
+ # Load existing versions from YAML (single source of truth)
28
+ existing_map = repository.all.to_h { |v| [snap_key(v), v] }
29
+
30
+ # Fetch current heads from snap_metadata API
31
+ current_versions = fetch_current_heads
32
+
33
+ # Merge: keep existing, add/update current from API
34
+ version_map = {}
35
+
36
+ # Add all existing versions
37
+ existing_map.each_value { |v| version_map[snap_key(v)] = v }
38
+
39
+ # Add/update current from API (overrides existing if same key)
40
+ current_versions.each do |cv|
41
+ key = snap_key_hash(cv)
42
+ version_map[key] = SnapVersion.new(
43
+ version: cv.fetch('version'),
44
+ revision: cv.fetch('revision'),
45
+ arch: cv.fetch('arch'),
46
+ channel: cv.fetch('channel')
47
+ )
48
+ end
49
+
50
+ version_map.values.sort
51
+ end
52
+
53
+ private
54
+
55
+ def snap_key(version)
56
+ "#{version.version}-#{version.revision}-#{version.arch}-#{version.channel}"
57
+ end
58
+
59
+ def snap_key_hash(hash)
60
+ "#{hash.fetch('version')}-#{hash.fetch('revision')}-#{hash.fetch('arch')}-#{hash.fetch('channel')}"
61
+ end
62
+
63
+ # Fetch current heads from snap_metadata API for all channel/arch combinations
64
+ def fetch_current_heads
65
+ versions = []
66
+
67
+ CHANNELS.each do |channel|
68
+ ARCHITECTURES.each do |arch|
69
+ body = {
70
+ snaps: [{
71
+ snap_id: SNAP_ID,
72
+ channel: channel,
73
+ architecture: arch
74
+ }],
75
+ fields: %w[version revision channel architecture download_url]
76
+ }
77
+
78
+ uri = URI(METADATA_API_URL)
79
+ http = Net::HTTP.new(uri.host, uri.port)
80
+ http.use_ssl = true
81
+ request = Net::HTTP::Post.new(uri.path)
82
+ request['Content-Type'] = 'application/json'
83
+ request['X-Ubuntu-Series'] = '16'
84
+ request.body = body.to_json
85
+
86
+ response = http.request(request)
87
+ data = JSON.parse(response.body)
88
+
89
+ if data['_embedded'] && data['_embedded']['clickindex:package']
90
+ pkg = data['_embedded']['clickindex:package'][0]
91
+ versions << {
92
+ 'version' => pkg['version'],
93
+ 'revision' => pkg['revision'],
94
+ 'arch' => arch,
95
+ 'channel' => channel
96
+ }
97
+ end
98
+ rescue StandardError => e
99
+ Logger.warning "Failed to fetch #{channel}/#{arch}: #{e.message}"
100
+ end
101
+ end
102
+
103
+ versions
104
+ end
105
+
106
+ def default_repository = @default_repository ||= SnapRepository.new
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'repository'
4
+ require_relative 'models/snap_version'
5
+
6
+ module Mnenv
7
+ class SnapRepository < Repository
8
+ def version_class = SnapVersion
9
+ def source_name = :snap
10
+
11
+ # Override save and save_all to use composite key for Snap
12
+ # Since same version can have multiple revisions/arch/channels
13
+ def save(version)
14
+ @versions_cache[snap_key(version)] = version
15
+ persist
16
+ end
17
+
18
+ def save_all(versions)
19
+ versions.each { |v| @versions_cache[snap_key(v)] = v }
20
+ persist
21
+ end
22
+
23
+ # Override find to work with composite keys
24
+ def find(version_number)
25
+ @versions_cache.values.find { |v| v.version == version_number }
26
+ end
27
+
28
+ # Find by specific version, revision, arch, channel
29
+ def find_exact(version_number, revision, arch, channel)
30
+ @versions_cache[snap_key(version_number, revision, arch, channel)]
31
+ end
32
+
33
+ # Get all entries for a specific version
34
+ def find_all_by_version(version_number)
35
+ @versions_cache.values.select { |v| v.version == version_number }.sort
36
+ end
37
+
38
+ def exists?(version_number)
39
+ @versions_cache.values.any? { |v| v.version == version_number }
40
+ end
41
+
42
+ private
43
+
44
+ def snap_key(*args)
45
+ case args.size
46
+ when 1
47
+ # Single SnapVersion object
48
+ v = args.first
49
+ "#{v.version}-#{v.revision}-#{v.arch}-#{v.channel}"
50
+ when 4
51
+ # version, revision, arch, channel
52
+ version_number, revision, arch, channel = args
53
+ "#{version_number}-#{revision}-#{arch}-#{channel}"
54
+ else
55
+ raise ArgumentError, 'snap_key requires 1 or 4 arguments'
56
+ end
57
+ end
58
+
59
+ # Override load to handle snap composite keys
60
+ def load
61
+ data = YAML.load_file(versions_file_path)
62
+ return if data.nil? || data['versions'].nil?
63
+
64
+ data['versions'].each do |version_hash|
65
+ version = version_class.new(version_hash)
66
+ @versions_cache[snap_key(version)] = version
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnenv
4
+ VERSION = '0.1.0'
5
+ end
data/lib/mnenv.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require 'thor'
5
+
6
+ require_relative 'mnenv/models'
7
+ require_relative 'mnenv/commands'
8
+ require_relative 'mnenv/cli'
data/mnenv.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/mnenv/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'mnenv'
7
+ spec.version = Mnenv::VERSION
8
+ spec.authors = ['Ribose Inc.']
9
+ spec.email = ['open.source@ribose.com']
10
+
11
+ spec.summary = 'Version discovery and management for Metanorma (mnenv)'
12
+ spec.description = 'Unified interface for discovering and managing Metanorma ' \
13
+ 'versions from Gemfile (Docker), Snap, Homebrew, and Chocolatey sources. ' \
14
+ 'Provides mnenv CLI command for querying version information.'
15
+ spec.homepage = 'https://github.com/metanorma/versions'
16
+ spec.license = 'MIT'
17
+
18
+ spec.required_ruby_version = '>= 3.1'
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = spec.homepage
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ # Specify which files should be added to the gem
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) ||
28
+ f.start_with?('.')
29
+ end
30
+ end
31
+ spec.bindir = 'exe'
32
+ spec.executables = ['mnenv']
33
+ spec.require_paths = ['lib']
34
+
35
+ # Runtime dependencies
36
+ spec.add_dependency 'activesupport'
37
+ spec.add_dependency 'lutaml-model', '~> 0.7'
38
+ spec.add_dependency 'lutaml-xsd', '~> 1.0'
39
+ spec.add_dependency 'moxml'
40
+ spec.add_dependency 'nokogiri'
41
+ spec.add_dependency 'paint'
42
+ spec.add_dependency 'thor', '~> 1.0'
43
+ end