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,363 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "python_requirement_parser"
4
+ require "dependabot/file_fetchers/python/pip"
5
+ require "dependabot/file_updaters/python/pip"
6
+ require "dependabot/shared_helpers"
7
+
8
+ # rubocop:disable Metrics/ClassLength
9
+ module Dependabot
10
+ module FileUpdaters
11
+ module Python
12
+ class Pip
13
+ class PipCompileFileUpdater
14
+ require_relative "requirement_replacer"
15
+ require_relative "requirement_file_updater"
16
+ require_relative "setup_file_sanitizer"
17
+
18
+ UNSAFE_PACKAGES = %w(setuptools distribute pip).freeze
19
+
20
+ attr_reader :dependencies, :dependency_files, :credentials
21
+
22
+ def initialize(dependencies:, dependency_files:, credentials:)
23
+ @dependencies = dependencies
24
+ @dependency_files = dependency_files
25
+ @credentials = credentials
26
+ end
27
+
28
+ def updated_dependency_files
29
+ return @updated_dependency_files if @update_already_attempted
30
+
31
+ @update_already_attempted = true
32
+ @updated_dependency_files ||= fetch_updated_dependency_files
33
+ end
34
+
35
+ private
36
+
37
+ def dependency
38
+ # For now, we'll only ever be updating a single dependency
39
+ dependencies.first
40
+ end
41
+
42
+ def fetch_updated_dependency_files
43
+ updated_compiled_files = compile_new_requirement_files
44
+ updated_manifest_files = update_manifest_files
45
+
46
+ updated_files = updated_compiled_files + updated_manifest_files
47
+ updated_uncompiled_files = update_uncompiled_files(updated_files)
48
+
49
+ [
50
+ *updated_manifest_files,
51
+ *updated_compiled_files,
52
+ *updated_uncompiled_files
53
+ ]
54
+ end
55
+
56
+ def compile_new_requirement_files
57
+ SharedHelpers.in_a_temporary_directory do
58
+ write_updated_dependency_files
59
+
60
+ filenames_to_compile.each do |filename|
61
+ # Shell out to pip-compile, generate a new set of requirements.
62
+ # This is slow, as pip-compile needs to do installs.
63
+ run_command(
64
+ "pyenv exec pip-compile #{pip_compile_options(filename)} "\
65
+ "-P #{dependency.name} #{filename}"
66
+ )
67
+ end
68
+
69
+ dependency_files.map do |file|
70
+ next unless file.name.end_with?(".txt")
71
+
72
+ updated_content = File.read(file.name)
73
+
74
+ updated_content =
75
+ replace_header_with_original(updated_content, file.content)
76
+ next if updated_content == file.content
77
+
78
+ file.dup.tap { |f| f.content = updated_content }
79
+ end.compact
80
+ end
81
+ end
82
+
83
+ def update_manifest_files
84
+ dependency_files.map do |file|
85
+ next unless file.name.end_with?(".in")
86
+
87
+ file = file.dup
88
+ updated_content = update_dependency_requirement(file)
89
+ next if updated_content == file.content
90
+
91
+ file.content = updated_content
92
+ file
93
+ end.compact
94
+ end
95
+
96
+ def update_uncompiled_files(updated_files)
97
+ updated_filenames = updated_files.map(&:name)
98
+ old_reqs = dependency.previous_requirements.
99
+ reject { |r| updated_filenames.include?(r[:file]) }
100
+ new_reqs = dependency.requirements.
101
+ reject { |r| updated_filenames.include?(r[:file]) }
102
+
103
+ return [] if new_reqs.none?
104
+
105
+ files = dependency_files.
106
+ reject { |file| updated_filenames.include?(file.name) }
107
+
108
+ args = dependency.to_h
109
+ args = Hash[args.keys.map { |k| [k.to_sym, args[k]] }]
110
+ args[:requirements] = new_reqs
111
+ args[:previous_requirements] = old_reqs
112
+
113
+ RequirementFileUpdater.new(
114
+ dependencies: [Dependency.new(**args)],
115
+ dependency_files: files,
116
+ credentials: credentials
117
+ ).updated_dependency_files
118
+ end
119
+
120
+ def run_command(command)
121
+ command = command.dup
122
+ raw_response = nil
123
+ IO.popen(command, err: %i(child out)) do |process|
124
+ raw_response = process.read
125
+ end
126
+
127
+ # Raise an error with the output from the shell session if
128
+ # pip-compile returns a non-zero status
129
+ return if $CHILD_STATUS.success?
130
+
131
+ raise SharedHelpers::HelperSubprocessFailed.new(
132
+ raw_response,
133
+ command
134
+ )
135
+ rescue SharedHelpers::HelperSubprocessFailed => error
136
+ original_error ||= error
137
+ msg = error.message
138
+
139
+ relevant_error =
140
+ if error_suggests_bad_python_version?(msg) then original_error
141
+ else error
142
+ end
143
+
144
+ raise relevant_error unless error_suggests_bad_python_version?(msg)
145
+ raise relevant_error if File.exist?(".python-version")
146
+
147
+ command = "pyenv local 2.7.15 && " + command
148
+ retry
149
+ ensure
150
+ FileUtils.remove_entry(".python-version", true)
151
+ end
152
+
153
+ def error_suggests_bad_python_version?(message)
154
+ return true if message.include?("not find a version that satisfies")
155
+
156
+ message.include?('Command "python setup.py egg_info" failed')
157
+ end
158
+
159
+ def write_updated_dependency_files
160
+ dependency_files.each do |file|
161
+ next if file.name == ".python-version"
162
+
163
+ path = file.name
164
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
165
+ File.write(path, freeze_dependency_requirement(file))
166
+ end
167
+
168
+ setup_files.each do |file|
169
+ path = file.name
170
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
171
+ File.write(path, sanitized_setup_file_content(file))
172
+ end
173
+
174
+ setup_cfg_files.each do |file|
175
+ path = file.name
176
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
177
+ File.write(path, "[metadata]\nname = sanitized-package\n")
178
+ end
179
+ end
180
+
181
+ def sanitized_setup_file_content(file)
182
+ @sanitized_setup_file_content ||= {}
183
+ if @sanitized_setup_file_content[file.name]
184
+ return @sanitized_setup_file_content[file.name]
185
+ end
186
+
187
+ @sanitized_setup_file_content[file.name] =
188
+ SetupFileSanitizer.
189
+ new(setup_file: file, setup_cfg: setup_cfg(file)).
190
+ sanitized_content
191
+ end
192
+
193
+ def setup_cfg(file)
194
+ dependency_files.find do |f|
195
+ f.name == file.name.sub(/\.py$/, ".cfg")
196
+ end
197
+ end
198
+
199
+ def freeze_dependency_requirement(file)
200
+ return file.content unless file.name.end_with?(".in")
201
+
202
+ old_req = dependency.previous_requirements.
203
+ find { |r| r[:file] == file.name }
204
+
205
+ return file.content unless old_req
206
+ return file.content if old_req == "==#{dependency.version}"
207
+
208
+ RequirementReplacer.new(
209
+ content: file.content,
210
+ dependency_name: dependency.name,
211
+ old_requirement: old_req[:requirement],
212
+ new_requirement: "==#{dependency.version}"
213
+ ).updated_content
214
+ end
215
+
216
+ def update_dependency_requirement(file)
217
+ return file.content unless file.name.end_with?(".in")
218
+
219
+ old_req = dependency.previous_requirements.
220
+ find { |r| r[:file] == file.name }
221
+ new_req = dependency.requirements.
222
+ find { |r| r[:file] == file.name }
223
+ return file.content unless old_req&.fetch(:requirement)
224
+ return file.content if old_req == new_req
225
+
226
+ RequirementReplacer.new(
227
+ content: file.content,
228
+ dependency_name: dependency.name,
229
+ old_requirement: old_req[:requirement],
230
+ new_requirement: new_req[:requirement]
231
+ ).updated_content
232
+ end
233
+
234
+ def replace_header_with_original(updated_content, original_content)
235
+ original_header_lines =
236
+ original_content.lines.take_while { |l| l.start_with?("#") }
237
+
238
+ updated_content_lines =
239
+ updated_content.lines.drop_while { |l| l.start_with?("#") }
240
+
241
+ [*original_header_lines, *updated_content_lines].join
242
+ end
243
+
244
+ def pip_compile_options(filename)
245
+ current_requirements_file_name = filename.sub(/\.in$/, ".txt")
246
+
247
+ requirements_file =
248
+ dependency_files.
249
+ find { |f| f.name == current_requirements_file_name }
250
+
251
+ return unless requirements_file
252
+
253
+ options = ""
254
+
255
+ if requirements_file.content.include?("--hash=sha")
256
+ options += " --generate-hashes"
257
+ end
258
+
259
+ if includes_unsafe_packages?(requirements_file.content)
260
+ options += " --allow-unsafe"
261
+ end
262
+
263
+ unless requirements_file.content.include?("# via ")
264
+ options += " --no-annotate"
265
+ end
266
+
267
+ unless requirements_file.content.include?("autogenerated by pip-c")
268
+ options += " --no-header"
269
+ end
270
+
271
+ options.strip
272
+ end
273
+
274
+ def includes_unsafe_packages?(content)
275
+ UNSAFE_PACKAGES.any? { |n| content.match?(/^#{Regexp.quote(n)}==/) }
276
+ end
277
+
278
+ def filenames_to_compile
279
+ files_from_reqs =
280
+ dependency.requirements.
281
+ map { |r| r[:file] }.
282
+ select { |fn| fn.end_with?(".in") }
283
+
284
+ files_from_compiled_files =
285
+ pip_compile_files.map(&:name).select do |fn|
286
+ compiled_file = dependency_files.
287
+ find { |f| f.name == fn.gsub(/\.in$/, ".txt") }
288
+ compiled_file_includes_dependency?(compiled_file)
289
+ end
290
+
291
+ filenames = [*files_from_reqs, *files_from_compiled_files].uniq
292
+
293
+ order_filenames_for_compilation(filenames)
294
+ end
295
+
296
+ def compiled_file_includes_dependency?(compiled_file)
297
+ return false unless compiled_file
298
+
299
+ regex = PythonRequirementParser::INSTALL_REQ_WITH_REQUIREMENT
300
+
301
+ matches = []
302
+ compiled_file.content.scan(regex) { matches << Regexp.last_match }
303
+ matches.any? { |m| normalise(m[:name]) == dependency.name }
304
+ end
305
+
306
+ # See https://www.python.org/dev/peps/pep-0503/#normalized-names
307
+ def normalise(name)
308
+ name.downcase.gsub(/[-_.]+/, "-")
309
+ end
310
+
311
+ # If the files we need to update require one another then we need to
312
+ # update them in the right order
313
+ def order_filenames_for_compilation(filenames)
314
+ ordered_filenames = []
315
+
316
+ while (remaining_filenames = filenames - ordered_filenames).any?
317
+ ordered_filenames +=
318
+ remaining_filenames.
319
+ select do |fn|
320
+ unupdated_reqs = requirement_map[fn] - ordered_filenames
321
+ (unupdated_reqs & filenames).empty?
322
+ end
323
+ end
324
+
325
+ ordered_filenames
326
+ end
327
+
328
+ def requirement_map
329
+ child_req_regex = FileFetchers::Python::Pip::CHILD_REQUIREMENT_REGEX
330
+ @requirement_map ||=
331
+ pip_compile_files.each_with_object({}) do |file, req_map|
332
+ paths = file.content.scan(child_req_regex).flatten
333
+ current_dir = File.dirname(file.name)
334
+
335
+ req_map[file.name] =
336
+ paths.map do |path|
337
+ path = File.join(current_dir, path) if current_dir != "."
338
+ path = Pathname.new(path).cleanpath.to_path
339
+ path = path.gsub(/\.txt$/, ".in")
340
+ next if path == file.name
341
+
342
+ path
343
+ end.uniq.compact
344
+ end
345
+ end
346
+
347
+ def setup_files
348
+ dependency_files.select { |f| f.name.end_with?("setup.py") }
349
+ end
350
+
351
+ def pip_compile_files
352
+ dependency_files.select { |f| f.name.end_with?(".in") }
353
+ end
354
+
355
+ def setup_cfg_files
356
+ dependency_files.select { |f| f.name.end_with?("setup.cfg") }
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
363
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,397 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "toml-rb"
4
+
5
+ require "python_requirement_parser"
6
+ require "dependabot/file_updaters/python/pip"
7
+ require "dependabot/shared_helpers"
8
+
9
+ # rubocop:disable Metrics/ClassLength
10
+ module Dependabot
11
+ module FileUpdaters
12
+ module Python
13
+ class Pip
14
+ class PipfileFileUpdater
15
+ require_relative "pipfile_preparer"
16
+ require_relative "setup_file_sanitizer"
17
+
18
+ attr_reader :dependencies, :dependency_files, :credentials
19
+
20
+ def initialize(dependencies:, dependency_files:, credentials:)
21
+ @dependencies = dependencies
22
+ @dependency_files = dependency_files
23
+ @credentials = credentials
24
+ end
25
+
26
+ def updated_dependency_files
27
+ return @updated_dependency_files if @update_already_attempted
28
+
29
+ @update_already_attempted = true
30
+ @updated_dependency_files ||= fetch_updated_dependency_files
31
+ end
32
+
33
+ private
34
+
35
+ def dependency
36
+ # For now, we'll only ever be updating a single dependency
37
+ dependencies.first
38
+ end
39
+
40
+ def fetch_updated_dependency_files
41
+ updated_files = []
42
+
43
+ if file_changed?(pipfile)
44
+ updated_files <<
45
+ updated_file(file: pipfile, content: updated_pipfile_content)
46
+ end
47
+
48
+ if lockfile
49
+ if lockfile.content == updated_lockfile_content
50
+ raise "Expected Pipfile.lock to change!"
51
+ end
52
+
53
+ updated_files <<
54
+ updated_file(file: lockfile, content: updated_lockfile_content)
55
+ end
56
+
57
+ updated_files += updated_generated_requirements_files
58
+ updated_files
59
+ end
60
+
61
+ def updated_pipfile_content
62
+ dependencies.
63
+ select { |dep| requirement_changed?(pipfile, dep) }.
64
+ reduce(pipfile.content.dup) do |content, dep|
65
+ updated_requirement =
66
+ dep.requirements.find { |r| r[:file] == pipfile.name }.
67
+ fetch(:requirement)
68
+
69
+ old_req =
70
+ dep.previous_requirements.
71
+ find { |r| r[:file] == pipfile.name }.
72
+ fetch(:requirement)
73
+
74
+ updated_content =
75
+ content.gsub(declaration_regex(dep)) do |line|
76
+ line.gsub(old_req, updated_requirement)
77
+ end
78
+
79
+ raise "Content did not change!" if content == updated_content
80
+
81
+ updated_content
82
+ end
83
+ end
84
+
85
+ def updated_lockfile_content
86
+ @updated_lockfile_content ||=
87
+ updated_generated_files.fetch(:lockfile)
88
+ end
89
+
90
+ def generate_updated_requirements_files?
91
+ return true if generated_requirements_files("default").any?
92
+
93
+ generated_requirements_files("develop").any?
94
+ end
95
+
96
+ def generated_requirements_files(type)
97
+ return [] unless lockfile
98
+
99
+ pipfile_lock_deps = parsed_lockfile[type]&.keys&.sort || []
100
+ pipfile_lock_deps = pipfile_lock_deps.map { |n| normalise(n) }
101
+
102
+ regex = PythonRequirementParser::INSTALL_REQ_WITH_REQUIREMENT
103
+
104
+ # Find any requirement files that list the same dependencies as
105
+ # the (old) Pipfile.lock. Any such files were almost certainly
106
+ # generated using `pipenv lock -r`
107
+ requirements_files.select do |req_file|
108
+ deps = []
109
+ req_file.content.scan(regex) { deps << Regexp.last_match }
110
+ deps = deps.map { |m| normalise(m[:name]) }
111
+ deps.sort == pipfile_lock_deps
112
+ end
113
+ end
114
+
115
+ def updated_generated_requirements_files
116
+ updated_files = []
117
+
118
+ generated_requirements_files("default").each do |file|
119
+ next if file.content == updated_req_content
120
+
121
+ updated_files <<
122
+ updated_file(file: file, content: updated_req_content)
123
+ end
124
+
125
+ generated_requirements_files("develop").each do |file|
126
+ next if file.content == updated_dev_req_content
127
+
128
+ updated_files <<
129
+ updated_file(file: file, content: updated_dev_req_content)
130
+ end
131
+
132
+ updated_files
133
+ end
134
+
135
+ def updated_req_content
136
+ updated_generated_files.fetch(:requirements_txt)
137
+ end
138
+
139
+ def updated_dev_req_content
140
+ updated_generated_files.fetch(:dev_requirements_txt)
141
+ end
142
+
143
+ def prepared_pipfile_content
144
+ content = updated_pipfile_content
145
+ content = freeze_other_dependencies(content)
146
+ content = freeze_dependencies_being_updated(content)
147
+ content = add_private_sources(content)
148
+ content
149
+ end
150
+
151
+ def freeze_other_dependencies(pipfile_content)
152
+ PipfilePreparer.
153
+ new(pipfile_content: pipfile_content).
154
+ freeze_top_level_dependencies_except(dependencies, lockfile)
155
+ end
156
+
157
+ def freeze_dependencies_being_updated(pipfile_content)
158
+ pipfile_object = TomlRB.parse(pipfile_content)
159
+
160
+ dependencies.each do |dep|
161
+ %w(packages dev-packages).each do |type|
162
+ names = pipfile_object[type]&.keys || []
163
+ pkg_name = names.find { |nm| normalise(nm) == dep.name }
164
+ next unless pkg_name
165
+
166
+ if pipfile_object[type][pkg_name].is_a?(Hash)
167
+ pipfile_object[type][pkg_name]["version"] =
168
+ "==#{dep.version}"
169
+ else
170
+ pipfile_object[type][pkg_name] = "==#{dep.version}"
171
+ end
172
+ end
173
+ end
174
+
175
+ TomlRB.dump(pipfile_object)
176
+ end
177
+
178
+ def add_private_sources(pipfile_content)
179
+ PipfilePreparer.
180
+ new(pipfile_content: pipfile_content).
181
+ replace_sources(credentials)
182
+ end
183
+
184
+ def updated_generated_files
185
+ @updated_generated_files ||=
186
+ SharedHelpers.in_a_temporary_directory do
187
+ SharedHelpers.with_git_configured(credentials: credentials) do
188
+ write_temporary_dependency_files(prepared_pipfile_content)
189
+
190
+ # Initialize a git repo to appease pip-tools
191
+ IO.popen("git init", err: %i(child out)) if setup_files.any?
192
+
193
+ run_pipenv_command(
194
+ pipenv_environment_variables + "pyenv exec pipenv lock"
195
+ )
196
+
197
+ result = { lockfile: File.read("Pipfile.lock") }
198
+ result[:lockfile] = post_process_lockfile(result[:lockfile])
199
+
200
+ # Generate updated requirement.txt entries, if needed.
201
+ if generate_updated_requirements_files?
202
+ generate_updated_requirements_files
203
+
204
+ result[:requirements_txt] = File.read("req.txt")
205
+ result[:dev_requirements_txt] = File.read("dev-req.txt")
206
+ end
207
+
208
+ result
209
+ end
210
+ end
211
+ end
212
+
213
+ def post_process_lockfile(updated_lockfile_content)
214
+ pipfile_hash = pipfile_hash_for(updated_pipfile_content)
215
+ original_reqs = parsed_lockfile["_meta"]["requires"]
216
+ original_source = parsed_lockfile["_meta"]["sources"]
217
+
218
+ new_lockfile = updated_lockfile_content.dup
219
+ new_lockfile_json = JSON.parse(new_lockfile)
220
+ new_lockfile_json["_meta"]["hash"]["sha256"] = pipfile_hash
221
+ new_lockfile_json["_meta"]["requires"] = original_reqs
222
+ new_lockfile_json["_meta"]["sources"] = original_source
223
+
224
+ JSON.pretty_generate(new_lockfile_json, indent: " ").
225
+ gsub(/\{\n\s*\}/, "{}").
226
+ gsub(/\}\z/, "}\n")
227
+ end
228
+
229
+ def generate_updated_requirements_files
230
+ run_pipenv_command(
231
+ pipenv_environment_variables +
232
+ "pyenv exec pipenv lock -r > req.txt"
233
+ )
234
+ run_pipenv_command(
235
+ pipenv_environment_variables +
236
+ "pyenv exec pipenv lock -r -d > dev-req.txt"
237
+ )
238
+ end
239
+
240
+ def run_pipenv_command(cmd)
241
+ raw_response = nil
242
+ IO.popen(cmd, err: %i(child out)) { |p| raw_response = p.read }
243
+
244
+ # Raise an error with the output from the shell session if Pipenv
245
+ # returns a non-zero status
246
+ return if $CHILD_STATUS.success?
247
+
248
+ raise SharedHelpers::HelperSubprocessFailed.new(raw_response, cmd)
249
+ rescue SharedHelpers::HelperSubprocessFailed => error
250
+ original_error ||= error
251
+ msg = error.message
252
+
253
+ relevant_error =
254
+ if error_suggests_bad_python_version?(msg) then original_error
255
+ else error
256
+ end
257
+
258
+ raise relevant_error unless error_suggests_bad_python_version?(msg)
259
+ raise relevant_error if cmd.include?("--two")
260
+
261
+ cmd = cmd.gsub("pipenv ", "pipenv --two ")
262
+ retry
263
+ end
264
+
265
+ def error_suggests_bad_python_version?(message)
266
+ return true if message.include?("UnsupportedPythonVersion")
267
+
268
+ message.include?('Command "python setup.py egg_info" failed')
269
+ end
270
+
271
+ def write_temporary_dependency_files(pipfile_content)
272
+ dependency_files.each do |file|
273
+ next if file.name == ".python-version"
274
+
275
+ path = file.name
276
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
277
+ File.write(path, file.content)
278
+ end
279
+
280
+ setup_files.each do |file|
281
+ path = file.name
282
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
283
+ File.write(path, sanitized_setup_file_content(file))
284
+ end
285
+
286
+ setup_cfg_files.each do |file|
287
+ path = file.name
288
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
289
+ File.write(path, "[metadata]\nname = sanitized-package\n")
290
+ end
291
+
292
+ # Overwrite the pipfile with updated content
293
+ File.write("Pipfile", pipfile_content)
294
+ end
295
+
296
+ def sanitized_setup_file_content(file)
297
+ @sanitized_setup_file_content ||= {}
298
+ if @sanitized_setup_file_content[file.name]
299
+ return @sanitized_setup_file_content[file.name]
300
+ end
301
+
302
+ @sanitized_setup_file_content[file.name] =
303
+ SetupFileSanitizer.
304
+ new(setup_file: file, setup_cfg: setup_cfg(file)).
305
+ sanitized_content
306
+ end
307
+
308
+ def setup_cfg(file)
309
+ dependency_files.find do |f|
310
+ f.name == file.name.sub(/\.py$/, ".cfg")
311
+ end
312
+ end
313
+
314
+ def pipfile_hash_for(pipfile_content)
315
+ SharedHelpers.in_a_temporary_directory do |dir|
316
+ File.write(File.join(dir, "Pipfile"), pipfile_content)
317
+ SharedHelpers.run_helper_subprocess(
318
+ command: "pyenv exec python #{python_helper_path}",
319
+ function: "get_pipfile_hash",
320
+ args: [dir]
321
+ )
322
+ end
323
+ end
324
+
325
+ def declaration_regex(dep)
326
+ escaped_name = Regexp.escape(dep.name).gsub("\\-", "[-_.]")
327
+ /(?:^|["'])#{escaped_name}["']?\s*=.*$/i
328
+ end
329
+
330
+ def file_changed?(file)
331
+ dependencies.any? { |dep| requirement_changed?(file, dep) }
332
+ end
333
+
334
+ def requirement_changed?(file, dependency)
335
+ changed_requirements =
336
+ dependency.requirements - dependency.previous_requirements
337
+
338
+ changed_requirements.any? { |f| f[:file] == file.name }
339
+ end
340
+
341
+ def updated_file(file:, content:)
342
+ updated_file = file.dup
343
+ updated_file.content = content
344
+ updated_file
345
+ end
346
+
347
+ def python_helper_path
348
+ project_root = File.join(File.dirname(__FILE__), "../../../../..")
349
+ File.join(project_root, "helpers/python/run.py")
350
+ end
351
+
352
+ # See https://www.python.org/dev/peps/pep-0503/#normalized-names
353
+ def normalise(name)
354
+ name.downcase.gsub(/[-_.]+/, "-")
355
+ end
356
+
357
+ def parsed_lockfile
358
+ @parsed_lockfile ||= JSON.parse(lockfile.content)
359
+ end
360
+
361
+ def pipfile
362
+ @pipfile ||= dependency_files.find { |f| f.name == "Pipfile" }
363
+ end
364
+
365
+ def lockfile
366
+ @lockfile ||= dependency_files.find { |f| f.name == "Pipfile.lock" }
367
+ end
368
+
369
+ def setup_files
370
+ dependency_files.select { |f| f.name.end_with?("setup.py") }
371
+ end
372
+
373
+ def setup_cfg_files
374
+ dependency_files.select { |f| f.name.end_with?("setup.cfg") }
375
+ end
376
+
377
+ def requirements_files
378
+ dependency_files.select { |f| f.name.end_with?(".txt") }
379
+ end
380
+
381
+ def pipenv_environment_variables
382
+ environment_variables = [
383
+ "PIPENV_YES=true", # Install new Python versions if needed
384
+ "PIPENV_MAX_RETRIES=3", # Retry timeouts
385
+ "PIPENV_NOSPIN=1", # Don't pollute logs with spinner
386
+ "PIPENV_TIMEOUT=600", # Set install timeout to 10 minutes
387
+ "PIP_DEFAULT_TIMEOUT=60" # Set pip timeout to 1 minute
388
+ ]
389
+
390
+ environment_variables.join(" ") + " "
391
+ end
392
+ end
393
+ end
394
+ end
395
+ end
396
+ end
397
+ # rubocop:enable Metrics/ClassLength