dependabot-core 0.76.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6408 -0
  3. data/LICENSE +37 -0
  4. data/README.md +115 -0
  5. data/helpers/elixir/bin/check_update.exs +92 -0
  6. data/helpers/elixir/bin/do_update.exs +39 -0
  7. data/helpers/elixir/bin/parse_deps.exs +103 -0
  8. data/helpers/elixir/bin/run.exs +76 -0
  9. data/helpers/elixir/mix.exs +21 -0
  10. data/helpers/elixir/mix.lock +3 -0
  11. data/helpers/go/Makefile +9 -0
  12. data/helpers/go/go.mod +9 -0
  13. data/helpers/go/go.sum +5 -0
  14. data/helpers/go/importresolver/main.go +34 -0
  15. data/helpers/go/main.go +77 -0
  16. data/helpers/go/updatechecker/main.go +107 -0
  17. data/helpers/go/updater/go.mod +3 -0
  18. data/helpers/go/updater/go.sum +2 -0
  19. data/helpers/go/updater/helpers.go +57 -0
  20. data/helpers/go/updater/main.go +48 -0
  21. data/helpers/npm/.agignore +1 -0
  22. data/helpers/npm/.envrc +2 -0
  23. data/helpers/npm/.eslintrc +14 -0
  24. data/helpers/npm/.nvimrc +7 -0
  25. data/helpers/npm/bin/run.js +34 -0
  26. data/helpers/npm/lib/helpers.js +25 -0
  27. data/helpers/npm/lib/peer-dependency-checker.js +102 -0
  28. data/helpers/npm/lib/subdependency-updater.js +48 -0
  29. data/helpers/npm/lib/updater.js +95 -0
  30. data/helpers/npm/package.json +17 -0
  31. data/helpers/npm/test/fixtures/npm-left-pad.json +1 -0
  32. data/helpers/npm/test/fixtures/updater/original/package-lock.json +16 -0
  33. data/helpers/npm/test/fixtures/updater/original/package.json +9 -0
  34. data/helpers/npm/test/fixtures/updater/updated/package-lock.json +16 -0
  35. data/helpers/npm/test/helpers.js +7 -0
  36. data/helpers/npm/test/updater.test.js +50 -0
  37. data/helpers/npm/yarn.lock +6120 -0
  38. data/helpers/php/.php_cs +34 -0
  39. data/helpers/php/bin/run.php +57 -0
  40. data/helpers/php/composer.json +14 -0
  41. data/helpers/php/composer.lock +1521 -0
  42. data/helpers/php/composer.phar +0 -0
  43. data/helpers/php/setup.sh +4 -0
  44. data/helpers/php/src/DependabotInstallationManager.php +61 -0
  45. data/helpers/php/src/DependabotPluginManager.php +23 -0
  46. data/helpers/php/src/ExceptionIO.php +25 -0
  47. data/helpers/php/src/Hasher.php +21 -0
  48. data/helpers/php/src/UpdateChecker.php +123 -0
  49. data/helpers/php/src/Updater.php +97 -0
  50. data/helpers/python/lib/__init__.py +0 -0
  51. data/helpers/python/lib/hasher.py +23 -0
  52. data/helpers/python/lib/parser.py +130 -0
  53. data/helpers/python/requirements.txt +9 -0
  54. data/helpers/python/run.py +18 -0
  55. data/helpers/test/run.rb +15 -0
  56. data/helpers/utils/git-credential-store-immutable +10 -0
  57. data/helpers/yarn/.agignore +1 -0
  58. data/helpers/yarn/.envrc +2 -0
  59. data/helpers/yarn/.eslintrc +14 -0
  60. data/helpers/yarn/.nvimrc +7 -0
  61. data/helpers/yarn/bin/run.js +36 -0
  62. data/helpers/yarn/lib/fix-duplicates.js +53 -0
  63. data/helpers/yarn/lib/helpers.js +5 -0
  64. data/helpers/yarn/lib/lockfile-parser.js +21 -0
  65. data/helpers/yarn/lib/peer-dependency-checker.js +130 -0
  66. data/helpers/yarn/lib/replace-lockfile-declaration.js +45 -0
  67. data/helpers/yarn/lib/subdependency-updater.js +69 -0
  68. data/helpers/yarn/lib/updater.js +254 -0
  69. data/helpers/yarn/package.json +17 -0
  70. data/helpers/yarn/test/fixtures/updater/original/package.json +6 -0
  71. data/helpers/yarn/test/fixtures/updater/original/yarn.lock +11 -0
  72. data/helpers/yarn/test/fixtures/updater/updated/yarn.lock +12 -0
  73. data/helpers/yarn/test/fixtures/updater/with-version-comments/package.json +5 -0
  74. data/helpers/yarn/test/fixtures/updater/with-version-comments/yarn.lock +13 -0
  75. data/helpers/yarn/test/fixtures/yarnpkg-is-positive.json +1 -0
  76. data/helpers/yarn/test/fixtures/yarnpkg-left-pad.json +1 -0
  77. data/helpers/yarn/test/helpers.js +7 -0
  78. data/helpers/yarn/test/updater.test.js +93 -0
  79. data/helpers/yarn/yarn.lock +4912 -0
  80. data/lib/bundler_definition_bundler_version_patch.rb +15 -0
  81. data/lib/bundler_definition_ruby_version_patch.rb +14 -0
  82. data/lib/bundler_git_source_patch.rb +27 -0
  83. data/lib/dependabot.rb +4 -0
  84. data/lib/dependabot/clients/bitbucket.rb +101 -0
  85. data/lib/dependabot/clients/github_with_retries.rb +117 -0
  86. data/lib/dependabot/clients/gitlab.rb +72 -0
  87. data/lib/dependabot/dependency.rb +118 -0
  88. data/lib/dependabot/dependency_file.rb +54 -0
  89. data/lib/dependabot/errors.rb +179 -0
  90. data/lib/dependabot/file_fetchers.rb +48 -0
  91. data/lib/dependabot/file_fetchers/README.md +65 -0
  92. data/lib/dependabot/file_fetchers/base.rb +302 -0
  93. data/lib/dependabot/file_fetchers/docker/docker.rb +40 -0
  94. data/lib/dependabot/file_fetchers/dotnet/nuget.rb +215 -0
  95. data/lib/dependabot/file_fetchers/dotnet/nuget/import_paths_finder.rb +51 -0
  96. data/lib/dependabot/file_fetchers/dotnet/nuget/sln_project_paths_finder.rb +55 -0
  97. data/lib/dependabot/file_fetchers/elixir/hex.rb +78 -0
  98. data/lib/dependabot/file_fetchers/elm/elm_package.rb +52 -0
  99. data/lib/dependabot/file_fetchers/git/submodules.rb +73 -0
  100. data/lib/dependabot/file_fetchers/go/dep.rb +69 -0
  101. data/lib/dependabot/file_fetchers/go/modules.rb +64 -0
  102. data/lib/dependabot/file_fetchers/java/gradle.rb +56 -0
  103. data/lib/dependabot/file_fetchers/java/gradle/settings_file_parser.rb +66 -0
  104. data/lib/dependabot/file_fetchers/java/maven.rb +127 -0
  105. data/lib/dependabot/file_fetchers/java_script/npm_and_yarn.rb +330 -0
  106. data/lib/dependabot/file_fetchers/java_script/npm_and_yarn/path_dependency_builder.rb +107 -0
  107. data/lib/dependabot/file_fetchers/php/composer.rb +131 -0
  108. data/lib/dependabot/file_fetchers/python/pip.rb +305 -0
  109. data/lib/dependabot/file_fetchers/ruby/bundler.rb +185 -0
  110. data/lib/dependabot/file_fetchers/ruby/bundler/child_gemfile_finder.rb +70 -0
  111. data/lib/dependabot/file_fetchers/ruby/bundler/path_gemspec_finder.rb +114 -0
  112. data/lib/dependabot/file_fetchers/ruby/bundler/require_relative_finder.rb +67 -0
  113. data/lib/dependabot/file_fetchers/rust/cargo.rb +240 -0
  114. data/lib/dependabot/file_parsers.rb +48 -0
  115. data/lib/dependabot/file_parsers/README.md +45 -0
  116. data/lib/dependabot/file_parsers/base.rb +31 -0
  117. data/lib/dependabot/file_parsers/base/dependency_set.rb +77 -0
  118. data/lib/dependabot/file_parsers/docker/docker.rb +164 -0
  119. data/lib/dependabot/file_parsers/dotnet/nuget.rb +85 -0
  120. data/lib/dependabot/file_parsers/dotnet/nuget/packages_config_parser.rb +65 -0
  121. data/lib/dependabot/file_parsers/dotnet/nuget/project_file_parser.rb +156 -0
  122. data/lib/dependabot/file_parsers/dotnet/nuget/property_value_finder.rb +131 -0
  123. data/lib/dependabot/file_parsers/elixir/hex.rb +134 -0
  124. data/lib/dependabot/file_parsers/elm/elm_package.rb +136 -0
  125. data/lib/dependabot/file_parsers/git/submodules.rb +69 -0
  126. data/lib/dependabot/file_parsers/go/dep.rb +163 -0
  127. data/lib/dependabot/file_parsers/go/modules.rb +34 -0
  128. data/lib/dependabot/file_parsers/go/modules/go_mod_parser.rb +134 -0
  129. data/lib/dependabot/file_parsers/java/gradle.rb +236 -0
  130. data/lib/dependabot/file_parsers/java/gradle/property_value_finder.rb +90 -0
  131. data/lib/dependabot/file_parsers/java/gradle/repositories_finder.rb +145 -0
  132. data/lib/dependabot/file_parsers/java/maven.rb +252 -0
  133. data/lib/dependabot/file_parsers/java/maven/property_value_finder.rb +166 -0
  134. data/lib/dependabot/file_parsers/java/maven/repositories_finder.rb +188 -0
  135. data/lib/dependabot/file_parsers/java_script/npm_and_yarn.rb +394 -0
  136. data/lib/dependabot/file_parsers/php/composer.rb +177 -0
  137. data/lib/dependabot/file_parsers/python/pip.rb +223 -0
  138. data/lib/dependabot/file_parsers/python/pip/pipfile_files_parser.rb +154 -0
  139. data/lib/dependabot/file_parsers/python/pip/poetry_files_parser.rb +141 -0
  140. data/lib/dependabot/file_parsers/python/pip/setup_file_parser.rb +160 -0
  141. data/lib/dependabot/file_parsers/ruby/bundler.rb +295 -0
  142. data/lib/dependabot/file_parsers/ruby/bundler/file_preparer.rb +85 -0
  143. data/lib/dependabot/file_parsers/ruby/bundler/gemfile_checker.rb +48 -0
  144. data/lib/dependabot/file_parsers/rust/cargo.rb +213 -0
  145. data/lib/dependabot/file_updaters.rb +48 -0
  146. data/lib/dependabot/file_updaters/README.md +58 -0
  147. data/lib/dependabot/file_updaters/base.rb +52 -0
  148. data/lib/dependabot/file_updaters/docker/docker.rb +133 -0
  149. data/lib/dependabot/file_updaters/dotnet/nuget.rb +151 -0
  150. data/lib/dependabot/file_updaters/dotnet/nuget/packages_config_declaration_finder.rb +69 -0
  151. data/lib/dependabot/file_updaters/dotnet/nuget/project_file_declaration_finder.rb +78 -0
  152. data/lib/dependabot/file_updaters/dotnet/nuget/property_value_updater.rb +64 -0
  153. data/lib/dependabot/file_updaters/elixir/hex.rb +71 -0
  154. data/lib/dependabot/file_updaters/elixir/hex/lockfile_updater.rb +147 -0
  155. data/lib/dependabot/file_updaters/elixir/hex/mixfile_git_pin_updater.rb +53 -0
  156. data/lib/dependabot/file_updaters/elixir/hex/mixfile_requirement_updater.rb +74 -0
  157. data/lib/dependabot/file_updaters/elixir/hex/mixfile_sanitizer.rb +28 -0
  158. data/lib/dependabot/file_updaters/elixir/hex/mixfile_updater.rb +98 -0
  159. data/lib/dependabot/file_updaters/elm/elm_package.rb +79 -0
  160. data/lib/dependabot/file_updaters/elm/elm_package/elm_json_updater.rb +69 -0
  161. data/lib/dependabot/file_updaters/elm/elm_package/elm_package_updater.rb +69 -0
  162. data/lib/dependabot/file_updaters/git/submodules.rb +38 -0
  163. data/lib/dependabot/file_updaters/go/dep.rb +77 -0
  164. data/lib/dependabot/file_updaters/go/dep/lockfile_updater.rb +219 -0
  165. data/lib/dependabot/file_updaters/go/dep/manifest_updater.rb +155 -0
  166. data/lib/dependabot/file_updaters/go/modules.rb +71 -0
  167. data/lib/dependabot/file_updaters/go/modules/go_mod_updater.rb +81 -0
  168. data/lib/dependabot/file_updaters/java/gradle.rb +176 -0
  169. data/lib/dependabot/file_updaters/java/gradle/dependency_set_updater.rb +66 -0
  170. data/lib/dependabot/file_updaters/java/gradle/property_value_updater.rb +58 -0
  171. data/lib/dependabot/file_updaters/java/maven.rb +155 -0
  172. data/lib/dependabot/file_updaters/java/maven/declaration_finder.rb +132 -0
  173. data/lib/dependabot/file_updaters/java/maven/property_value_updater.rb +61 -0
  174. data/lib/dependabot/file_updaters/java_script/npm_and_yarn.rb +159 -0
  175. data/lib/dependabot/file_updaters/java_script/npm_and_yarn/npm_lockfile_updater.rb +532 -0
  176. data/lib/dependabot/file_updaters/java_script/npm_and_yarn/npmrc_builder.rb +191 -0
  177. data/lib/dependabot/file_updaters/java_script/npm_and_yarn/package_json_preparer.rb +91 -0
  178. data/lib/dependabot/file_updaters/java_script/npm_and_yarn/package_json_updater.rb +220 -0
  179. data/lib/dependabot/file_updaters/java_script/npm_and_yarn/yarn_lockfile_updater.rb +475 -0
  180. data/lib/dependabot/file_updaters/php/composer.rb +78 -0
  181. data/lib/dependabot/file_updaters/php/composer/lockfile_updater.rb +264 -0
  182. data/lib/dependabot/file_updaters/php/composer/manifest_updater.rb +70 -0
  183. data/lib/dependabot/file_updaters/python/pip.rb +147 -0
  184. data/lib/dependabot/file_updaters/python/pip/pip_compile_file_updater.rb +363 -0
  185. data/lib/dependabot/file_updaters/python/pip/pipfile_file_updater.rb +397 -0
  186. data/lib/dependabot/file_updaters/python/pip/pipfile_preparer.rb +125 -0
  187. data/lib/dependabot/file_updaters/python/pip/poetry_file_updater.rb +289 -0
  188. data/lib/dependabot/file_updaters/python/pip/pyproject_preparer.rb +105 -0
  189. data/lib/dependabot/file_updaters/python/pip/requirement_file_updater.rb +166 -0
  190. data/lib/dependabot/file_updaters/python/pip/requirement_replacer.rb +95 -0
  191. data/lib/dependabot/file_updaters/python/pip/setup_file_sanitizer.rb +91 -0
  192. data/lib/dependabot/file_updaters/ruby/bundler.rb +121 -0
  193. data/lib/dependabot/file_updaters/ruby/bundler/gemfile_updater.rb +116 -0
  194. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_dependency_name_finder.rb +52 -0
  195. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_sanitizer.rb +298 -0
  196. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_updater.rb +64 -0
  197. data/lib/dependabot/file_updaters/ruby/bundler/git_pin_replacer.rb +80 -0
  198. data/lib/dependabot/file_updaters/ruby/bundler/git_source_remover.rb +102 -0
  199. data/lib/dependabot/file_updaters/ruby/bundler/lockfile_updater.rb +384 -0
  200. data/lib/dependabot/file_updaters/ruby/bundler/requirement_replacer.rb +188 -0
  201. data/lib/dependabot/file_updaters/rust/cargo.rb +83 -0
  202. data/lib/dependabot/file_updaters/rust/cargo/lockfile_updater.rb +251 -0
  203. data/lib/dependabot/file_updaters/rust/cargo/manifest_updater.rb +162 -0
  204. data/lib/dependabot/git_commit_checker.rb +412 -0
  205. data/lib/dependabot/metadata_finders.rb +46 -0
  206. data/lib/dependabot/metadata_finders/README.md +53 -0
  207. data/lib/dependabot/metadata_finders/base.rb +117 -0
  208. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +317 -0
  209. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +177 -0
  210. data/lib/dependabot/metadata_finders/base/commits_finder.rb +217 -0
  211. data/lib/dependabot/metadata_finders/base/release_finder.rb +251 -0
  212. data/lib/dependabot/metadata_finders/docker/docker.rb +18 -0
  213. data/lib/dependabot/metadata_finders/dotnet/nuget.rb +116 -0
  214. data/lib/dependabot/metadata_finders/elixir/hex.rb +69 -0
  215. data/lib/dependabot/metadata_finders/elm/elm_package.rb +22 -0
  216. data/lib/dependabot/metadata_finders/git/submodules.rb +20 -0
  217. data/lib/dependabot/metadata_finders/go/dep.rb +56 -0
  218. data/lib/dependabot/metadata_finders/java/maven.rb +173 -0
  219. data/lib/dependabot/metadata_finders/java_script/npm_and_yarn.rb +215 -0
  220. data/lib/dependabot/metadata_finders/php/composer.rb +66 -0
  221. data/lib/dependabot/metadata_finders/python/pip.rb +120 -0
  222. data/lib/dependabot/metadata_finders/ruby/bundler.rb +150 -0
  223. data/lib/dependabot/metadata_finders/rust/cargo.rb +64 -0
  224. data/lib/dependabot/pull_request_creator.rb +151 -0
  225. data/lib/dependabot/pull_request_creator/branch_namer.rb +170 -0
  226. data/lib/dependabot/pull_request_creator/commit_signer.rb +63 -0
  227. data/lib/dependabot/pull_request_creator/github.rb +233 -0
  228. data/lib/dependabot/pull_request_creator/gitlab.rb +122 -0
  229. data/lib/dependabot/pull_request_creator/labeler.rb +361 -0
  230. data/lib/dependabot/pull_request_creator/message_builder.rb +888 -0
  231. data/lib/dependabot/pull_request_updater.rb +43 -0
  232. data/lib/dependabot/pull_request_updater/github.rb +151 -0
  233. data/lib/dependabot/shared_helpers.rb +201 -0
  234. data/lib/dependabot/source.rb +120 -0
  235. data/lib/dependabot/update_checkers.rb +48 -0
  236. data/lib/dependabot/update_checkers/README.md +67 -0
  237. data/lib/dependabot/update_checkers/base.rb +220 -0
  238. data/lib/dependabot/update_checkers/docker/docker.rb +290 -0
  239. data/lib/dependabot/update_checkers/dotnet/nuget.rb +127 -0
  240. data/lib/dependabot/update_checkers/dotnet/nuget/property_updater.rb +97 -0
  241. data/lib/dependabot/update_checkers/dotnet/nuget/repository_finder.rb +232 -0
  242. data/lib/dependabot/update_checkers/dotnet/nuget/requirements_updater.rb +81 -0
  243. data/lib/dependabot/update_checkers/dotnet/nuget/version_finder.rb +231 -0
  244. data/lib/dependabot/update_checkers/elixir/hex.rb +274 -0
  245. data/lib/dependabot/update_checkers/elixir/hex/file_preparer.rb +193 -0
  246. data/lib/dependabot/update_checkers/elixir/hex/requirements_updater.rb +177 -0
  247. data/lib/dependabot/update_checkers/elixir/hex/version_resolver.rb +175 -0
  248. data/lib/dependabot/update_checkers/elm/elm_package.rb +126 -0
  249. data/lib/dependabot/update_checkers/elm/elm_package/cli_parser.rb +33 -0
  250. data/lib/dependabot/update_checkers/elm/elm_package/elm_18_version_resolver.rb +234 -0
  251. data/lib/dependabot/update_checkers/elm/elm_package/elm_19_version_resolver.rb +198 -0
  252. data/lib/dependabot/update_checkers/elm/elm_package/requirements_updater.rb +75 -0
  253. data/lib/dependabot/update_checkers/git/submodules.rb +52 -0
  254. data/lib/dependabot/update_checkers/go/dep.rb +311 -0
  255. data/lib/dependabot/update_checkers/go/dep/file_preparer.rb +221 -0
  256. data/lib/dependabot/update_checkers/go/dep/latest_version_finder.rb +169 -0
  257. data/lib/dependabot/update_checkers/go/dep/requirements_updater.rb +223 -0
  258. data/lib/dependabot/update_checkers/go/dep/version_resolver.rb +164 -0
  259. data/lib/dependabot/update_checkers/go/modules.rb +112 -0
  260. data/lib/dependabot/update_checkers/java/gradle.rb +148 -0
  261. data/lib/dependabot/update_checkers/java/gradle/multi_dependency_updater.rb +105 -0
  262. data/lib/dependabot/update_checkers/java/gradle/version_finder.rb +183 -0
  263. data/lib/dependabot/update_checkers/java/maven.rb +159 -0
  264. data/lib/dependabot/update_checkers/java/maven/property_updater.rb +127 -0
  265. data/lib/dependabot/update_checkers/java/maven/requirements_updater.rb +92 -0
  266. data/lib/dependabot/update_checkers/java/maven/version_finder.rb +225 -0
  267. data/lib/dependabot/update_checkers/java_script/npm_and_yarn.rb +280 -0
  268. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/latest_version_finder.rb +342 -0
  269. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/library_detector.rb +69 -0
  270. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/registry_finder.rb +226 -0
  271. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/requirements_updater.rb +197 -0
  272. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/subdependency_version_resolver.rb +228 -0
  273. data/lib/dependabot/update_checkers/java_script/npm_and_yarn/version_resolver.rb +452 -0
  274. data/lib/dependabot/update_checkers/php/composer.rb +165 -0
  275. data/lib/dependabot/update_checkers/php/composer/requirements_updater.rb +243 -0
  276. data/lib/dependabot/update_checkers/php/composer/version_resolver.rb +203 -0
  277. data/lib/dependabot/update_checkers/python/pip.rb +227 -0
  278. data/lib/dependabot/update_checkers/python/pip/latest_version_finder.rb +252 -0
  279. data/lib/dependabot/update_checkers/python/pip/pip_compile_version_resolver.rb +380 -0
  280. data/lib/dependabot/update_checkers/python/pip/pipfile_version_resolver.rb +559 -0
  281. data/lib/dependabot/update_checkers/python/pip/poetry_version_resolver.rb +300 -0
  282. data/lib/dependabot/update_checkers/python/pip/requirements_updater.rb +367 -0
  283. data/lib/dependabot/update_checkers/ruby/bundler.rb +324 -0
  284. data/lib/dependabot/update_checkers/ruby/bundler/file_preparer.rb +278 -0
  285. data/lib/dependabot/update_checkers/ruby/bundler/force_updater.rb +261 -0
  286. data/lib/dependabot/update_checkers/ruby/bundler/latest_version_finder.rb +169 -0
  287. data/lib/dependabot/update_checkers/ruby/bundler/requirements_updater.rb +264 -0
  288. data/lib/dependabot/update_checkers/ruby/bundler/ruby_requirement_setter.rb +115 -0
  289. data/lib/dependabot/update_checkers/ruby/bundler/shared_bundler_helpers.rb +243 -0
  290. data/lib/dependabot/update_checkers/ruby/bundler/version_resolver.rb +255 -0
  291. data/lib/dependabot/update_checkers/rust/cargo.rb +282 -0
  292. data/lib/dependabot/update_checkers/rust/cargo/file_preparer.rb +202 -0
  293. data/lib/dependabot/update_checkers/rust/cargo/requirements_updater.rb +175 -0
  294. data/lib/dependabot/update_checkers/rust/cargo/version_resolver.rb +242 -0
  295. data/lib/dependabot/utils.rb +84 -0
  296. data/lib/dependabot/utils/docker/credentials_finder.rb +65 -0
  297. data/lib/dependabot/utils/dotnet/requirement.rb +90 -0
  298. data/lib/dependabot/utils/dotnet/version.rb +22 -0
  299. data/lib/dependabot/utils/elixir/requirement.rb +53 -0
  300. data/lib/dependabot/utils/elixir/version.rb +59 -0
  301. data/lib/dependabot/utils/elm/requirement.rb +92 -0
  302. data/lib/dependabot/utils/elm/version.rb +19 -0
  303. data/lib/dependabot/utils/go/path_converter.rb +74 -0
  304. data/lib/dependabot/utils/go/requirement.rb +152 -0
  305. data/lib/dependabot/utils/go/shared_helper.rb +20 -0
  306. data/lib/dependabot/utils/go/version.rb +40 -0
  307. data/lib/dependabot/utils/java/requirement.rb +110 -0
  308. data/lib/dependabot/utils/java/version.rb +179 -0
  309. data/lib/dependabot/utils/java_script/requirement.rb +117 -0
  310. data/lib/dependabot/utils/java_script/version.rb +30 -0
  311. data/lib/dependabot/utils/php/requirement.rb +97 -0
  312. data/lib/dependabot/utils/php/version.rb +22 -0
  313. data/lib/dependabot/utils/python/requirement.rb +130 -0
  314. data/lib/dependabot/utils/python/version.rb +88 -0
  315. data/lib/dependabot/utils/ruby/requirement.rb +26 -0
  316. data/lib/dependabot/utils/rust/requirement.rb +108 -0
  317. data/lib/dependabot/utils/rust/version.rb +32 -0
  318. data/lib/dependabot/version.rb +5 -0
  319. data/lib/python_requirement_parser.rb +33 -0
  320. data/lib/python_versions.rb +21 -0
  321. metadata +641 -0
@@ -0,0 +1,888 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "dependabot/clients/github_with_retries"
5
+ require "dependabot/clients/gitlab"
6
+ require "dependabot/metadata_finders"
7
+ require "dependabot/pull_request_creator"
8
+
9
+ # rubocop:disable Metrics/ClassLength
10
+ module Dependabot
11
+ class PullRequestCreator
12
+ class MessageBuilder
13
+ ANGULAR_PREFIXES = %w(build chore ci docs feat fix perf refactor style
14
+ test).freeze
15
+ ESLINT_PREFIXES = %w(Breaking Build Chore Docs Fix New Update
16
+ Upgrade).freeze
17
+ GITMOJI_PREFIXES = %w(art zap fire bug ambulance sparkles memo rocket
18
+ lipstick tada white_check_mark lock apple penguin
19
+ checkered_flag robot green_apple bookmark
20
+ rotating_light construction green_heart arrow_down
21
+ arrow_up pushpin construction_worker
22
+ chart_with_upwards_trend recycle heavy_minus_sign
23
+ whale heavy_plus_sign wrench globe_with_meridians
24
+ pencil2 hankey rewind twisted_rightwards_arrows
25
+ package alien truck page_facing_up boom bento
26
+ ok_hand wheelchair bulb beers speech_balloon
27
+ card_file_box loud_sound mute busts_in_silhouette
28
+ children_crossing building_construction iphone
29
+ clown_face egg see_no_evil camera_flash).freeze
30
+ ISSUE_TAG_REGEX =
31
+ /(?<=[\s(\\]|^)(?<tag>(?:\#|GH-)\d+)(?=[^A-z0-9\-]|$)/.freeze
32
+ GITHUB_REF_REGEX = %r{github\.com/[^/\s]+/[^/\s]+/(?:issue|pull)}.freeze
33
+
34
+ attr_reader :source, :dependencies, :files, :credentials,
35
+ :pr_message_footer, :author_details, :vulnerabilities_fixed
36
+
37
+ def initialize(source:, dependencies:, files:, credentials:,
38
+ pr_message_footer: nil, author_details: nil,
39
+ vulnerabilities_fixed: {})
40
+ @dependencies = dependencies
41
+ @files = files
42
+ @source = source
43
+ @credentials = credentials
44
+ @pr_message_footer = pr_message_footer
45
+ @author_details = author_details
46
+ @vulnerabilities_fixed = vulnerabilities_fixed
47
+ end
48
+
49
+ def pr_name
50
+ return library_pr_name if library?
51
+
52
+ application_pr_name
53
+ end
54
+
55
+ def pr_message
56
+ commit_message_intro + metadata_cascades + prefixed_pr_message_footer
57
+ end
58
+
59
+ def commit_message
60
+ message = commit_subject + "\n\n"
61
+ message += commit_message_intro
62
+ message += metadata_links
63
+ message += "\n\n" + signoff_message if signoff_message
64
+ message
65
+ end
66
+
67
+ private
68
+
69
+ def commit_subject
70
+ subject = pr_name.gsub("⬆️", ":arrow_up:").gsub("🔒", ":lock:")
71
+ return subject unless subject.length > 72
72
+
73
+ subject.split(" from ").first
74
+ end
75
+
76
+ def commit_message_intro
77
+ return requirement_commit_message_intro if library?
78
+
79
+ version_commit_message_intro
80
+ end
81
+
82
+ def prefixed_pr_message_footer
83
+ return "" unless pr_message_footer
84
+
85
+ "\n\n#{pr_message_footer}"
86
+ end
87
+
88
+ def signoff_message
89
+ return unless author_details.is_a?(Hash)
90
+ return unless author_details[:name] && author_details[:email]
91
+
92
+ "Signed-off-by: #{author_details[:name]} <#{author_details[:email]}>"
93
+ end
94
+
95
+ def library_pr_name
96
+ pr_name = pr_name_prefix
97
+
98
+ pr_name +=
99
+ if dependencies.count == 1
100
+ "#{dependencies.first.display_name} requirement "\
101
+ "from #{old_library_requirement(dependencies.first)} "\
102
+ "to #{new_library_requirement(dependencies.first)}"
103
+ else
104
+ names = dependencies.map(&:name)
105
+ "requirements for #{names[0..-2].join(', ')} and #{names[-1]}"
106
+ end
107
+
108
+ return pr_name if files.first.directory == "/"
109
+
110
+ pr_name + " in #{files.first.directory}"
111
+ end
112
+
113
+ # rubocop:disable Metrics/AbcSize
114
+ def application_pr_name
115
+ pr_name = pr_name_prefix
116
+
117
+ pr_name +=
118
+ if dependencies.count == 1
119
+ dependency = dependencies.first
120
+ "#{dependency.display_name} from #{previous_version(dependency)} "\
121
+ "to #{new_version(dependency)}"
122
+ elsif updating_a_property?
123
+ dependency = dependencies.first
124
+ "#{property_name} from #{previous_version(dependency)} "\
125
+ "to #{new_version(dependency)}"
126
+ elsif updating_a_dependency_set?
127
+ dependency = dependencies.first
128
+ "#{dependency_set.fetch(:group)} dependency set "\
129
+ "from #{previous_version(dependency)} "\
130
+ "to #{new_version(dependency)}"
131
+ else
132
+ names = dependencies.map(&:name)
133
+ "#{names[0..-2].join(', ')} and #{names[-1]}"
134
+ end
135
+
136
+ return pr_name if files.first.directory == "/"
137
+
138
+ pr_name + " in #{files.first.directory}"
139
+ end
140
+ # rubocop:enable Metrics/AbcSize
141
+
142
+ def pr_name_prefix
143
+ prefix = commit_prefix.to_s
144
+ prefix += security_prefix if includes_security_fixes?
145
+ prefix + pr_name_first_word
146
+ end
147
+
148
+ def commit_prefix
149
+ # If there is a previous Dependabot commit, and it used a known style,
150
+ # use that as our model for subsequent commits
151
+ case last_dependabot_commit_style
152
+ when :gitmoji then "⬆️ "
153
+ when :contentional_prefix then "#{last_dependabot_commit_prefix}: "
154
+ when :contentional_prefix_with_scope
155
+ scope = dependencies.any?(&:production?) ? "deps" : "deps-dev"
156
+ "#{last_dependabot_commit_prefix}(#{scope}): "
157
+ else
158
+ # Otherwise we need to detect the user's preferred style from the
159
+ # existing commits on their repo
160
+ build_commit_prefix_from_previous_commits
161
+ end
162
+ end
163
+
164
+ def security_prefix
165
+ return "🔒 " if commit_prefix == "⬆️ "
166
+
167
+ capitalize_first_word? ? "[Security] " : "[security] "
168
+ end
169
+
170
+ def pr_name_first_word
171
+ first_word = library? ? "update " : "bump "
172
+ capitalize_first_word? ? first_word.capitalize : first_word
173
+ end
174
+
175
+ def capitalize_first_word?
176
+ case last_dependabot_commit_style
177
+ when :gitmoji then true
178
+ when :contentional_prefix, :contentional_prefix_with_scope
179
+ last_dependabot_commit_message.match?(/: (\[Security\] )?(B|U)/)
180
+ else
181
+ if using_angular_commit_messages? || using_eslint_commit_messages?
182
+ prefixes = ANGULAR_PREFIXES + ESLINT_PREFIXES
183
+ semantic_msgs = recent_commit_messages.select do |message|
184
+ prefixes.any? { |pre| message.match?(/#{pre}[:(]/i) }
185
+ end
186
+
187
+ return true if semantic_msgs.all? { |m| m.match?(/:\s+\[?[A-Z]/) }
188
+ return false if semantic_msgs.all? { |m| m.match?(/:\s+\[?[a-z]/) }
189
+ end
190
+
191
+ !commit_prefix&.match(/^[a-z]/)
192
+ end
193
+ end
194
+
195
+ def build_commit_prefix_from_previous_commits
196
+ if using_angular_commit_messages?
197
+ scope = dependencies.any?(&:production?) ? "deps" : "deps-dev"
198
+ "#{angular_commit_prefix}(#{scope}): "
199
+ elsif using_eslint_commit_messages?
200
+ # https://eslint.org/docs/developer-guide/contributing/pull-requests
201
+ "Upgrade: "
202
+ elsif using_gitmoji_commit_messages?
203
+ "⬆️ "
204
+ end
205
+ end
206
+
207
+ def last_dependabot_commit_style
208
+ return unless (msg = last_dependabot_commit_message)
209
+
210
+ return :gitmoji if msg.start_with?("⬆️")
211
+ return :contentional_prefix if msg.match?(/^(chore|build|upgrade):/i)
212
+ return unless msg.match?(/^(chore|build|upgrade)\(/i)
213
+
214
+ :contentional_prefix_with_scope
215
+ end
216
+
217
+ def last_dependabot_commit_prefix
218
+ last_dependabot_commit_message&.split(/[:(]/)&.first
219
+ end
220
+
221
+ def requirement_commit_message_intro
222
+ msg = "Updates the requirements on "
223
+
224
+ msg +=
225
+ if dependencies.count == 1
226
+ "#{dependency_links.first} "
227
+ else
228
+ "#{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]} "
229
+ end
230
+
231
+ msg + "to permit the latest version."
232
+ end
233
+
234
+ # rubocop:disable Metrics/CyclomaticComplexity
235
+ # rubocop:disable Metrics/PerceivedComplexity
236
+ def version_commit_message_intro
237
+ if dependencies.count > 1 && updating_a_property?
238
+ return multidependency_property_intro
239
+ end
240
+
241
+ if dependencies.count > 1 && updating_a_dependency_set?
242
+ return dependency_set_intro
243
+ end
244
+
245
+ return multidependency_intro if dependencies.count > 1
246
+
247
+ dependency = dependencies.first
248
+ msg = "Bumps #{dependency_links.first} "\
249
+ "from #{previous_version(dependency)} "\
250
+ "to #{new_version(dependency)}."
251
+
252
+ if switching_from_ref_to_release?(dependency)
253
+ msg += " This release includes the previously tagged commit."
254
+ end
255
+
256
+ if vulnerabilities_fixed[dependency.name]&.any?
257
+ msg += " **This update includes security fixes.**"
258
+ end
259
+
260
+ msg
261
+ end
262
+ # rubocop:enable Metrics/CyclomaticComplexity
263
+ # rubocop:enable Metrics/PerceivedComplexity
264
+
265
+ def multidependency_property_intro
266
+ dependency = dependencies.first
267
+
268
+ "Bumps `#{property_name}` "\
269
+ "from #{previous_version(dependency)} "\
270
+ "to #{new_version(dependency)}."
271
+ end
272
+
273
+ def dependency_set_intro
274
+ dependency = dependencies.first
275
+
276
+ "Bumps `#{dependency_set.fetch(:group)}` "\
277
+ "dependency set from #{previous_version(dependency)} "\
278
+ "to #{new_version(dependency)}."
279
+ end
280
+
281
+ def multidependency_intro
282
+ "Bumps #{dependency_links[0..-2].join(', ')} "\
283
+ "and #{dependency_links[-1]}. These "\
284
+ "dependencies needed to be updated together."
285
+ end
286
+
287
+ def updating_a_property?
288
+ dependencies.first.
289
+ requirements.
290
+ any? { |r| r.dig(:metadata, :property_name) }
291
+ end
292
+
293
+ def updating_a_dependency_set?
294
+ dependencies.first.
295
+ requirements.
296
+ any? { |r| r.dig(:metadata, :dependency_set) }
297
+ end
298
+
299
+ def property_name
300
+ @property_name ||= dependencies.first.requirements.
301
+ find { |r| r.dig(:metadata, :property_name) }&.
302
+ dig(:metadata, :property_name)
303
+
304
+ raise "No property name!" unless @property_name
305
+
306
+ @property_name
307
+ end
308
+
309
+ def dependency_set
310
+ @dependency_set ||= dependencies.first.requirements.
311
+ find { |r| r.dig(:metadata, :dependency_set) }&.
312
+ dig(:metadata, :dependency_set)
313
+
314
+ raise "No dependency set!" unless @dependency_set
315
+
316
+ @dependency_set
317
+ end
318
+
319
+ def dependency_links
320
+ dependencies.map do |dependency|
321
+ if source_url(dependency)
322
+ "[#{dependency.display_name}](#{source_url(dependency)})"
323
+ elsif homepage_url(dependency)
324
+ "[#{dependency.display_name}](#{homepage_url(dependency)})"
325
+ else
326
+ dependency.display_name
327
+ end
328
+ end
329
+ end
330
+
331
+ def metadata_links
332
+ if dependencies.count == 1
333
+ return metadata_links_for_dep(dependencies.first)
334
+ end
335
+
336
+ dependencies.map do |dep|
337
+ "\n\nUpdates `#{dep.display_name}` from #{previous_version(dep)} to "\
338
+ "#{new_version(dep)}"\
339
+ "#{metadata_links_for_dep(dep)}"
340
+ end.join
341
+ end
342
+
343
+ def metadata_links_for_dep(dep)
344
+ msg = ""
345
+ msg += "\n- [Release notes](#{releases_url(dep)})" if releases_url(dep)
346
+ msg += "\n- [Changelog](#{changelog_url(dep)})" if changelog_url(dep)
347
+ msg += "\n- [Upgrade guide](#{upgrade_url(dep)})" if upgrade_url(dep)
348
+ msg += "\n- [Commits](#{commits_url(dep)})" if commits_url(dep)
349
+ msg
350
+ end
351
+
352
+ def metadata_cascades
353
+ if dependencies.count == 1
354
+ return metadata_cascades_for_dep(dependencies.first)
355
+ end
356
+
357
+ dependencies.map do |dep|
358
+ msg = "\n\nUpdates `#{dep.display_name}` from "\
359
+ "#{previous_version(dep)} to #{new_version(dep)}"
360
+ if vulnerabilities_fixed[dep.name]&.any?
361
+ msg += ". **This update includes security fixes.**"
362
+ end
363
+ msg + metadata_cascades_for_dep(dep)
364
+ end.join
365
+ end
366
+
367
+ def metadata_cascades_for_dep(dep)
368
+ msg = ""
369
+ msg += vulnerabilities_cascade(dep)
370
+ msg += release_cascade(dep)
371
+ msg += changelog_cascade(dep)
372
+ msg += upgrade_guide_cascade(dep)
373
+ msg += commits_cascade(dep)
374
+ msg += maintainer_changes_cascade(dep)
375
+ msg += "\n<br />" unless msg == ""
376
+ sanitize_links_and_mentions(msg)
377
+ end
378
+
379
+ def vulnerabilities_cascade(dep)
380
+ fixed_vulns = vulnerabilities_fixed[dep.name]
381
+ return "" unless fixed_vulns&.any?
382
+
383
+ msg = "\n<details>\n<summary>Vulnerabilities fixed</summary>\n\n"
384
+ fixed_vulns.each { |v| msg += serialized_vulnerability_details(v) }
385
+ msg += "</details>"
386
+ sanitize_template_tags(msg)
387
+ end
388
+
389
+ def release_cascade(dep)
390
+ return "" unless releases_text(dep) && releases_url(dep)
391
+
392
+ msg = "\n<details>\n<summary>Release notes</summary>\n\n"
393
+ msg += "*Sourced from [#{dep.display_name}'s releases]"\
394
+ "(#{releases_url(dep)}).*\n\n"
395
+ msg +=
396
+ begin
397
+ release_note_lines = releases_text(dep).split("\n").first(50)
398
+ release_note_lines = release_note_lines.map { |line| "> #{line}\n" }
399
+ if release_note_lines.count == 50
400
+ release_note_lines << truncated_line
401
+ end
402
+ release_note_lines.join
403
+ end
404
+ msg += "</details>"
405
+ msg = link_issues(text: msg, dependency: dep)
406
+ sanitize_template_tags(msg)
407
+ end
408
+
409
+ def changelog_cascade(dep)
410
+ return "" unless changelog_url(dep) && changelog_text(dep)
411
+
412
+ msg = "\n<details>\n<summary>Changelog</summary>\n\n"
413
+ msg += "*Sourced from "\
414
+ "[#{dep.display_name}'s changelog](#{changelog_url(dep)}).*\n\n"
415
+ msg +=
416
+ begin
417
+ changelog_lines = changelog_text(dep).split("\n").first(50)
418
+ changelog_lines = changelog_lines.map { |line| "> #{line}\n" }
419
+ changelog_lines << truncated_line if changelog_lines.count == 50
420
+ changelog_lines.join
421
+ end
422
+ msg += "</details>"
423
+ msg = link_issues(text: msg, dependency: dep)
424
+ msg = fix_relative_links(text: msg, base_url: changelog_url(dep))
425
+ sanitize_template_tags(msg)
426
+ end
427
+
428
+ def upgrade_guide_cascade(dep)
429
+ return "" unless upgrade_url(dep) && upgrade_text(dep)
430
+
431
+ msg = "\n<details>\n<summary>Upgrade guide</summary>\n\n"
432
+ msg += "*Sourced from "\
433
+ "[#{dep.display_name}'s upgrade guide]"\
434
+ "(#{upgrade_url(dep)}).*\n\n"
435
+ msg +=
436
+ begin
437
+ upgrade_lines = upgrade_text(dep).split("\n").first(50)
438
+ upgrade_lines = upgrade_lines.map { |line| "> #{line}\n" }
439
+ upgrade_lines << truncated_line if upgrade_lines.count == 50
440
+ upgrade_lines.join
441
+ end
442
+ msg += "</details>"
443
+ msg = link_issues(text: msg, dependency: dep)
444
+ msg = fix_relative_links(text: msg, base_url: upgrade_url(dep))
445
+ sanitize_template_tags(msg)
446
+ end
447
+
448
+ def commits_cascade(dep)
449
+ return "" unless commits_url(dep) && commits(dep)
450
+
451
+ msg = "\n<details>\n<summary>Commits</summary>\n\n"
452
+
453
+ commits(dep).reverse.first(10).each do |commit|
454
+ title = commit[:message].strip.split("\n").first
455
+ title = title.slice(0..76) + "..." if title && title.length > 80
456
+ sha = commit[:sha][0, 7]
457
+ msg += "- [`#{sha}`](#{commit[:html_url]}) #{title}\n"
458
+ end
459
+
460
+ msg +=
461
+ if commits(dep).count > 10
462
+ "- Additional commits viewable in "\
463
+ "[compare view](#{commits_url(dep)})\n"
464
+ else
465
+ "- See full diff in [compare view](#{commits_url(dep)})\n"
466
+ end
467
+
468
+ msg += "</details>"
469
+ msg = link_issues(text: msg, dependency: dep)
470
+ sanitize_template_tags(msg)
471
+ end
472
+
473
+ def maintainer_changes_cascade(dep)
474
+ return "" unless maintainer_changes(dep)
475
+
476
+ msg = "\n<details>\n<summary>Maintainer changes</summary>\n\n"
477
+ msg += maintainer_changes(dep)
478
+ msg + "\n</details>"
479
+ end
480
+
481
+ def serialized_vulnerability_details(details)
482
+ msg = vulnerability_source_line(details)
483
+
484
+ if details["title"]
485
+ msg += "> **#{details['title'].lines.map(&:strip).join(' ')}**\n"
486
+ end
487
+
488
+ if (description = details["description"])
489
+ description.strip.lines.first(20).each { |line| msg += "> #{line}" }
490
+ msg += truncated_line if description.strip.lines.count > 20
491
+ end
492
+
493
+ msg += "\n" unless msg.end_with?("\n")
494
+ msg += "> \n"
495
+ msg += vulnerability_version_range_lines(details)
496
+ msg + "\n"
497
+ end
498
+
499
+ def vulnerability_source_line(details)
500
+ if details["source_url"] && details["source_name"]
501
+ "*Sourced from [#{details['source_name']}]"\
502
+ "(#{details['source_url']}).*\n\n"
503
+ elsif details["source_name"]
504
+ "*Sourced from #{details['source_name']}.*\n\n"
505
+ else
506
+ ""
507
+ end
508
+ end
509
+
510
+ def vulnerability_version_range_lines(details)
511
+ msg = ""
512
+ %w(patched_versions unaffected_versions affected_versions).each do |tp|
513
+ type = tp.split("_").first.capitalize
514
+ next unless details[tp]
515
+
516
+ versions_string = details[tp].any? ? details[tp].join("; ") : "none"
517
+ versions_string = versions_string.gsub(/(?<!\\)~/, '\~')
518
+ msg += "> #{type} versions: #{versions_string}\n"
519
+ end
520
+ msg
521
+ end
522
+
523
+ def truncated_line
524
+ # Tables can spill out of truncated details, so we close them
525
+ "></table> ... (truncated)\n"
526
+ end
527
+
528
+ def releases_url(dependency)
529
+ metadata_finder(dependency).releases_url
530
+ end
531
+
532
+ def releases_text(dependency)
533
+ metadata_finder(dependency).releases_text
534
+ end
535
+
536
+ def changelog_url(dependency)
537
+ metadata_finder(dependency).changelog_url
538
+ end
539
+
540
+ def changelog_text(dependency)
541
+ metadata_finder(dependency).changelog_text
542
+ end
543
+
544
+ def upgrade_url(dependency)
545
+ metadata_finder(dependency).upgrade_guide_url
546
+ end
547
+
548
+ def upgrade_text(dependency)
549
+ metadata_finder(dependency).upgrade_guide_text
550
+ end
551
+
552
+ def commits_url(dependency)
553
+ metadata_finder(dependency).commits_url
554
+ end
555
+
556
+ def commits(dependency)
557
+ metadata_finder(dependency).commits
558
+ end
559
+
560
+ def maintainer_changes(dependency)
561
+ metadata_finder(dependency).maintainer_changes
562
+ end
563
+
564
+ def source_url(dependency)
565
+ metadata_finder(dependency).source_url
566
+ end
567
+
568
+ def homepage_url(dependency)
569
+ metadata_finder(dependency).homepage_url
570
+ end
571
+
572
+ def metadata_finder(dependency)
573
+ @metadata_finder ||= {}
574
+ @metadata_finder[dependency.name] ||=
575
+ MetadataFinders.
576
+ for_package_manager(dependency.package_manager).
577
+ new(dependency: dependency, credentials: credentials)
578
+ end
579
+
580
+ def previous_version(dependency)
581
+ if dependency.previous_version.match?(/^[0-9a-f]{40}$/)
582
+ return previous_ref(dependency) if ref_changed?(dependency)
583
+
584
+ "`#{dependency.previous_version[0..6]}`"
585
+ elsif dependency.version == dependency.previous_version &&
586
+ package_manager == "docker"
587
+ digest =
588
+ dependency.previous_requirements.
589
+ map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
590
+ compact.first
591
+ "`#{digest.split(':').last[0..6]}`"
592
+ else
593
+ dependency.previous_version
594
+ end
595
+ end
596
+
597
+ def new_version(dependency)
598
+ if dependency.version.match?(/^[0-9a-f]{40}$/)
599
+ return new_ref(dependency) if ref_changed?(dependency)
600
+
601
+ "`#{dependency.version[0..6]}`"
602
+ elsif dependency.version == dependency.previous_version &&
603
+ package_manager == "docker"
604
+ digest =
605
+ dependency.requirements.
606
+ map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
607
+ compact.first
608
+ "`#{digest.split(':').last[0..6]}`"
609
+ else
610
+ dependency.version
611
+ end
612
+ end
613
+
614
+ def previous_ref(dependency)
615
+ dependency.previous_requirements.map do |r|
616
+ r.dig(:source, "ref") || r.dig(:source, :ref)
617
+ end.compact.first
618
+ end
619
+
620
+ def new_ref(dependency)
621
+ dependency.requirements.map do |r|
622
+ r.dig(:source, "ref") || r.dig(:source, :ref)
623
+ end.compact.first
624
+ end
625
+
626
+ def old_library_requirement(dependency)
627
+ old_reqs =
628
+ dependency.previous_requirements - dependency.requirements
629
+
630
+ gemspec =
631
+ old_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
632
+ return gemspec.fetch(:requirement) if gemspec
633
+
634
+ old_reqs.first.fetch(:requirement)
635
+ end
636
+
637
+ def new_library_requirement(dependency)
638
+ updated_reqs =
639
+ dependency.requirements - dependency.previous_requirements
640
+
641
+ gemspec =
642
+ updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
643
+ return gemspec.fetch(:requirement) if gemspec
644
+
645
+ updated_reqs.first.fetch(:requirement)
646
+ end
647
+
648
+ def link_issues(text:, dependency:)
649
+ text.gsub(ISSUE_TAG_REGEX) do |mention|
650
+ number = mention.tr("#", "").gsub("GH-", "")
651
+ "[#{mention}](#{source_url(dependency)}/issues/#{number})"
652
+ end
653
+ end
654
+
655
+ def fix_relative_links(text:, base_url:)
656
+ text.gsub(/\[.*?\]\(\..*?\)/) do |link|
657
+ relative_path = link.match(/\((\..*?)\)/).captures.last
658
+ base = base_url.split("://").last.gsub(%r{[^/]*$}, "")
659
+ path = File.join(base, relative_path)
660
+ absolute_path =
661
+ base_url.sub(
662
+ %r{(?<=://).*$},
663
+ Pathname.new(path).cleanpath.to_s
664
+ )
665
+ link.gsub(relative_path, absolute_path)
666
+ end
667
+ end
668
+
669
+ def sanitize_links_and_mentions(text)
670
+ text = text.gsub(%r{(?<![A-Za-z0-9\-])@[A-Za-z0-9\-/]+}) do |mention|
671
+ next mention if mention.include?("/")
672
+
673
+ "[**#{mention.tr('@', '')}**]"\
674
+ "(https://github.com/#{mention.tr('@', '')})"
675
+ end
676
+
677
+ text.gsub(GITHUB_REF_REGEX) do |ref|
678
+ ref.gsub("github.com", "github-redirect.dependabot.com")
679
+ end
680
+ end
681
+
682
+ def sanitize_template_tags(text)
683
+ text.gsub(/\<.*?\>/) do |tag|
684
+ tag_contents = tag.match(/\<(.*?)\>/).captures.first.strip
685
+
686
+ # Unclosed calls to template overflow out of the blockquote block,
687
+ # wrecking the rest of our PRs. Other tags don't share this problem.
688
+ next "\\#{tag}" if tag_contents.start_with?("template")
689
+
690
+ tag
691
+ end
692
+ end
693
+
694
+ def ref_changed?(dependency)
695
+ previous_ref(dependency) && new_ref(dependency) &&
696
+ previous_ref(dependency) != new_ref(dependency)
697
+ end
698
+
699
+ def library?
700
+ filenames = files.map(&:name)
701
+ return true if filenames.any? { |nm| nm.match?(%r{^[^/]*\.gemspec$}) }
702
+
703
+ dependencies.none?(&:appears_in_lockfile?)
704
+ end
705
+
706
+ def switching_from_ref_to_release?(dependency)
707
+ return false unless dependency.previous_version.match?(/^[0-9a-f]{40}$/)
708
+
709
+ Gem::Version.correct?(dependency.version)
710
+ end
711
+
712
+ def includes_security_fixes?
713
+ vulnerabilities_fixed.values.flatten.any?
714
+ end
715
+
716
+ def using_angular_commit_messages?
717
+ return false if recent_commit_messages.none?
718
+
719
+ angular_messages = recent_commit_messages.select do |message|
720
+ ANGULAR_PREFIXES.any? { |pre| message.match?(/#{pre}[:(]/i) }
721
+ end
722
+
723
+ # Definitely not using Angular commits if < 30% match angular commits
724
+ if angular_messages.count.to_f / recent_commit_messages.count < 0.3
725
+ return false
726
+ end
727
+
728
+ eslint_only_pres = ESLINT_PREFIXES.map(&:downcase) - ANGULAR_PREFIXES
729
+ angular_only_pres = ANGULAR_PREFIXES - ESLINT_PREFIXES.map(&:downcase)
730
+
731
+ uses_eslint_only_pres =
732
+ recent_commit_messages.
733
+ any? { |m| eslint_only_pres.any? { |pre| m.match?(/#{pre}[:(]/i) } }
734
+
735
+ uses_angular_only_pres =
736
+ recent_commit_messages.
737
+ any? { |m| angular_only_pres.any? { |pre| m.match?(/#{pre}[:(]/i) } }
738
+
739
+ # If using any angular-only prefixes, return true
740
+ # (i.e., we assume Angular over ESLint when both are present)
741
+ return true if uses_angular_only_pres
742
+ return false if uses_eslint_only_pres
743
+
744
+ true
745
+ end
746
+
747
+ def using_eslint_commit_messages?
748
+ return false if recent_commit_messages.none?
749
+
750
+ semantic_messages = recent_commit_messages.select do |message|
751
+ ESLINT_PREFIXES.any? { |pre| message.start_with?(/#{pre}[:(]/) }
752
+ end
753
+
754
+ semantic_messages.count.to_f / recent_commit_messages.count > 0.3
755
+ end
756
+
757
+ def angular_commit_prefix
758
+ raise "Not using angular commits!" unless using_angular_commit_messages?
759
+
760
+ recent_commits_using_chore =
761
+ recent_commit_messages.
762
+ any? { |msg| msg.start_with?("chore", "Chore") }
763
+
764
+ recent_commits_using_build =
765
+ recent_commit_messages.
766
+ any? { |msg| msg.start_with?("build", "Build") }
767
+
768
+ commit_prefix =
769
+ if recent_commits_using_chore && !recent_commits_using_build
770
+ "chore"
771
+ else
772
+ "build"
773
+ end
774
+
775
+ if capitalize_angular_commit_prefix?
776
+ commit_prefix = commit_prefix.capitalize
777
+ end
778
+
779
+ commit_prefix
780
+ end
781
+
782
+ def capitalize_angular_commit_prefix?
783
+ semantic_messages = recent_commit_messages.select do |message|
784
+ ANGULAR_PREFIXES.any? { |pre| message.match?(/#{pre}[:(]/i) }
785
+ end
786
+
787
+ if semantic_messages.none?
788
+ return last_dependabot_commit_message&.match?(/^A-Z/)
789
+ end
790
+
791
+ capitalized_msgs = semantic_messages.select { |m| m.match?(/^[A-Z]/) }
792
+ capitalized_msgs.count.to_f / semantic_messages.count > 0.5
793
+ end
794
+
795
+ def using_gitmoji_commit_messages?
796
+ return false if recent_commit_messages.none?
797
+
798
+ gitmoji_messages = recent_commit_messages.select do |message|
799
+ GITMOJI_PREFIXES.any? { |pre| message.match?(/:#{pre}:/i) }
800
+ end
801
+
802
+ gitmoji_messages.count.to_f / recent_commit_messages.count > 0.3
803
+ end
804
+
805
+ def recent_commit_messages
806
+ case source.provider
807
+ when "github" then recent_github_commit_messages
808
+ when "gitlab" then recent_gitlab_commit_messages
809
+ else raise "Unsupported provider: #{source.provider}"
810
+ end
811
+ end
812
+
813
+ def recent_github_commit_messages
814
+ @recent_github_commit_messages ||=
815
+ github_client_for_source.commits(source.repo)
816
+
817
+ @recent_github_commit_messages.
818
+ reject { |c| c.author&.type == "Bot" }.
819
+ reject { |c| c.commit&.message&.start_with?("Merge") }.
820
+ map(&:commit).
821
+ map(&:message).
822
+ compact.
823
+ map(&:strip)
824
+ end
825
+
826
+ def recent_gitlab_commit_messages
827
+ @recent_gitlab_commit_messages ||=
828
+ gitlab_client_for_source.commits(source.repo)
829
+
830
+ @recent_gitlab_commit_messages.
831
+ reject { |c| c.author_email == "support@dependabot.com" }.
832
+ reject { |c| c.message&.start_with?("merge !") }.
833
+ map(&:message).
834
+ compact.
835
+ map(&:strip)
836
+ end
837
+
838
+ def last_dependabot_commit_message
839
+ case source.provider
840
+ when "github" then last_github_dependabot_commit_message
841
+ when "gitlab" then last_gitlab_dependabot_commit_message
842
+ else raise "Unsupported provider: #{source.provider}"
843
+ end
844
+ end
845
+
846
+ def last_github_dependabot_commit_message
847
+ @recent_github_commit_messages ||=
848
+ github_client_for_source.commits(source.repo)
849
+
850
+ @recent_github_commit_messages.
851
+ find { |c| c.author&.login == "dependabot[bot]" }&.
852
+ commit&.
853
+ message&.
854
+ strip
855
+ end
856
+
857
+ def last_gitlab_dependabot_commit_message
858
+ @recent_gitlab_commit_messages ||=
859
+ gitlab_client_for_source.commits(source.repo)
860
+
861
+ @recent_gitlab_commit_messages.
862
+ find { |c| c.author_email == "support@dependabot.com" }&.
863
+ message&.
864
+ strip
865
+ end
866
+
867
+ def github_client_for_source
868
+ @github_client_for_source ||=
869
+ Dependabot::Clients::GithubWithRetries.for_source(
870
+ source: source,
871
+ credentials: credentials
872
+ )
873
+ end
874
+
875
+ def gitlab_client_for_source
876
+ @gitlab_client_for_source ||= Dependabot::Clients::Gitlab.for_source(
877
+ source: source,
878
+ credentials: credentials
879
+ )
880
+ end
881
+
882
+ def package_manager
883
+ @package_manager ||= dependencies.first.package_manager
884
+ end
885
+ end
886
+ end
887
+ end
888
+ # rubocop:enable Metrics/ClassLength