rubygems-update 3.1.2 → 3.2.6

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 (562) hide show
  1. checksums.yaml +4 -4
  2. data/{History.txt → CHANGELOG.md} +756 -489
  3. data/CODE_OF_CONDUCT.md +55 -19
  4. data/CONTRIBUTING.md +25 -11
  5. data/Manifest.txt +75 -104
  6. data/POLICIES.md +6 -12
  7. data/README.md +5 -5
  8. data/Rakefile +64 -92
  9. data/bin/update_rubygems +1 -1
  10. data/bundler/CHANGELOG.md +1636 -1422
  11. data/bundler/README.md +6 -8
  12. data/bundler/UPGRADING.md +18 -32
  13. data/bundler/bundler.gemspec +4 -4
  14. data/bundler/exe/bundle +3 -0
  15. data/bundler/lib/bundler.rb +32 -8
  16. data/bundler/lib/bundler/build_metadata.rb +4 -12
  17. data/bundler/lib/bundler/cli.rb +55 -21
  18. data/bundler/lib/bundler/cli/add.rb +1 -1
  19. data/bundler/lib/bundler/cli/binstubs.rb +6 -2
  20. data/bundler/lib/bundler/cli/cache.rb +1 -7
  21. data/bundler/lib/bundler/cli/clean.rb +1 -1
  22. data/bundler/lib/bundler/cli/common.rb +14 -0
  23. data/bundler/lib/bundler/cli/console.rb +1 -1
  24. data/bundler/lib/bundler/cli/doctor.rb +1 -1
  25. data/bundler/lib/bundler/cli/exec.rb +4 -4
  26. data/bundler/lib/bundler/cli/fund.rb +36 -0
  27. data/bundler/lib/bundler/cli/gem.rb +86 -11
  28. data/bundler/lib/bundler/cli/info.rb +15 -4
  29. data/bundler/lib/bundler/cli/init.rb +2 -2
  30. data/bundler/lib/bundler/cli/inject.rb +1 -1
  31. data/bundler/lib/bundler/cli/install.rb +16 -13
  32. data/bundler/lib/bundler/cli/issue.rb +2 -2
  33. data/bundler/lib/bundler/cli/list.rb +12 -10
  34. data/bundler/lib/bundler/cli/outdated.rb +87 -66
  35. data/bundler/lib/bundler/cli/plugin.rb +10 -0
  36. data/bundler/lib/bundler/cli/pristine.rb +5 -0
  37. data/bundler/lib/bundler/cli/show.rb +1 -1
  38. data/bundler/lib/bundler/cli/update.rb +3 -1
  39. data/bundler/lib/bundler/compact_index_client.rb +1 -1
  40. data/bundler/lib/bundler/compact_index_client/cache.rb +6 -14
  41. data/bundler/lib/bundler/compact_index_client/gem_parser.rb +28 -0
  42. data/bundler/lib/bundler/compact_index_client/updater.rb +5 -13
  43. data/bundler/lib/bundler/definition.rb +66 -82
  44. data/bundler/lib/bundler/dep_proxy.rb +16 -9
  45. data/bundler/lib/bundler/dependency.rb +3 -10
  46. data/bundler/lib/bundler/dsl.rb +5 -9
  47. data/bundler/lib/bundler/endpoint_specification.rb +1 -1
  48. data/bundler/lib/bundler/env.rb +1 -1
  49. data/bundler/lib/bundler/environment_preserver.rb +26 -2
  50. data/bundler/lib/bundler/errors.rb +1 -0
  51. data/bundler/lib/bundler/feature_flag.rb +0 -3
  52. data/bundler/lib/bundler/fetcher.rb +4 -3
  53. data/bundler/lib/bundler/fetcher/base.rb +1 -1
  54. data/bundler/lib/bundler/fetcher/compact_index.rb +1 -1
  55. data/bundler/lib/bundler/fetcher/downloader.rb +1 -1
  56. data/bundler/lib/bundler/fetcher/index.rb +3 -4
  57. data/bundler/lib/bundler/friendly_errors.rb +22 -13
  58. data/bundler/lib/bundler/gem_helper.rb +33 -19
  59. data/bundler/lib/bundler/gem_helpers.rb +36 -25
  60. data/bundler/lib/bundler/gem_version_promoter.rb +4 -4
  61. data/bundler/lib/bundler/graph.rb +1 -1
  62. data/bundler/lib/bundler/index.rb +6 -2
  63. data/bundler/lib/bundler/injector.rb +22 -4
  64. data/bundler/lib/bundler/inline.rb +2 -2
  65. data/bundler/lib/bundler/installer.rb +35 -32
  66. data/bundler/lib/bundler/installer/gem_installer.rb +3 -3
  67. data/bundler/lib/bundler/installer/parallel_installer.rb +10 -10
  68. data/bundler/lib/bundler/installer/standalone.rb +2 -2
  69. data/bundler/lib/bundler/lazy_specification.rb +35 -11
  70. data/bundler/lib/bundler/lockfile_generator.rb +1 -1
  71. data/bundler/lib/bundler/lockfile_parser.rb +1 -1
  72. data/bundler/lib/bundler/man/.document +1 -0
  73. data/bundler/{man → lib/bundler/man}/bundle-add.1 +1 -1
  74. data/bundler/{man/bundle-add.ronn → lib/bundler/man/bundle-add.1.ronn} +0 -0
  75. data/bundler/{man → lib/bundler/man}/bundle-binstubs.1 +5 -3
  76. data/bundler/{man/bundle-binstubs.ronn → lib/bundler/man/bundle-binstubs.1.ronn} +2 -4
  77. data/bundler/{man → lib/bundler/man}/bundle-cache.1 +1 -1
  78. data/bundler/{man/bundle-cache.ronn → lib/bundler/man/bundle-cache.1.ronn} +0 -0
  79. data/bundler/{man → lib/bundler/man}/bundle-check.1 +1 -1
  80. data/bundler/{man/bundle-check.ronn → lib/bundler/man/bundle-check.1.ronn} +0 -0
  81. data/bundler/{man → lib/bundler/man}/bundle-clean.1 +1 -1
  82. data/bundler/{man/bundle-clean.ronn → lib/bundler/man/bundle-clean.1.ronn} +0 -0
  83. data/bundler/{man → lib/bundler/man}/bundle-config.1 +16 -25
  84. data/bundler/{man/bundle-config.ronn → lib/bundler/man/bundle-config.1.ronn} +19 -30
  85. data/bundler/{man → lib/bundler/man}/bundle-doctor.1 +1 -1
  86. data/bundler/{man/bundle-doctor.ronn → lib/bundler/man/bundle-doctor.1.ronn} +0 -0
  87. data/bundler/{man → lib/bundler/man}/bundle-exec.1 +1 -1
  88. data/bundler/{man/bundle-exec.ronn → lib/bundler/man/bundle-exec.1.ronn} +0 -0
  89. data/bundler/{man → lib/bundler/man}/bundle-gem.1 +25 -3
  90. data/bundler/{man/bundle-gem.ronn → lib/bundler/man/bundle-gem.1.ronn} +30 -7
  91. data/bundler/{man → lib/bundler/man}/bundle-info.1 +1 -1
  92. data/bundler/{man/bundle-info.ronn → lib/bundler/man/bundle-info.1.ronn} +0 -0
  93. data/bundler/{man → lib/bundler/man}/bundle-init.1 +1 -1
  94. data/bundler/{man/bundle-init.ronn → lib/bundler/man/bundle-init.1.ronn} +0 -0
  95. data/bundler/{man → lib/bundler/man}/bundle-inject.1 +1 -1
  96. data/bundler/{man/bundle-inject.ronn → lib/bundler/man/bundle-inject.1.ronn} +0 -0
  97. data/bundler/{man → lib/bundler/man}/bundle-install.1 +30 -3
  98. data/bundler/{man/bundle-install.ronn → lib/bundler/man/bundle-install.1.ronn} +25 -3
  99. data/bundler/{man → lib/bundler/man}/bundle-list.1 +7 -7
  100. data/bundler/{man/bundle-list.ronn → lib/bundler/man/bundle-list.1.ronn} +6 -6
  101. data/bundler/{man → lib/bundler/man}/bundle-lock.1 +1 -1
  102. data/bundler/{man/bundle-lock.ronn → lib/bundler/man/bundle-lock.1.ronn} +0 -0
  103. data/bundler/{man → lib/bundler/man}/bundle-open.1 +1 -1
  104. data/bundler/{man/bundle-open.ronn → lib/bundler/man/bundle-open.1.ronn} +0 -0
  105. data/bundler/{man → lib/bundler/man}/bundle-outdated.1 +1 -1
  106. data/bundler/{man/bundle-outdated.ronn → lib/bundler/man/bundle-outdated.1.ronn} +0 -0
  107. data/bundler/{man → lib/bundler/man}/bundle-platform.1 +1 -1
  108. data/bundler/{man/bundle-platform.ronn → lib/bundler/man/bundle-platform.1.ronn} +0 -0
  109. data/bundler/{man → lib/bundler/man}/bundle-pristine.1 +1 -1
  110. data/bundler/{man/bundle-pristine.ronn → lib/bundler/man/bundle-pristine.1.ronn} +0 -0
  111. data/bundler/{man → lib/bundler/man}/bundle-remove.1 +1 -1
  112. data/bundler/{man/bundle-remove.ronn → lib/bundler/man/bundle-remove.1.ronn} +0 -0
  113. data/bundler/{man → lib/bundler/man}/bundle-show.1 +1 -1
  114. data/bundler/{man/bundle-show.ronn → lib/bundler/man/bundle-show.1.ronn} +0 -0
  115. data/bundler/{man → lib/bundler/man}/bundle-update.1 +1 -1
  116. data/bundler/{man/bundle-update.ronn → lib/bundler/man/bundle-update.1.ronn} +0 -0
  117. data/bundler/{man → lib/bundler/man}/bundle-viz.1 +1 -1
  118. data/bundler/{man/bundle-viz.ronn → lib/bundler/man/bundle-viz.1.ronn} +0 -0
  119. data/bundler/{man → lib/bundler/man}/bundle.1 +1 -1
  120. data/bundler/{man/bundle.ronn → lib/bundler/man/bundle.1.ronn} +0 -0
  121. data/bundler/{man → lib/bundler/man}/gemfile.5 +4 -4
  122. data/bundler/{man → lib/bundler/man}/gemfile.5.ronn +4 -4
  123. data/bundler/{man → lib/bundler/man}/index.txt +0 -0
  124. data/bundler/lib/bundler/mirror.rb +2 -2
  125. data/bundler/lib/bundler/plugin.rb +30 -5
  126. data/bundler/lib/bundler/plugin/api/source.rb +1 -1
  127. data/bundler/lib/bundler/plugin/dsl.rb +1 -1
  128. data/bundler/lib/bundler/plugin/index.rb +10 -1
  129. data/bundler/lib/bundler/plugin/installer.rb +1 -1
  130. data/bundler/lib/bundler/plugin/installer/rubygems.rb +1 -1
  131. data/bundler/lib/bundler/plugin/source_list.rb +1 -1
  132. data/bundler/lib/bundler/psyched_yaml.rb +0 -15
  133. data/bundler/lib/bundler/remote_specification.rb +5 -2
  134. data/bundler/lib/bundler/resolver.rb +43 -19
  135. data/bundler/lib/bundler/resolver/spec_group.rb +39 -24
  136. data/bundler/lib/bundler/retry.rb +1 -1
  137. data/bundler/lib/bundler/ruby_version.rb +1 -1
  138. data/bundler/lib/bundler/rubygems_ext.rb +69 -9
  139. data/bundler/lib/bundler/rubygems_gem_installer.rb +3 -9
  140. data/bundler/lib/bundler/rubygems_integration.rb +26 -61
  141. data/bundler/lib/bundler/runtime.rb +4 -14
  142. data/bundler/lib/bundler/settings.rb +49 -46
  143. data/bundler/lib/bundler/shared_helpers.rb +2 -2
  144. data/bundler/lib/bundler/similarity_detector.rb +1 -1
  145. data/bundler/lib/bundler/source.rb +1 -1
  146. data/bundler/lib/bundler/source/git.rb +23 -21
  147. data/bundler/lib/bundler/source/git/git_proxy.rb +82 -80
  148. data/bundler/lib/bundler/source/path.rb +7 -3
  149. data/bundler/lib/bundler/source/path/installer.rb +10 -10
  150. data/bundler/lib/bundler/source/rubygems.rb +23 -17
  151. data/bundler/lib/bundler/source/rubygems/remote.rb +1 -1
  152. data/bundler/lib/bundler/source_list.rb +2 -2
  153. data/bundler/lib/bundler/spec_set.rb +8 -10
  154. data/bundler/lib/bundler/stub_specification.rb +17 -7
  155. data/bundler/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +57 -47
  156. data/bundler/lib/bundler/templates/newgem/Gemfile.tt +9 -1
  157. data/bundler/lib/bundler/templates/newgem/README.md.tt +1 -2
  158. data/bundler/lib/bundler/templates/newgem/Rakefile.tt +19 -5
  159. data/bundler/lib/bundler/templates/newgem/bin/console.tt +1 -0
  160. data/bundler/lib/bundler/templates/newgem/circleci/config.yml.tt +13 -0
  161. data/bundler/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +2 -0
  162. data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +18 -0
  163. data/bundler/lib/bundler/templates/newgem/gitlab-ci.yml.tt +9 -0
  164. data/bundler/lib/bundler/templates/newgem/lib/newgem.rb.tt +4 -2
  165. data/bundler/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +2 -0
  166. data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +15 -7
  167. data/bundler/lib/bundler/templates/newgem/rubocop.yml.tt +13 -0
  168. data/bundler/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +2 -0
  169. data/bundler/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -1
  170. data/bundler/lib/bundler/templates/newgem/test/{newgem_test.rb.tt → minitest/newgem_test.rb.tt} +2 -0
  171. data/bundler/lib/bundler/templates/newgem/test/{test_helper.rb.tt → minitest/test_helper.rb.tt} +2 -0
  172. data/bundler/lib/bundler/templates/newgem/test/test-unit/newgem_test.rb.tt +15 -0
  173. data/bundler/lib/bundler/templates/newgem/test/test-unit/test_helper.rb.tt +6 -0
  174. data/bundler/lib/bundler/ui/shell.rb +5 -5
  175. data/bundler/lib/bundler/uri_credentials_filter.rb +3 -1
  176. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -1
  177. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +34 -1
  178. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +2 -2
  179. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +1 -1
  180. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +1 -1
  181. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +48 -46
  182. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +79 -208
  183. data/bundler/lib/bundler/vendor/thor/lib/thor.rb +0 -7
  184. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +2 -1
  185. data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +9 -0
  186. data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  187. data/bundler/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +154 -0
  188. data/bundler/lib/bundler/vendored_persistent.rb +0 -7
  189. data/bundler/lib/bundler/vendored_tmpdir.rb +4 -0
  190. data/bundler/lib/bundler/version.rb +1 -1
  191. data/bundler/lib/bundler/worker.rb +1 -1
  192. data/bundler/lib/bundler/yaml_serializer.rb +1 -1
  193. data/lib/rubygems.rb +138 -187
  194. data/lib/rubygems/available_set.rb +4 -6
  195. data/lib/rubygems/basic_specification.rb +12 -10
  196. data/lib/rubygems/bundler_version_finder.rb +14 -9
  197. data/lib/rubygems/command.rb +17 -17
  198. data/lib/rubygems/command_manager.rb +5 -6
  199. data/lib/rubygems/commands/build_command.rb +40 -20
  200. data/lib/rubygems/commands/cert_command.rb +2 -10
  201. data/lib/rubygems/commands/check_command.rb +0 -2
  202. data/lib/rubygems/commands/cleanup_command.rb +11 -7
  203. data/lib/rubygems/commands/contents_command.rb +4 -6
  204. data/lib/rubygems/commands/dependency_command.rb +6 -8
  205. data/lib/rubygems/commands/environment_command.rb +1 -3
  206. data/lib/rubygems/commands/fetch_command.rb +2 -4
  207. data/lib/rubygems/commands/generate_index_command.rb +0 -2
  208. data/lib/rubygems/commands/help_command.rb +4 -4
  209. data/lib/rubygems/commands/info_command.rb +8 -5
  210. data/lib/rubygems/commands/install_command.rb +3 -5
  211. data/lib/rubygems/commands/list_command.rb +8 -7
  212. data/lib/rubygems/commands/lock_command.rb +1 -3
  213. data/lib/rubygems/commands/mirror_command.rb +0 -2
  214. data/lib/rubygems/commands/open_command.rb +0 -4
  215. data/lib/rubygems/commands/outdated_command.rb +0 -2
  216. data/lib/rubygems/commands/owner_command.rb +9 -4
  217. data/lib/rubygems/commands/pristine_command.rb +11 -5
  218. data/lib/rubygems/commands/push_command.rb +10 -47
  219. data/lib/rubygems/commands/query_command.rb +14 -344
  220. data/lib/rubygems/commands/rdoc_command.rb +0 -2
  221. data/lib/rubygems/commands/search_command.rb +7 -7
  222. data/lib/rubygems/commands/server_command.rb +3 -1
  223. data/lib/rubygems/commands/setup_command.rb +101 -74
  224. data/lib/rubygems/commands/signin_command.rb +0 -2
  225. data/lib/rubygems/commands/signout_command.rb +0 -2
  226. data/lib/rubygems/commands/sources_command.rb +9 -7
  227. data/lib/rubygems/commands/specification_command.rb +8 -4
  228. data/lib/rubygems/commands/stale_command.rb +1 -3
  229. data/lib/rubygems/commands/uninstall_command.rb +2 -4
  230. data/lib/rubygems/commands/unpack_command.rb +1 -3
  231. data/lib/rubygems/commands/update_command.rb +59 -14
  232. data/lib/rubygems/commands/which_command.rb +0 -2
  233. data/lib/rubygems/commands/yank_command.rb +4 -7
  234. data/lib/rubygems/config_file.rb +11 -4
  235. data/lib/rubygems/core_ext/kernel_require.rb +29 -36
  236. data/lib/rubygems/core_ext/kernel_warn.rb +12 -13
  237. data/lib/rubygems/defaults.rb +101 -7
  238. data/lib/rubygems/dependency.rb +3 -8
  239. data/lib/rubygems/dependency_installer.rb +6 -78
  240. data/lib/rubygems/dependency_list.rb +7 -9
  241. data/lib/rubygems/deprecate.rb +46 -1
  242. data/lib/rubygems/doctor.rb +4 -4
  243. data/lib/rubygems/errors.rb +3 -14
  244. data/lib/rubygems/exceptions.rb +2 -33
  245. data/lib/rubygems/ext.rb +6 -6
  246. data/lib/rubygems/ext/build_error.rb +2 -0
  247. data/lib/rubygems/ext/builder.rb +21 -39
  248. data/lib/rubygems/ext/cmake_builder.rb +6 -9
  249. data/lib/rubygems/ext/configure_builder.rb +5 -8
  250. data/lib/rubygems/ext/ext_conf_builder.rb +21 -19
  251. data/lib/rubygems/ext/rake_builder.rb +4 -6
  252. data/lib/rubygems/gem_runner.rb +3 -10
  253. data/lib/rubygems/gemcutter_utilities.rb +102 -21
  254. data/lib/rubygems/indexer.rb +1 -22
  255. data/lib/rubygems/install_update_options.rb +7 -7
  256. data/lib/rubygems/installer.rb +59 -80
  257. data/lib/rubygems/installer_test_case.rb +25 -11
  258. data/lib/rubygems/installer_uninstaller_utils.rb +24 -0
  259. data/lib/rubygems/local_remote_options.rb +1 -1
  260. data/lib/rubygems/mock_gem_ui.rb +0 -6
  261. data/lib/rubygems/name_tuple.rb +3 -7
  262. data/lib/rubygems/openssl.rb +7 -0
  263. data/lib/rubygems/package.rb +14 -25
  264. data/lib/rubygems/package/digest_io.rb +0 -2
  265. data/lib/rubygems/package/file_source.rb +0 -2
  266. data/lib/rubygems/package/io_source.rb +0 -2
  267. data/lib/rubygems/package/old.rb +1 -3
  268. data/lib/rubygems/package/tar_header.rb +4 -6
  269. data/lib/rubygems/package/tar_reader.rb +0 -3
  270. data/lib/rubygems/package/tar_reader/entry.rb +0 -3
  271. data/lib/rubygems/package/tar_test_case.rb +2 -4
  272. data/lib/rubygems/package/tar_writer.rb +2 -12
  273. data/lib/rubygems/package_task.rb +1 -7
  274. data/lib/rubygems/path_support.rb +1 -3
  275. data/lib/rubygems/platform.rb +21 -16
  276. data/lib/rubygems/psych_tree.rb +0 -2
  277. data/lib/rubygems/query_utils.rb +353 -0
  278. data/lib/rubygems/rdoc.rb +0 -12
  279. data/lib/rubygems/remote_fetcher.rb +14 -29
  280. data/lib/rubygems/request.rb +4 -11
  281. data/lib/rubygems/request/connection_pools.rb +1 -5
  282. data/lib/rubygems/request/http_pool.rb +0 -2
  283. data/lib/rubygems/request/https_pool.rb +0 -2
  284. data/lib/rubygems/request_set.rb +7 -20
  285. data/lib/rubygems/request_set/gem_dependency_api.rb +6 -8
  286. data/lib/rubygems/request_set/lockfile.rb +8 -12
  287. data/lib/rubygems/request_set/lockfile/parser.rb +0 -2
  288. data/lib/rubygems/request_set/lockfile/tokenizer.rb +1 -3
  289. data/lib/rubygems/requirement.rb +21 -22
  290. data/lib/rubygems/resolver.rb +14 -12
  291. data/lib/rubygems/resolver/activation_request.rb +9 -3
  292. data/lib/rubygems/resolver/api_set.rb +31 -24
  293. data/lib/rubygems/resolver/api_set/gem_parser.rb +20 -0
  294. data/lib/rubygems/resolver/api_specification.rb +24 -10
  295. data/lib/rubygems/resolver/best_set.rb +2 -4
  296. data/lib/rubygems/resolver/composed_set.rb +3 -5
  297. data/lib/rubygems/resolver/conflict.rb +2 -4
  298. data/lib/rubygems/resolver/current_set.rb +0 -2
  299. data/lib/rubygems/resolver/dependency_request.rb +1 -3
  300. data/lib/rubygems/resolver/git_set.rb +0 -2
  301. data/lib/rubygems/resolver/git_specification.rb +0 -2
  302. data/lib/rubygems/resolver/index_set.rb +1 -3
  303. data/lib/rubygems/resolver/index_specification.rb +29 -2
  304. data/lib/rubygems/resolver/installed_specification.rb +0 -2
  305. data/lib/rubygems/resolver/installer_set.rb +60 -13
  306. data/lib/rubygems/resolver/local_specification.rb +0 -2
  307. data/lib/rubygems/resolver/lock_set.rb +2 -4
  308. data/lib/rubygems/resolver/lock_specification.rb +0 -2
  309. data/lib/rubygems/resolver/molinillo/lib/molinillo.rb +6 -5
  310. data/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb +7 -0
  311. data/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb +1 -0
  312. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +39 -5
  313. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb +1 -0
  314. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +2 -1
  315. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +2 -1
  316. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +2 -1
  317. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +2 -1
  318. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb +7 -6
  319. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb +2 -1
  320. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb +4 -3
  321. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb +43 -10
  322. data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +75 -7
  323. data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +2 -1
  324. data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb +1 -0
  325. data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb +3 -1
  326. data/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +506 -165
  327. data/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb +3 -2
  328. data/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb +8 -4
  329. data/lib/rubygems/resolver/requirement_list.rb +0 -2
  330. data/lib/rubygems/resolver/set.rb +0 -2
  331. data/lib/rubygems/resolver/source_set.rb +0 -2
  332. data/lib/rubygems/resolver/spec_specification.rb +14 -2
  333. data/lib/rubygems/resolver/specification.rb +13 -3
  334. data/lib/rubygems/resolver/stats.rb +0 -2
  335. data/lib/rubygems/resolver/vendor_set.rb +0 -2
  336. data/lib/rubygems/resolver/vendor_specification.rb +0 -2
  337. data/lib/rubygems/s3_uri_signer.rb +2 -8
  338. data/lib/rubygems/safe_yaml.rb +4 -4
  339. data/lib/rubygems/security.rb +27 -34
  340. data/lib/rubygems/security/policy.rb +4 -8
  341. data/lib/rubygems/security/signer.rb +5 -7
  342. data/lib/rubygems/security/trust_dir.rb +1 -3
  343. data/lib/rubygems/server.rb +16 -13
  344. data/lib/rubygems/source.rb +23 -12
  345. data/lib/rubygems/source/git.rb +7 -8
  346. data/lib/rubygems/source/installed.rb +0 -2
  347. data/lib/rubygems/source/local.rb +2 -4
  348. data/lib/rubygems/source/lock.rb +0 -2
  349. data/lib/rubygems/source/specific_file.rb +0 -2
  350. data/lib/rubygems/source/vendor.rb +0 -2
  351. data/lib/rubygems/source_list.rb +4 -7
  352. data/lib/rubygems/spec_fetcher.rb +19 -18
  353. data/lib/rubygems/specification.rb +122 -131
  354. data/lib/rubygems/specification_policy.rb +88 -30
  355. data/lib/rubygems/ssl_certs/{index.rubygems.org → rubygems.org}/GlobalSignRootCA.pem +0 -0
  356. data/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem +21 -0
  357. data/lib/rubygems/stub_specification.rb +1 -5
  358. data/lib/rubygems/syck_hack.rb +0 -2
  359. data/lib/rubygems/test_case.rb +120 -134
  360. data/lib/rubygems/test_utilities.rb +12 -19
  361. data/lib/rubygems/uninstaller.rb +35 -16
  362. data/lib/rubygems/uri_formatter.rb +2 -3
  363. data/lib/rubygems/uri_parser.rb +0 -2
  364. data/lib/rubygems/user_interaction.rb +1 -26
  365. data/lib/rubygems/util.rb +15 -3
  366. data/lib/rubygems/util/licenses.rb +4 -6
  367. data/lib/rubygems/util/list.rb +0 -2
  368. data/lib/rubygems/validator.rb +1 -3
  369. data/lib/rubygems/version.rb +5 -7
  370. data/lib/rubygems/version_option.rb +6 -0
  371. data/rubygems-update.gemspec +3 -3
  372. data/setup.rb +2 -7
  373. data/test/rubygems/data/null-required-rubygems-version.gemspec.rz +0 -0
  374. data/test/rubygems/plugin/load/rubygems_plugin.rb +0 -2
  375. data/test/rubygems/rubygems/commands/crash_command.rb +0 -2
  376. data/test/rubygems/rubygems_plugin.rb +0 -2
  377. data/test/rubygems/specifications/bar-0.0.2.gemspec +0 -2
  378. data/test/rubygems/specifications/rubyforge-0.0.1.gemspec +12 -0
  379. data/test/rubygems/test_bundled_ca.rb +42 -45
  380. data/test/rubygems/test_config.rb +0 -2
  381. data/test/rubygems/test_deprecate.rb +40 -7
  382. data/test/rubygems/test_gem.rb +156 -99
  383. data/test/rubygems/test_gem_available_set.rb +3 -5
  384. data/test/rubygems/test_gem_bundler_version_finder.rb +19 -3
  385. data/test/rubygems/test_gem_command.rb +24 -7
  386. data/test/rubygems/test_gem_command_manager.rb +36 -5
  387. data/test/rubygems/test_gem_commands_build_command.rb +250 -15
  388. data/test/rubygems/test_gem_commands_cert_command.rb +4 -6
  389. data/test/rubygems/test_gem_commands_check_command.rb +0 -2
  390. data/test/rubygems/test_gem_commands_cleanup_command.rb +14 -5
  391. data/test/rubygems/test_gem_commands_contents_command.rb +50 -19
  392. data/test/rubygems/test_gem_commands_dependency_command.rb +0 -2
  393. data/test/rubygems/test_gem_commands_environment_command.rb +21 -23
  394. data/test/rubygems/test_gem_commands_fetch_command.rb +0 -2
  395. data/test/rubygems/test_gem_commands_generate_index_command.rb +1 -7
  396. data/test/rubygems/test_gem_commands_help_command.rb +15 -4
  397. data/test/rubygems/test_gem_commands_info_command.rb +6 -8
  398. data/test/rubygems/test_gem_commands_install_command.rb +163 -34
  399. data/test/rubygems/test_gem_commands_list_command.rb +0 -2
  400. data/test/rubygems/test_gem_commands_lock_command.rb +0 -2
  401. data/test/rubygems/test_gem_commands_mirror.rb +1 -3
  402. data/test/rubygems/test_gem_commands_open_command.rb +4 -6
  403. data/test/rubygems/test_gem_commands_outdated_command.rb +0 -2
  404. data/test/rubygems/test_gem_commands_owner_command.rb +59 -5
  405. data/test/rubygems/test_gem_commands_pristine_command.rb +43 -12
  406. data/test/rubygems/test_gem_commands_push_command.rb +77 -9
  407. data/test/rubygems/test_gem_commands_query_command.rb +12 -12
  408. data/test/rubygems/test_gem_commands_search_command.rb +0 -2
  409. data/test/rubygems/test_gem_commands_server_command.rb +0 -2
  410. data/test/rubygems/test_gem_commands_setup_command.rb +160 -135
  411. data/test/rubygems/test_gem_commands_signin_command.rb +33 -9
  412. data/test/rubygems/test_gem_commands_signout_command.rb +0 -7
  413. data/test/rubygems/test_gem_commands_sources_command.rb +99 -3
  414. data/test/rubygems/test_gem_commands_specification_command.rb +46 -20
  415. data/test/rubygems/test_gem_commands_stale_command.rb +0 -2
  416. data/test/rubygems/test_gem_commands_uninstall_command.rb +2 -3
  417. data/test/rubygems/test_gem_commands_unpack_command.rb +0 -2
  418. data/test/rubygems/test_gem_commands_update_command.rb +116 -7
  419. data/test/rubygems/test_gem_commands_which_command.rb +3 -5
  420. data/test/rubygems/test_gem_commands_yank_command.rb +44 -8
  421. data/test/rubygems/test_gem_config_file.rb +7 -12
  422. data/test/rubygems/test_gem_dependency.rb +0 -2
  423. data/test/rubygems/test_gem_dependency_installer.rb +116 -239
  424. data/test/rubygems/test_gem_dependency_list.rb +10 -12
  425. data/test/rubygems/test_gem_dependency_resolution_error.rb +1 -3
  426. data/test/rubygems/test_gem_doctor.rb +28 -2
  427. data/test/rubygems/test_gem_ext_builder.rb +50 -47
  428. data/test/rubygems/test_gem_ext_cmake_builder.rb +16 -25
  429. data/test/rubygems/test_gem_ext_configure_builder.rb +6 -22
  430. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +9 -29
  431. data/test/rubygems/test_gem_ext_rake_builder.rb +39 -24
  432. data/test/rubygems/test_gem_gem_runner.rb +44 -1
  433. data/test/rubygems/test_gem_gemcutter_utilities.rb +8 -5
  434. data/test/rubygems/test_gem_impossible_dependencies_error.rb +0 -2
  435. data/test/rubygems/test_gem_indexer.rb +9 -15
  436. data/test/rubygems/test_gem_install_update_options.rb +14 -4
  437. data/test/rubygems/test_gem_installer.rb +258 -115
  438. data/test/rubygems/test_gem_local_remote_options.rb +0 -2
  439. data/test/rubygems/test_gem_name_tuple.rb +0 -2
  440. data/test/rubygems/test_gem_package.rb +41 -39
  441. data/test/rubygems/test_gem_package_old.rb +4 -6
  442. data/test/rubygems/test_gem_package_tar_header.rb +18 -1
  443. data/test/rubygems/test_gem_package_tar_reader.rb +0 -2
  444. data/test/rubygems/test_gem_package_tar_reader_entry.rb +0 -2
  445. data/test/rubygems/test_gem_package_tar_writer.rb +9 -6
  446. data/test/rubygems/test_gem_package_task.rb +46 -13
  447. data/test/rubygems/test_gem_path_support.rb +0 -2
  448. data/test/rubygems/test_gem_platform.rb +71 -6
  449. data/test/rubygems/test_gem_rdoc.rb +0 -2
  450. data/test/rubygems/test_gem_remote_fetcher.rb +169 -212
  451. data/test/rubygems/test_gem_request.rb +13 -17
  452. data/test/rubygems/test_gem_request_connection_pools.rb +0 -4
  453. data/test/rubygems/test_gem_request_set.rb +72 -22
  454. data/test/rubygems/test_gem_request_set_gem_dependency_api.rb +3 -5
  455. data/test/rubygems/test_gem_request_set_lockfile.rb +4 -6
  456. data/test/rubygems/test_gem_request_set_lockfile_parser.rb +9 -11
  457. data/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb +118 -120
  458. data/test/rubygems/test_gem_requirement.rb +43 -3
  459. data/test/rubygems/test_gem_resolver.rb +6 -8
  460. data/test/rubygems/test_gem_resolver_activation_request.rb +0 -2
  461. data/test/rubygems/test_gem_resolver_api_set.rb +60 -59
  462. data/test/rubygems/test_gem_resolver_api_specification.rb +3 -5
  463. data/test/rubygems/test_gem_resolver_best_set.rb +27 -6
  464. data/test/rubygems/test_gem_resolver_composed_set.rb +0 -2
  465. data/test/rubygems/test_gem_resolver_conflict.rb +1 -3
  466. data/test/rubygems/test_gem_resolver_dependency_request.rb +0 -2
  467. data/test/rubygems/test_gem_resolver_git_set.rb +0 -2
  468. data/test/rubygems/test_gem_resolver_git_specification.rb +0 -2
  469. data/test/rubygems/test_gem_resolver_index_set.rb +2 -4
  470. data/test/rubygems/test_gem_resolver_index_specification.rb +0 -2
  471. data/test/rubygems/test_gem_resolver_installed_specification.rb +0 -2
  472. data/test/rubygems/test_gem_resolver_installer_set.rb +7 -9
  473. data/test/rubygems/test_gem_resolver_local_specification.rb +0 -2
  474. data/test/rubygems/test_gem_resolver_lock_set.rb +3 -5
  475. data/test/rubygems/test_gem_resolver_lock_specification.rb +0 -2
  476. data/test/rubygems/test_gem_resolver_requirement_list.rb +0 -2
  477. data/test/rubygems/test_gem_resolver_specification.rb +0 -4
  478. data/test/rubygems/test_gem_resolver_vendor_set.rb +1 -3
  479. data/test/rubygems/test_gem_resolver_vendor_specification.rb +0 -2
  480. data/test/rubygems/test_gem_security.rb +22 -24
  481. data/test/rubygems/test_gem_security_policy.rb +7 -12
  482. data/test/rubygems/test_gem_security_signer.rb +10 -12
  483. data/test/rubygems/test_gem_security_trust_dir.rb +4 -6
  484. data/test/rubygems/test_gem_server.rb +10 -14
  485. data/test/rubygems/test_gem_silent_ui.rb +0 -2
  486. data/test/rubygems/test_gem_source.rb +19 -18
  487. data/test/rubygems/test_gem_source_fetch_problem.rb +0 -2
  488. data/test/rubygems/test_gem_source_git.rb +12 -13
  489. data/test/rubygems/test_gem_source_installed.rb +7 -9
  490. data/test/rubygems/test_gem_source_list.rb +1 -2
  491. data/test/rubygems/test_gem_source_local.rb +8 -10
  492. data/test/rubygems/test_gem_source_lock.rb +10 -12
  493. data/test/rubygems/test_gem_source_specific_file.rb +7 -9
  494. data/test/rubygems/test_gem_source_subpath_problem.rb +49 -0
  495. data/test/rubygems/test_gem_source_vendor.rb +7 -9
  496. data/test/rubygems/test_gem_spec_fetcher.rb +11 -4
  497. data/test/rubygems/test_gem_specification.rb +188 -131
  498. data/test/rubygems/test_gem_stream_ui.rb +3 -3
  499. data/test/rubygems/test_gem_stub_specification.rb +4 -7
  500. data/test/rubygems/test_gem_text.rb +1 -3
  501. data/test/rubygems/test_gem_uninstaller.rb +134 -12
  502. data/test/rubygems/test_gem_unsatisfiable_dependency_error.rb +0 -2
  503. data/test/rubygems/test_gem_uri_formatter.rb +0 -2
  504. data/test/rubygems/test_gem_util.rb +7 -7
  505. data/test/rubygems/test_gem_validator.rb +1 -3
  506. data/test/rubygems/test_gem_version.rb +1 -3
  507. data/test/rubygems/test_gem_version_option.rb +1 -3
  508. data/test/rubygems/test_kernel.rb +25 -10
  509. data/test/rubygems/test_project_sanity.rb +7 -2
  510. data/test/rubygems/test_remote_fetch_error.rb +0 -2
  511. data/test/rubygems/test_require.rb +291 -56
  512. data/test/test_changelog_generator.rb +17 -0
  513. metadata +79 -133
  514. data/.bundle/config +0 -2
  515. data/.rubocop.yml +0 -91
  516. data/Gemfile +0 -8
  517. data/Gemfile.lock +0 -43
  518. data/bundler/CODE_OF_CONDUCT.md +0 -136
  519. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +0 -26
  520. data/bundler/man/bundle-add.1.txt +0 -58
  521. data/bundler/man/bundle-binstubs.1.txt +0 -48
  522. data/bundler/man/bundle-cache.1.txt +0 -78
  523. data/bundler/man/bundle-check.1.txt +0 -33
  524. data/bundler/man/bundle-clean.1.txt +0 -26
  525. data/bundler/man/bundle-config.1.txt +0 -528
  526. data/bundler/man/bundle-doctor.1.txt +0 -44
  527. data/bundler/man/bundle-exec.1.txt +0 -178
  528. data/bundler/man/bundle-gem.1.txt +0 -91
  529. data/bundler/man/bundle-info.1.txt +0 -21
  530. data/bundler/man/bundle-init.1.txt +0 -34
  531. data/bundler/man/bundle-inject.1.txt +0 -32
  532. data/bundler/man/bundle-install.1.txt +0 -401
  533. data/bundler/man/bundle-list.1.txt +0 -43
  534. data/bundler/man/bundle-lock.1.txt +0 -93
  535. data/bundler/man/bundle-open.1.txt +0 -29
  536. data/bundler/man/bundle-outdated.1.txt +0 -131
  537. data/bundler/man/bundle-platform.1.txt +0 -57
  538. data/bundler/man/bundle-pristine.1.txt +0 -44
  539. data/bundler/man/bundle-remove.1.txt +0 -34
  540. data/bundler/man/bundle-show.1.txt +0 -27
  541. data/bundler/man/bundle-update.1.txt +0 -390
  542. data/bundler/man/bundle-viz.1.txt +0 -39
  543. data/bundler/man/bundle.1.txt +0 -116
  544. data/bundler/man/gemfile.5.txt +0 -649
  545. data/lib/rubygems/source_local.rb +0 -7
  546. data/lib/rubygems/source_specific_file.rb +0 -6
  547. data/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +0 -23
  548. data/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +0 -25
  549. data/lib/ubygems.rb +0 -14
  550. data/tmp/.keep +0 -0
  551. data/util/CL2notes +0 -55
  552. data/util/bisect +0 -10
  553. data/util/ci.sh +0 -62
  554. data/util/cops/deprecations.rb +0 -52
  555. data/util/create_certs.rb +0 -171
  556. data/util/create_certs.sh +0 -27
  557. data/util/create_encrypted_key.rb +0 -16
  558. data/util/generate_spdx_license_list.rb +0 -63
  559. data/util/patch_with_prs.rb +0 -77
  560. data/util/rubocop +0 -8
  561. data/util/update_bundled_ca_certificates.rb +0 -139
  562. data/util/update_changelog.rb +0 -67
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  module Delegates
4
5
  # Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'set'
3
4
  require 'tsort'
4
5
 
5
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log'
6
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex'
6
+ require_relative 'dependency_graph/log'
7
+ require_relative 'dependency_graph/vertex'
7
8
 
8
9
  module Gem::Resolver::Molinillo
9
10
  # A directed acyclic graph that is tuned to hold named dependencies
@@ -123,6 +124,7 @@ module Gem::Resolver::Molinillo
123
124
  dot.join("\n")
124
125
  end
125
126
 
127
+ # @param [DependencyGraph] other
126
128
  # @return [Boolean] whether the two dependency graphs are equal, determined
127
129
  # by a recursive traversal of each {#root_vertices} and its
128
130
  # {Vertex#successors}
@@ -147,8 +149,8 @@ module Gem::Resolver::Molinillo
147
149
  vertex = add_vertex(name, payload, root)
148
150
  vertex.explicit_requirements << requirement if root
149
151
  parent_names.each do |parent_name|
150
- parent_node = vertex_named(parent_name)
151
- add_edge(parent_node, vertex, requirement)
152
+ parent_vertex = vertex_named(parent_name)
153
+ add_edge(parent_vertex, vertex, requirement)
152
154
  end
153
155
  vertex
154
156
  end
@@ -189,7 +191,7 @@ module Gem::Resolver::Molinillo
189
191
  # @return [Edge] the added edge
190
192
  def add_edge(origin, destination, requirement)
191
193
  if destination.path_to?(origin)
192
- raise CircularDependencyError.new([origin, destination])
194
+ raise CircularDependencyError.new(path(destination, origin))
193
195
  end
194
196
  add_edge_no_circular(origin, destination, requirement)
195
197
  end
@@ -218,5 +220,37 @@ module Gem::Resolver::Molinillo
218
220
  def add_edge_no_circular(origin, destination, requirement)
219
221
  log.add_edge_no_circular(self, origin.name, destination.name, requirement)
220
222
  end
223
+
224
+ # Returns the path between two vertices
225
+ # @raise [ArgumentError] if there is no path between the vertices
226
+ # @param [Vertex] from
227
+ # @param [Vertex] to
228
+ # @return [Array<Vertex>] the shortest path from `from` to `to`
229
+ def path(from, to)
230
+ distances = Hash.new(vertices.size + 1)
231
+ distances[from.name] = 0
232
+ predecessors = {}
233
+ each do |vertex|
234
+ vertex.successors.each do |successor|
235
+ if distances[successor.name] > distances[vertex.name] + 1
236
+ distances[successor.name] = distances[vertex.name] + 1
237
+ predecessors[successor] = vertex
238
+ end
239
+ end
240
+ end
241
+
242
+ path = [to]
243
+ while before = predecessors[to]
244
+ path << before
245
+ to = before
246
+ break if to == from
247
+ end
248
+
249
+ unless path.last.equal?(from)
250
+ raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
251
+ end
252
+
253
+ path.reverse
254
+ end
221
255
  end
222
256
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  class DependencyGraph
4
5
  # An action that modifies a {DependencyGraph} that is reversible.
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular'
3
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex'
4
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge'
5
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named'
6
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload'
7
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag'
2
+
3
+ require_relative 'add_edge_no_circular'
4
+ require_relative 'add_vertex'
5
+ require_relative 'delete_edge'
6
+ require_relative 'detach_vertex_named'
7
+ require_relative 'set_payload'
8
+ require_relative 'tag'
8
9
 
9
10
  module Gem::Resolver::Molinillo
10
11
  class DependencyGraph
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
2
+
3
+ require_relative 'action'
3
4
  module Gem::Resolver::Molinillo
4
5
  class DependencyGraph
5
6
  # @!visibility private
@@ -13,11 +14,11 @@ module Gem::Resolver::Molinillo
13
14
  end
14
15
 
15
16
  # (see Action#up)
16
- def up(_graph)
17
+ def up(graph)
17
18
  end
18
19
 
19
20
  # (see Action#down)
20
- def down(_graph)
21
+ def down(graph)
21
22
  end
22
23
 
23
24
  # @!group Tag
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  class DependencyGraph
4
5
  # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
@@ -32,7 +33,7 @@ module Gem::Resolver::Molinillo
32
33
  # @return [Array<Object>] all of the requirements that required
33
34
  # this vertex
34
35
  def requirements
35
- incoming_edges.map(&:requirement) + explicit_requirements
36
+ (incoming_edges.map(&:requirement) + explicit_requirements).uniq
36
37
  end
37
38
 
38
39
  # @return [Array<Edge>] the edges of {#graph} that have `self` as their
@@ -49,14 +50,25 @@ module Gem::Resolver::Molinillo
49
50
  incoming_edges.map(&:origin)
50
51
  end
51
52
 
52
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is a
53
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
53
54
  # {#descendent?}
54
55
  def recursive_predecessors
55
- vertices = predecessors
56
- vertices += vertices.map(&:recursive_predecessors).flatten(1)
57
- vertices.uniq!
56
+ _recursive_predecessors
57
+ end
58
+
59
+ # @param [Set<Vertex>] vertices the set to add the predecessors to
60
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
61
+ # {#descendent?}
62
+ def _recursive_predecessors(vertices = Set.new)
63
+ incoming_edges.each do |edge|
64
+ vertex = edge.origin
65
+ next unless vertices.add?(vertex)
66
+ vertex._recursive_predecessors(vertices)
67
+ end
68
+
58
69
  vertices
59
70
  end
71
+ protected :_recursive_predecessors
60
72
 
61
73
  # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
62
74
  # `self` as their {Edge#origin}
@@ -64,14 +76,25 @@ module Gem::Resolver::Molinillo
64
76
  outgoing_edges.map(&:destination)
65
77
  end
66
78
 
67
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is an
79
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
68
80
  # {#ancestor?}
69
81
  def recursive_successors
70
- vertices = successors
71
- vertices += vertices.map(&:recursive_successors).flatten(1)
72
- vertices.uniq!
82
+ _recursive_successors
83
+ end
84
+
85
+ # @param [Set<Vertex>] vertices the set to add the successors to
86
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
87
+ # {#ancestor?}
88
+ def _recursive_successors(vertices = Set.new)
89
+ outgoing_edges.each do |edge|
90
+ vertex = edge.destination
91
+ next unless vertices.add?(vertex)
92
+ vertex._recursive_successors(vertices)
93
+ end
94
+
73
95
  vertices
74
96
  end
97
+ protected :_recursive_successors
75
98
 
76
99
  # @return [String] a string suitable for debugging
77
100
  def inspect
@@ -107,11 +130,21 @@ module Gem::Resolver::Molinillo
107
130
  # dependency graph?
108
131
  # @return true iff there is a path following edges within this {#graph}
109
132
  def path_to?(other)
110
- equal?(other) || successors.any? { |v| v.path_to?(other) }
133
+ _path_to?(other)
111
134
  end
112
135
 
113
136
  alias descendent? path_to?
114
137
 
138
+ # @param [Vertex] other the vertex to check if there's a path to
139
+ # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
140
+ # @return [Boolean] whether there is a path to `other` from `self`
141
+ def _path_to?(other, visited = Set.new)
142
+ return false unless visited.add?(self)
143
+ return true if equal?(other)
144
+ successors.any? { |v| v._path_to?(other, visited) }
145
+ end
146
+ protected :_path_to?
147
+
115
148
  # Is there a path from `other` to `self` following edges in the
116
149
  # dependency graph?
117
150
  # @return true iff there is a path following edges within this {#graph}
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  # An error that occurred during the resolution process
4
5
  class ResolverError < StandardError; end
@@ -17,7 +18,7 @@ module Gem::Resolver::Molinillo
17
18
  # @param [Array<Object>] required_by @see {#required_by}
18
19
  def initialize(dependency, required_by = [])
19
20
  @dependency = dependency
20
- @required_by = required_by
21
+ @required_by = required_by.uniq
21
22
  super()
22
23
  end
23
24
 
@@ -41,11 +42,11 @@ module Gem::Resolver::Molinillo
41
42
  attr_reader :dependencies
42
43
 
43
44
  # Initializes a new error with the given circular vertices.
44
- # @param [Array<DependencyGraph::Vertex>] nodes the nodes in the dependency
45
+ # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
45
46
  # that caused the error
46
- def initialize(nodes)
47
- super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}"
48
- @dependencies = nodes.map(&:payload).to_set
47
+ def initialize(vertices)
48
+ super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
49
+ @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
49
50
  end
50
51
  end
51
52
 
@@ -55,11 +56,16 @@ module Gem::Resolver::Molinillo
55
56
  # resolution to fail
56
57
  attr_reader :conflicts
57
58
 
59
+ # @return [SpecificationProvider] the specification provider used during
60
+ # resolution
61
+ attr_reader :specification_provider
62
+
58
63
  # Initializes a new error with the given version conflicts.
59
64
  # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
60
- def initialize(conflicts)
65
+ # @param [SpecificationProvider] specification_provider see {#specification_provider}
66
+ def initialize(conflicts, specification_provider)
61
67
  pairs = []
62
- conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting|
68
+ conflicts.values.flat_map(&:requirements).each do |conflicting|
63
69
  conflicting.each do |source, conflict_requirements|
64
70
  conflict_requirements.each do |c|
65
71
  pairs << [c, source]
@@ -69,7 +75,69 @@ module Gem::Resolver::Molinillo
69
75
 
70
76
  super "Unable to satisfy the following requirements:\n\n" \
71
77
  "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
78
+
72
79
  @conflicts = conflicts
80
+ @specification_provider = specification_provider
81
+ end
82
+
83
+ require_relative 'delegates/specification_provider'
84
+ include Delegates::SpecificationProvider
85
+
86
+ # @return [String] An error message that includes requirement trees,
87
+ # which is much more detailed & customizable than the default message
88
+ # @param [Hash] opts the options to create a message with.
89
+ # @option opts [String] :solver_name The user-facing name of the solver
90
+ # @option opts [String] :possibility_type The generic name of a possibility
91
+ # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
92
+ # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
93
+ # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
94
+ # messages for each conflict
95
+ # @option opts [Proc] :version_for_spec A proc that returns the version number for a
96
+ # possibility
97
+ def message_with_trees(opts = {})
98
+ solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
99
+ possibility_type = opts.delete(:possibility_type) { 'possibility named' }
100
+ reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
101
+ printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
102
+ additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
103
+ version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
104
+ incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
105
+ proc do |name, _conflict|
106
+ %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
107
+ end
108
+ end
109
+
110
+ conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
111
+ o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
112
+ if conflict.locked_requirement
113
+ o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
114
+ o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
115
+ o << %(\n)
116
+ end
117
+ o << %( In #{name_for_explicit_dependency_source}:\n)
118
+ trees = reduce_trees.call(conflict.requirement_trees)
119
+
120
+ o << trees.map do |tree|
121
+ t = ''.dup
122
+ depth = 2
123
+ tree.each do |req|
124
+ t << ' ' * depth << req.to_s
125
+ unless tree.last == req
126
+ if spec = conflict.activated_by_name[name_for(req)]
127
+ t << %( was resolved to #{version_for_spec.call(spec)}, which)
128
+ end
129
+ t << %( depends on)
130
+ end
131
+ t << %(\n)
132
+ depth += 1
133
+ end
134
+ t
135
+ end.join("\n")
136
+
137
+ additional_message_for_conflict.call(o, name, conflict)
138
+
139
+ o
140
+ end.strip
73
141
  end
74
142
  end
75
143
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  # The version of Gem::Resolver::Molinillo.
4
- VERSION = '0.5.7'.freeze
5
+ VERSION = '0.7.0'.freeze
5
6
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  # Provides information about specifcations and dependencies to the resolver,
4
5
  # allowing the {Resolver} class to remain generic while still providing power
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  # Conveys information about the resolution process to a user.
4
5
  module UI
@@ -48,7 +49,8 @@ module Gem::Resolver::Molinillo
48
49
  if debug?
49
50
  debug_info = yield
50
51
  debug_info = debug_info.inspect unless debug_info.is_a?(String)
51
- output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
52
+ debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
53
+ output.puts debug_info
52
54
  end
53
55
  end
54
56
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Gem::Resolver::Molinillo
3
4
  class Resolver
4
5
  # A specific resolution from a given {Resolver}
@@ -8,22 +9,125 @@ module Gem::Resolver::Molinillo
8
9
  # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
9
10
  # @attr [Object, nil] existing the existing spec that was in conflict with
10
11
  # the {#possibility}
11
- # @attr [Object] possibility the spec that was unable to be activated due
12
- # to a conflict
12
+ # @attr [Object] possibility_set the set of specs that was unable to be
13
+ # activated due to a conflict.
13
14
  # @attr [Object] locked_requirement the relevant locking requirement.
14
15
  # @attr [Array<Array<Object>>] requirement_trees the different requirement
15
16
  # trees that led to every requirement for the conflicting name.
16
17
  # @attr [{String=>Object}] activated_by_name the already-activated specs.
18
+ # @attr [Object] underlying_error an error that has occurred during resolution, and
19
+ # will be raised at the end of it if no resolution is found.
17
20
  Conflict = Struct.new(
18
21
  :requirement,
19
22
  :requirements,
20
23
  :existing,
21
- :possibility,
24
+ :possibility_set,
22
25
  :locked_requirement,
23
26
  :requirement_trees,
24
- :activated_by_name
27
+ :activated_by_name,
28
+ :underlying_error
25
29
  )
26
30
 
31
+ class Conflict
32
+ # @return [Object] a spec that was unable to be activated due to a conflict
33
+ def possibility
34
+ possibility_set && possibility_set.latest_version
35
+ end
36
+ end
37
+
38
+ # A collection of possibility states that share the same dependencies
39
+ # @attr [Array] dependencies the dependencies for this set of possibilities
40
+ # @attr [Array] possibilities the possibilities
41
+ PossibilitySet = Struct.new(:dependencies, :possibilities)
42
+
43
+ class PossibilitySet
44
+ # String representation of the possibility set, for debugging
45
+ def to_s
46
+ "[#{possibilities.join(', ')}]"
47
+ end
48
+
49
+ # @return [Object] most up-to-date dependency in the possibility set
50
+ def latest_version
51
+ possibilities.last
52
+ end
53
+ end
54
+
55
+ # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
56
+ # @attr [Integer] state_index the index of the state to unwind to
57
+ # @attr [Object] state_requirement the requirement of the state we're unwinding to
58
+ # @attr [Array] requirement_tree for the requirement we're relaxing
59
+ # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
60
+ # @attr [Array] requirement_trees for the conflict
61
+ # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
62
+ UnwindDetails = Struct.new(
63
+ :state_index,
64
+ :state_requirement,
65
+ :requirement_tree,
66
+ :conflicting_requirements,
67
+ :requirement_trees,
68
+ :requirements_unwound_to_instead
69
+ )
70
+
71
+ class UnwindDetails
72
+ include Comparable
73
+
74
+ # We compare UnwindDetails when choosing which state to unwind to. If
75
+ # two options have the same state_index we prefer the one most
76
+ # removed from a requirement that caused the conflict. Both options
77
+ # would unwind to the same state, but a `grandparent` option will
78
+ # filter out fewer of its possibilities after doing so - where a state
79
+ # is both a `parent` and a `grandparent` to requirements that have
80
+ # caused a conflict this is the correct behaviour.
81
+ # @param [UnwindDetail] other UnwindDetail to be compared
82
+ # @return [Integer] integer specifying ordering
83
+ def <=>(other)
84
+ if state_index > other.state_index
85
+ 1
86
+ elsif state_index == other.state_index
87
+ reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
88
+ else
89
+ -1
90
+ end
91
+ end
92
+
93
+ # @return [Integer] index of state requirement in reversed requirement tree
94
+ # (the conflicting requirement itself will be at position 0)
95
+ def reversed_requirement_tree_index
96
+ @reversed_requirement_tree_index ||=
97
+ if state_requirement
98
+ requirement_tree.reverse.index(state_requirement)
99
+ else
100
+ 999_999
101
+ end
102
+ end
103
+
104
+ # @return [Boolean] where the requirement of the state we're unwinding
105
+ # to directly caused the conflict. Note: in this case, it is
106
+ # impossible for the state we're unwinding to to be a parent of
107
+ # any of the other conflicting requirements (or we would have
108
+ # circularity)
109
+ def unwinding_to_primary_requirement?
110
+ requirement_tree.last == state_requirement
111
+ end
112
+
113
+ # @return [Array] array of sub-dependencies to avoid when choosing a
114
+ # new possibility for the state we've unwound to. Only relevant for
115
+ # non-primary unwinds
116
+ def sub_dependencies_to_avoid
117
+ @requirements_to_avoid ||=
118
+ requirement_trees.map do |tree|
119
+ index = tree.index(state_requirement)
120
+ tree[index + 1] if index
121
+ end.compact
122
+ end
123
+
124
+ # @return [Array] array of all the requirements that led to the need for
125
+ # this unwind
126
+ def all_requirements
127
+ @all_requirements ||= requirement_trees.flatten(1)
128
+ end
129
+ end
130
+
27
131
  # @return [SpecificationProvider] the provider that knows about
28
132
  # dependencies, requirements, specifications, versions, etc.
29
133
  attr_reader :specification_provider
@@ -64,7 +168,7 @@ module Gem::Resolver::Molinillo
64
168
  start_resolution
65
169
 
66
170
  while state
67
- break unless state.requirements.any? || state.requirement
171
+ break if !state.requirement && state.requirements.empty?
68
172
  indicate_progress
69
173
  if state.respond_to?(:pop_possibility_state) # DependencyState
70
174
  debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
@@ -78,7 +182,7 @@ module Gem::Resolver::Molinillo
78
182
  process_topmost_state
79
183
  end
80
184
 
81
- activated.freeze
185
+ resolve_activated_specs
82
186
  ensure
83
187
  end_resolution
84
188
  end
@@ -103,12 +207,25 @@ module Gem::Resolver::Molinillo
103
207
  def start_resolution
104
208
  @started_at = Time.now
105
209
 
106
- handle_missing_or_push_dependency_state(initial_state)
210
+ push_initial_state
107
211
 
108
212
  debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
109
213
  resolver_ui.before_resolution
110
214
  end
111
215
 
216
+ def resolve_activated_specs
217
+ activated.vertices.each do |_, vertex|
218
+ next unless vertex.payload
219
+
220
+ latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
221
+ vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
222
+ end
223
+
224
+ activated.set_payload(vertex.name, latest_version)
225
+ end
226
+ activated.freeze
227
+ end
228
+
112
229
  # Ends the resolution process
113
230
  # @return [void]
114
231
  def end_resolution
@@ -121,11 +238,11 @@ module Gem::Resolver::Molinillo
121
238
  debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
122
239
  end
123
240
 
124
- require 'rubygems/resolver/molinillo/lib/molinillo/state'
125
- require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
241
+ require_relative 'state'
242
+ require_relative 'modules/specification_provider'
126
243
 
127
- require 'rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state'
128
- require 'rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider'
244
+ require_relative 'delegates/resolution_state'
245
+ require_relative 'delegates/specification_provider'
129
246
 
130
247
  include Gem::Resolver::Molinillo::Delegates::ResolutionState
131
248
  include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
@@ -136,9 +253,12 @@ module Gem::Resolver::Molinillo
136
253
  if possibility
137
254
  attempt_to_activate
138
255
  else
139
- create_conflict if state.is_a? PossibilityState
140
- unwind_for_conflict until possibility && state.is_a?(DependencyState)
256
+ create_conflict
257
+ unwind_for_conflict
141
258
  end
259
+ rescue CircularDependencyError => underlying_error
260
+ create_conflict(underlying_error)
261
+ unwind_for_conflict
142
262
  end
143
263
 
144
264
  # @return [Object] the current possibility that the resolution is trying
@@ -153,63 +273,292 @@ module Gem::Resolver::Molinillo
153
273
  states.last
154
274
  end
155
275
 
156
- # Creates the initial state for the resolution, based upon the
276
+ # Creates and pushes the initial state for the resolution, based upon the
157
277
  # {#requested} dependencies
158
- # @return [DependencyState] the initial state for the resolution
159
- def initial_state
278
+ # @return [void]
279
+ def push_initial_state
160
280
  graph = DependencyGraph.new.tap do |dg|
161
- original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
281
+ original_requested.each do |requested|
282
+ vertex = dg.add_vertex(name_for(requested), nil, true)
283
+ vertex.explicit_requirements << requested
284
+ end
162
285
  dg.tag(:initial_state)
163
286
  end
164
287
 
165
- requirements = sort_dependencies(original_requested, graph, {})
166
- initial_requirement = requirements.shift
167
- DependencyState.new(
168
- initial_requirement && name_for(initial_requirement),
169
- requirements,
170
- graph,
171
- initial_requirement,
172
- initial_requirement && search_for(initial_requirement),
173
- 0,
174
- {}
175
- )
288
+ push_state_for_requirements(original_requested, true, graph)
176
289
  end
177
290
 
178
291
  # Unwinds the states stack because a conflict has been encountered
179
292
  # @return [void]
180
293
  def unwind_for_conflict
181
- debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" }
294
+ details_for_unwind = build_details_for_unwind
295
+ unwind_options = unused_unwind_options
296
+ debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
182
297
  conflicts.tap do |c|
183
- sliced_states = states.slice!((state_index_for_unwind + 1)..-1)
184
- raise VersionConflict.new(c) unless state
298
+ sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
299
+ raise_error_unless_state(c)
185
300
  activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
186
301
  state.conflicts = c
302
+ state.unused_unwind_options = unwind_options
303
+ filter_possibilities_after_unwind(details_for_unwind)
187
304
  index = states.size - 1
188
305
  @parents_of.each { |_, a| a.reject! { |i| i >= index } }
306
+ state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
307
+ end
308
+ end
309
+
310
+ # Raises a VersionConflict error, or any underlying error, if there is no
311
+ # current state
312
+ # @return [void]
313
+ def raise_error_unless_state(conflicts)
314
+ return if state
315
+
316
+ error = conflicts.values.map(&:underlying_error).compact.first
317
+ raise error || VersionConflict.new(conflicts, specification_provider)
318
+ end
319
+
320
+ # @return [UnwindDetails] Details of the nearest index to which we could unwind
321
+ def build_details_for_unwind
322
+ # Get the possible unwinds for the current conflict
323
+ current_conflict = conflicts[name]
324
+ binding_requirements = binding_requirements_for_conflict(current_conflict)
325
+ unwind_details = unwind_options_for_requirements(binding_requirements)
326
+
327
+ last_detail_for_current_unwind = unwind_details.sort.last
328
+ current_detail = last_detail_for_current_unwind
329
+
330
+ # Look for past conflicts that could be unwound to affect the
331
+ # requirement tree for the current conflict
332
+ relevant_unused_unwinds = unused_unwind_options.select do |alternative|
333
+ intersecting_requirements =
334
+ last_detail_for_current_unwind.all_requirements &
335
+ alternative.requirements_unwound_to_instead
336
+ next if intersecting_requirements.empty?
337
+ # Find the highest index unwind whilst looping through
338
+ current_detail = alternative if alternative > current_detail
339
+ alternative
340
+ end
341
+
342
+ # Add the current unwind options to the `unused_unwind_options` array.
343
+ # The "used" option will be filtered out during `unwind_for_conflict`.
344
+ state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
345
+
346
+ # Update the requirements_unwound_to_instead on any relevant unused unwinds
347
+ relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
348
+ unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
349
+
350
+ current_detail
351
+ end
352
+
353
+ # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
354
+ # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
355
+ # of resolving the passed requirements
356
+ def unwind_options_for_requirements(binding_requirements)
357
+ unwind_details = []
358
+
359
+ trees = []
360
+ binding_requirements.reverse_each do |r|
361
+ partial_tree = [r]
362
+ trees << partial_tree
363
+ unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
364
+
365
+ # If this requirement has alternative possibilities, check if any would
366
+ # satisfy the other requirements that created this conflict
367
+ requirement_state = find_state_for(r)
368
+ if conflict_fixing_possibilities?(requirement_state, binding_requirements)
369
+ unwind_details << UnwindDetails.new(
370
+ states.index(requirement_state),
371
+ r,
372
+ partial_tree,
373
+ binding_requirements,
374
+ trees,
375
+ []
376
+ )
377
+ end
378
+
379
+ # Next, look at the parent of this requirement, and check if the requirement
380
+ # could have been avoided if an alternative PossibilitySet had been chosen
381
+ parent_r = parent_of(r)
382
+ next if parent_r.nil?
383
+ partial_tree.unshift(parent_r)
384
+ requirement_state = find_state_for(parent_r)
385
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
386
+ unwind_details << UnwindDetails.new(
387
+ states.index(requirement_state),
388
+ parent_r,
389
+ partial_tree,
390
+ binding_requirements,
391
+ trees,
392
+ []
393
+ )
394
+ end
395
+
396
+ # Finally, look at the grandparent and up of this requirement, looking
397
+ # for any possibilities that wouldn't create their parent requirement
398
+ grandparent_r = parent_of(parent_r)
399
+ until grandparent_r.nil?
400
+ partial_tree.unshift(grandparent_r)
401
+ requirement_state = find_state_for(grandparent_r)
402
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
403
+ unwind_details << UnwindDetails.new(
404
+ states.index(requirement_state),
405
+ grandparent_r,
406
+ partial_tree,
407
+ binding_requirements,
408
+ trees,
409
+ []
410
+ )
411
+ end
412
+ parent_r = grandparent_r
413
+ grandparent_r = parent_of(parent_r)
414
+ end
415
+ end
416
+
417
+ unwind_details
418
+ end
419
+
420
+ # @param [DependencyState] state
421
+ # @param [Array] binding_requirements array of requirements
422
+ # @return [Boolean] whether or not the given state has any possibilities
423
+ # that could satisfy the given requirements
424
+ def conflict_fixing_possibilities?(state, binding_requirements)
425
+ return false unless state
426
+
427
+ state.possibilities.any? do |possibility_set|
428
+ possibility_set.possibilities.any? do |poss|
429
+ possibility_satisfies_requirements?(poss, binding_requirements)
430
+ end
431
+ end
432
+ end
433
+
434
+ # Filter's a state's possibilities to remove any that would not fix the
435
+ # conflict we've just rewound from
436
+ # @param [UnwindDetails] unwind_details details of the conflict just
437
+ # unwound from
438
+ # @return [void]
439
+ def filter_possibilities_after_unwind(unwind_details)
440
+ return unless state && !state.possibilities.empty?
441
+
442
+ if unwind_details.unwinding_to_primary_requirement?
443
+ filter_possibilities_for_primary_unwind(unwind_details)
444
+ else
445
+ filter_possibilities_for_parent_unwind(unwind_details)
446
+ end
447
+ end
448
+
449
+ # Filter's a state's possibilities to remove any that would not satisfy
450
+ # the requirements in the conflict we've just rewound from
451
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
452
+ # @return [void]
453
+ def filter_possibilities_for_primary_unwind(unwind_details)
454
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
455
+ unwinds_to_state << unwind_details
456
+ unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
457
+
458
+ state.possibilities.reject! do |possibility_set|
459
+ possibility_set.possibilities.none? do |poss|
460
+ unwind_requirement_sets.any? do |requirements|
461
+ possibility_satisfies_requirements?(poss, requirements)
462
+ end
463
+ end
189
464
  end
190
465
  end
191
466
 
192
- # @return [Integer] The index to which the resolution should unwind in the
193
- # case of conflict.
194
- def state_index_for_unwind
195
- current_requirement = requirement
196
- existing_requirement = requirement_for_existing_name(name)
197
- index = -1
198
- [current_requirement, existing_requirement].each do |r|
199
- until r.nil?
200
- current_state = find_state_for(r)
201
- if state_any?(current_state)
202
- current_index = states.index(current_state)
203
- index = current_index if current_index > index
204
- break
467
+ # @param [Object] possibility a single possibility
468
+ # @param [Array] requirements an array of requirements
469
+ # @return [Boolean] whether the possibility satisfies all of the
470
+ # given requirements
471
+ def possibility_satisfies_requirements?(possibility, requirements)
472
+ name = name_for(possibility)
473
+
474
+ activated.tag(:swap)
475
+ activated.set_payload(name, possibility) if activated.vertex_named(name)
476
+ satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
477
+ activated.rewind_to(:swap)
478
+
479
+ satisfied
480
+ end
481
+
482
+ # Filter's a state's possibilities to remove any that would (eventually)
483
+ # create a requirement in the conflict we've just rewound from
484
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
485
+ # @return [void]
486
+ def filter_possibilities_for_parent_unwind(unwind_details)
487
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
488
+ unwinds_to_state << unwind_details
489
+
490
+ primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
491
+ parent_unwinds = unwinds_to_state.uniq - primary_unwinds
492
+
493
+ allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
494
+ states[unwind.state_index].possibilities.select do |possibility_set|
495
+ possibility_set.possibilities.any? do |poss|
496
+ possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
205
497
  end
206
- r = parent_of(r)
207
498
  end
208
499
  end
209
500
 
210
- index
501
+ requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
502
+
503
+ state.possibilities.reject! do |possibility_set|
504
+ !allowed_possibility_sets.include?(possibility_set) &&
505
+ (requirements_to_avoid - possibility_set.dependencies).empty?
506
+ end
211
507
  end
212
508
 
509
+ # @param [Conflict] conflict
510
+ # @return [Array] minimal array of requirements that would cause the passed
511
+ # conflict to occur.
512
+ def binding_requirements_for_conflict(conflict)
513
+ return [conflict.requirement] if conflict.possibility.nil?
514
+
515
+ possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
516
+
517
+ # When there's a `CircularDependency` error the conflicting requirement
518
+ # (the one causing the circular) won't be `conflict.requirement`
519
+ # (which won't be for the right state, because we won't have created it,
520
+ # because it's circular).
521
+ # We need to make sure we have that requirement in the conflict's list,
522
+ # otherwise we won't be able to unwind properly, so we just return all
523
+ # the requirements for the conflict.
524
+ return possible_binding_requirements if conflict.underlying_error
525
+
526
+ possibilities = search_for(conflict.requirement)
527
+
528
+ # If all the requirements together don't filter out all possibilities,
529
+ # then the only two requirements we need to consider are the initial one
530
+ # (where the dependency's version was first chosen) and the last
531
+ if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
532
+ return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
533
+ end
534
+
535
+ # Loop through the possible binding requirements, removing each one
536
+ # that doesn't bind. Use a `reverse_each` as we want the earliest set of
537
+ # binding requirements, and don't use `reject!` as we wish to refine the
538
+ # array *on each iteration*.
539
+ binding_requirements = possible_binding_requirements.dup
540
+ possible_binding_requirements.reverse_each do |req|
541
+ next if req == conflict.requirement
542
+ unless binding_requirement_in_set?(req, binding_requirements, possibilities)
543
+ binding_requirements -= [req]
544
+ end
545
+ end
546
+
547
+ binding_requirements
548
+ end
549
+
550
+ # @param [Object] requirement we wish to check
551
+ # @param [Array] possible_binding_requirements array of requirements
552
+ # @param [Array] possibilities array of possibilities the requirements will be used to filter
553
+ # @return [Boolean] whether or not the given requirement is required to filter
554
+ # out all elements of the array of possibilities.
555
+ def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
556
+ possibilities.any? do |poss|
557
+ possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
558
+ end
559
+ end
560
+
561
+ # @param [Object] requirement
213
562
  # @return [Object] the requirement that led to `requirement` being added
214
563
  # to the list of requirements.
215
564
  def parent_of(requirement)
@@ -219,29 +568,27 @@ module Gem::Resolver::Molinillo
219
568
  parent_state.requirement
220
569
  end
221
570
 
571
+ # @param [String] name
222
572
  # @return [Object] the requirement that led to a version of a possibility
223
573
  # with the given name being activated.
224
574
  def requirement_for_existing_name(name)
225
- return nil unless activated.vertex_named(name).payload
575
+ return nil unless vertex = activated.vertex_named(name)
576
+ return nil unless vertex.payload
226
577
  states.find { |s| s.name == name }.requirement
227
578
  end
228
579
 
580
+ # @param [Object] requirement
229
581
  # @return [ResolutionState] the state whose `requirement` is the given
230
582
  # `requirement`.
231
583
  def find_state_for(requirement)
232
584
  return nil unless requirement
233
- states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
234
- end
235
-
236
- # @return [Boolean] whether or not the given state has any possibilities
237
- # left.
238
- def state_any?(state)
239
- state && state.possibilities.any?
585
+ states.find { |i| requirement == i.requirement }
240
586
  end
241
587
 
588
+ # @param [Object] underlying_error
242
589
  # @return [Conflict] a {Conflict} that reflects the failure to activate
243
590
  # the {#possibility} in conjunction with the current {#state}
244
- def create_conflict
591
+ def create_conflict(underlying_error = nil)
245
592
  vertex = activated.vertex_named(name)
246
593
  locked_requirement = locked_requirement_named(name)
247
594
 
@@ -250,18 +597,21 @@ module Gem::Resolver::Molinillo
250
597
  requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
251
598
  end
252
599
  requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
253
- vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) }
600
+ vertex.incoming_edges.each do |edge|
601
+ (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
602
+ end
254
603
 
255
604
  activated_by_name = {}
256
- activated.each { |v| activated_by_name[v.name] = v.payload if v.payload }
605
+ activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
257
606
  conflicts[name] = Conflict.new(
258
607
  requirement,
259
608
  requirements,
260
- vertex.payload,
609
+ vertex.payload && vertex.payload.latest_version,
261
610
  possibility,
262
611
  locked_requirement,
263
612
  requirement_trees,
264
- activated_by_name
613
+ activated_by_name,
614
+ underlying_error
265
615
  )
266
616
  end
267
617
 
@@ -272,6 +622,7 @@ module Gem::Resolver::Molinillo
272
622
  vertex.requirements.map { |r| requirement_tree_for(r) }
273
623
  end
274
624
 
625
+ # @param [Object] requirement
275
626
  # @return [Array<Object>] the list of requirements that led to
276
627
  # `requirement` being required.
277
628
  def requirement_tree_for(requirement)
@@ -311,116 +662,47 @@ module Gem::Resolver::Molinillo
311
662
  # @return [void]
312
663
  def attempt_to_activate
313
664
  debug(depth) { 'Attempting to activate ' + possibility.to_s }
314
- existing_node = activated.vertex_named(name)
315
- if existing_node.payload
316
- debug(depth) { "Found existing spec (#{existing_node.payload})" }
317
- attempt_to_activate_existing_spec(existing_node)
665
+ existing_vertex = activated.vertex_named(name)
666
+ if existing_vertex.payload
667
+ debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
668
+ attempt_to_filter_existing_spec(existing_vertex)
318
669
  else
319
- attempt_to_activate_new_spec
320
- end
321
- end
322
-
323
- # Attempts to activate the current {#possibility} (given that it has
324
- # already been activated)
325
- # @return [void]
326
- def attempt_to_activate_existing_spec(existing_node)
327
- existing_spec = existing_node.payload
328
- if requirement_satisfied_by?(requirement, activated, existing_spec)
329
- new_requirements = requirements.dup
330
- push_state_for_requirements(new_requirements, false)
331
- else
332
- return if attempt_to_swap_possibility
333
- create_conflict
334
- debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
335
- unwind_for_conflict
336
- end
337
- end
338
-
339
- # Attempts to swp the current {#possibility} with the already-activated
340
- # spec with the given name
341
- # @return [Boolean] Whether the possibility was swapped into {#activated}
342
- def attempt_to_swap_possibility
343
- activated.tag(:swap)
344
- vertex = activated.vertex_named(name)
345
- activated.set_payload(name, possibility)
346
- if !vertex.requirements.
347
- all? { |r| requirement_satisfied_by?(r, activated, possibility) } ||
348
- !new_spec_satisfied?
349
- activated.rewind_to(:swap)
350
- return
351
- end
352
- fixup_swapped_children(vertex)
353
- activate_spec
354
- end
355
-
356
- # Ensures there are no orphaned successors to the given {vertex}.
357
- # @param [DependencyGraph::Vertex] vertex the vertex to fix up.
358
- # @return [void]
359
- def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity
360
- payload = vertex.payload
361
- deps = dependencies_for(payload).group_by(&method(:name_for))
362
- vertex.outgoing_edges.each do |outgoing_edge|
363
- requirement = outgoing_edge.requirement
364
- parent_index = @parents_of[requirement].last
365
- succ = outgoing_edge.destination
366
- matching_deps = Array(deps[succ.name])
367
- dep_matched = matching_deps.include?(requirement)
368
-
369
- # only push the current index when it was originally required by the
370
- # same named spec
371
- if parent_index && states[parent_index].name == name
372
- @parents_of[requirement].push(states.size - 1)
670
+ latest = possibility.latest_version
671
+ possibility.possibilities.select! do |possibility|
672
+ requirement_satisfied_by?(requirement, activated, possibility)
373
673
  end
374
-
375
- if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex]
376
- debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
377
- succ.requirements.each { |r| @parents_of.delete(r) }
378
-
379
- removed_names = activated.detach_vertex_named(succ.name).map(&:name)
380
- requirements.delete_if do |r|
381
- # the only removed vertices are those with no other requirements,
382
- # so it's safe to delete only based upon name here
383
- removed_names.include?(name_for(r))
384
- end
385
- elsif !dep_matched
386
- debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" }
387
- # also reset if we're removing the edge, but only if its parent has
388
- # already been fixed up
389
- @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty?
390
-
391
- activated.delete_edge(outgoing_edge)
392
- requirements.delete(requirement)
674
+ if possibility.latest_version.nil?
675
+ # ensure there's a possibility for better error messages
676
+ possibility.possibilities << latest if latest
677
+ create_conflict
678
+ unwind_for_conflict
679
+ else
680
+ activate_new_spec
393
681
  end
394
682
  end
395
683
  end
396
684
 
397
- # Attempts to activate the current {#possibility} (given that it hasn't
398
- # already been activated)
685
+ # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
399
686
  # @return [void]
400
- def attempt_to_activate_new_spec
401
- if new_spec_satisfied?
402
- activate_spec
687
+ def attempt_to_filter_existing_spec(vertex)
688
+ filtered_set = filtered_possibility_set(vertex)
689
+ if !filtered_set.possibilities.empty?
690
+ activated.set_payload(name, filtered_set)
691
+ new_requirements = requirements.dup
692
+ push_state_for_requirements(new_requirements, false)
403
693
  else
404
694
  create_conflict
695
+ debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
405
696
  unwind_for_conflict
406
697
  end
407
698
  end
408
699
 
409
- # @return [Boolean] whether the current spec is satisfied as a new
410
- # possibility.
411
- def new_spec_satisfied?
412
- unless requirement_satisfied_by?(requirement, activated, possibility)
413
- debug(depth) { 'Unsatisfied by requested spec' }
414
- return false
415
- end
416
-
417
- locked_requirement = locked_requirement_named(name)
418
-
419
- locked_spec_satisfied = !locked_requirement ||
420
- requirement_satisfied_by?(locked_requirement, activated, possibility)
421
- debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
422
-
423
- locked_spec_satisfied
700
+ # Generates a filtered version of the existing vertex's `PossibilitySet` using the
701
+ # current state's `requirement`
702
+ # @param [Object] vertex existing vertex
703
+ # @return [PossibilitySet] filtered possibility set
704
+ def filtered_possibility_set(vertex)
705
+ PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
424
706
  end
425
707
 
426
708
  # @param [String] requirement_name the spec name to search for
@@ -434,7 +716,7 @@ module Gem::Resolver::Molinillo
434
716
  # Add the current {#possibility} to the dependency graph of the current
435
717
  # {#state}
436
718
  # @return [void]
437
- def activate_spec
719
+ def activate_new_spec
438
720
  conflicts.delete(name)
439
721
  debug(depth) { "Activated #{name} at #{possibility}" }
440
722
  activated.set_payload(name, possibility)
@@ -442,14 +724,14 @@ module Gem::Resolver::Molinillo
442
724
  end
443
725
 
444
726
  # Requires the dependencies that the recently activated spec has
445
- # @param [Object] activated_spec the specification that has just been
727
+ # @param [Object] possibility_set the PossibilitySet that has just been
446
728
  # activated
447
729
  # @return [void]
448
- def require_nested_dependencies_for(activated_spec)
449
- nested_dependencies = dependencies_for(activated_spec)
730
+ def require_nested_dependencies_for(possibility_set)
731
+ nested_dependencies = dependencies_for(possibility_set.latest_version)
450
732
  debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
451
733
  nested_dependencies.each do |d|
452
- activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d)
734
+ activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
453
735
  parent_index = states.size - 1
454
736
  parents = @parents_of[d]
455
737
  parents << parent_index if parents.empty?
@@ -461,23 +743,82 @@ module Gem::Resolver::Molinillo
461
743
  # Pushes a new {DependencyState} that encapsulates both existing and new
462
744
  # requirements
463
745
  # @param [Array] new_requirements
746
+ # @param [Boolean] requires_sort
747
+ # @param [Object] new_activated
464
748
  # @return [void]
465
749
  def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
466
750
  new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
467
- new_requirement = new_requirements.shift
751
+ new_requirement = nil
752
+ loop do
753
+ new_requirement = new_requirements.shift
754
+ break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
755
+ end
468
756
  new_name = new_requirement ? name_for(new_requirement) : ''.freeze
469
- possibilities = new_requirement ? search_for(new_requirement) : []
757
+ possibilities = possibilities_for_requirement(new_requirement)
470
758
  handle_missing_or_push_dependency_state DependencyState.new(
471
759
  new_name, new_requirements, new_activated,
472
- new_requirement, possibilities, depth, conflicts.dup
760
+ new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
473
761
  )
474
762
  end
475
763
 
764
+ # Checks a proposed requirement with any existing locked requirement
765
+ # before generating an array of possibilities for it.
766
+ # @param [Object] requirement the proposed requirement
767
+ # @param [Object] activated
768
+ # @return [Array] possibilities
769
+ def possibilities_for_requirement(requirement, activated = self.activated)
770
+ return [] unless requirement
771
+ if locked_requirement_named(name_for(requirement))
772
+ return locked_requirement_possibility_set(requirement, activated)
773
+ end
774
+
775
+ group_possibilities(search_for(requirement))
776
+ end
777
+
778
+ # @param [Object] requirement the proposed requirement
779
+ # @param [Object] activated
780
+ # @return [Array] possibility set containing only the locked requirement, if any
781
+ def locked_requirement_possibility_set(requirement, activated = self.activated)
782
+ all_possibilities = search_for(requirement)
783
+ locked_requirement = locked_requirement_named(name_for(requirement))
784
+
785
+ # Longwinded way to build a possibilities array with either the locked
786
+ # requirement or nothing in it. Required, since the API for
787
+ # locked_requirement isn't guaranteed.
788
+ locked_possibilities = all_possibilities.select do |possibility|
789
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
790
+ end
791
+
792
+ group_possibilities(locked_possibilities)
793
+ end
794
+
795
+ # Build an array of PossibilitySets, with each element representing a group of
796
+ # dependency versions that all have the same sub-dependency version constraints
797
+ # and are contiguous.
798
+ # @param [Array] possibilities an array of possibilities
799
+ # @return [Array<PossibilitySet>] an array of possibility sets
800
+ def group_possibilities(possibilities)
801
+ possibility_sets = []
802
+ current_possibility_set = nil
803
+
804
+ possibilities.reverse_each do |possibility|
805
+ dependencies = dependencies_for(possibility)
806
+ if current_possibility_set && current_possibility_set.dependencies == dependencies
807
+ current_possibility_set.possibilities.unshift(possibility)
808
+ else
809
+ possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
810
+ current_possibility_set = possibility_sets.first
811
+ end
812
+ end
813
+
814
+ possibility_sets
815
+ end
816
+
476
817
  # Pushes a new {DependencyState}.
477
818
  # If the {#specification_provider} says to
478
819
  # {SpecificationProvider#allow_missing?} that particular requirement, and
479
820
  # there are no possibilities for that requirement, then `state` is not
480
- # pushed, and the node in {#activated} is removed, and we continue
821
+ # pushed, and the vertex in {#activated} is removed, and we continue
481
822
  # resolving the remaining requirements.
482
823
  # @param [DependencyState] state
483
824
  # @return [void]