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,324 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/update_checkers/base"
4
+ require "dependabot/file_updaters/ruby/bundler/requirement_replacer"
5
+ require "dependabot/git_commit_checker"
6
+
7
+ module Dependabot
8
+ module UpdateCheckers
9
+ module Ruby
10
+ class Bundler < Dependabot::UpdateCheckers::Base
11
+ require_relative "bundler/force_updater"
12
+ require_relative "bundler/file_preparer"
13
+ require_relative "bundler/requirements_updater"
14
+ require_relative "bundler/version_resolver"
15
+ require_relative "bundler/latest_version_finder"
16
+
17
+ def latest_version
18
+ return latest_version_for_git_dependency if git_dependency?
19
+
20
+ latest_version_details&.fetch(:version)
21
+ end
22
+
23
+ def latest_resolvable_version
24
+ return latest_resolvable_version_for_git_dependency if git_dependency?
25
+
26
+ latest_resolvable_version_details&.fetch(:version)
27
+ end
28
+
29
+ def latest_resolvable_version_with_no_unlock
30
+ current_ver = dependency.version
31
+ return current_ver if git_dependency? && git_commit_checker.pinned?
32
+
33
+ @latest_resolvable_version_detail_with_no_unlock ||=
34
+ version_resolver(
35
+ remove_git_source: false,
36
+ unlock_requirement: false
37
+ ).latest_resolvable_version_details
38
+
39
+ if git_dependency?
40
+ @latest_resolvable_version_detail_with_no_unlock&.fetch(:commit_sha)
41
+ else
42
+ @latest_resolvable_version_detail_with_no_unlock&.fetch(:version)
43
+ end
44
+ end
45
+
46
+ def updated_requirements
47
+ RequirementsUpdater.new(
48
+ requirements: dependency.requirements,
49
+ library: library?,
50
+ updated_source: updated_source,
51
+ latest_version: latest_version_details&.fetch(:version)&.to_s,
52
+ latest_resolvable_version:
53
+ latest_resolvable_version_details&.fetch(:version)&.to_s
54
+ ).updated_requirements
55
+ end
56
+
57
+ def requirements_unlocked_or_can_be?
58
+ dependency.requirements.
59
+ reject { |r| r[:requirement].nil? }.
60
+ all? do |req|
61
+ requirement = requirement_class.new(req[:requirement])
62
+ next true if requirement.satisfied_by?(Gem::Version.new("100000"))
63
+
64
+ file = dependency_files.find { |f| f.name == req.fetch(:file) }
65
+ updated = FileUpdaters::Ruby::Bundler::RequirementReplacer.new(
66
+ dependency: dependency,
67
+ file_type: file.name.end_with?("gemspec") ? :gemspec : :gemfile,
68
+ updated_requirement: "whatever"
69
+ ).rewrite(file.content)
70
+
71
+ updated != file.content
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def latest_version_resolvable_with_full_unlock?
78
+ return false unless latest_version
79
+
80
+ updated_dependencies = force_updater.updated_dependencies
81
+
82
+ updated_dependencies.none? do |dep|
83
+ old_version = dep.previous_version
84
+ next unless Gem::Version.correct?(old_version)
85
+ next if Gem::Version.new(old_version).prerelease?
86
+
87
+ Gem::Version.new(dep.version).prerelease?
88
+ end
89
+ rescue Dependabot::DependencyFileNotResolvable
90
+ false
91
+ end
92
+
93
+ def library?
94
+ dependency.version.nil?
95
+ end
96
+
97
+ def updated_dependencies_after_full_unlock
98
+ force_updater.updated_dependencies
99
+ end
100
+
101
+ def git_dependency?
102
+ git_commit_checker.git_dependency?
103
+ end
104
+
105
+ def latest_version_details(remove_git_source: false)
106
+ @latest_version_details ||= {}
107
+ @latest_version_details[remove_git_source] ||=
108
+ latest_version_finder(remove_git_source: remove_git_source).
109
+ latest_version_details
110
+ end
111
+
112
+ def latest_resolvable_version_details(remove_git_source: false)
113
+ @latest_resolvable_version_details ||= {}
114
+ @latest_resolvable_version_details[remove_git_source] ||=
115
+ version_resolver(remove_git_source: remove_git_source).
116
+ latest_resolvable_version_details
117
+ end
118
+
119
+ def latest_version_for_git_dependency
120
+ latest_release =
121
+ latest_version_details(remove_git_source: true)&.
122
+ fetch(:version)
123
+
124
+ # If there's been a release that includes the current pinned ref or
125
+ # that the current branch is behind, we switch to that release.
126
+ return latest_release if git_branch_or_ref_in_release?(latest_release)
127
+
128
+ # Otherwise, if the gem isn't pinned, the latest version is just the
129
+ # latest commit for the specified branch.
130
+ unless git_commit_checker.pinned?
131
+ return git_commit_checker.head_commit_for_current_branch
132
+ end
133
+
134
+ # If the dependency is pinned to a tag that looks like a version then
135
+ # we want to update that tag. The latest version will then be the SHA
136
+ # of the latest tag that looks like a version.
137
+ if git_commit_checker.pinned_ref_looks_like_version?
138
+ latest_tag = git_commit_checker.local_tag_for_latest_version
139
+ return latest_tag&.fetch(:tag_sha) || dependency.version
140
+ end
141
+
142
+ # If the dependency is pinned to a tag that doesn't look like a
143
+ # version then there's nothing we can do.
144
+ dependency.version
145
+ end
146
+
147
+ def latest_resolvable_version_for_git_dependency
148
+ latest_release = latest_resolvable_version_without_git_source
149
+
150
+ # If there's a resolvable release that includes the current pinned
151
+ # ref or that the current branch is behind, we switch to that release.
152
+ return latest_release if git_branch_or_ref_in_release?(latest_release)
153
+
154
+ # Otherwise, if the gem isn't pinned, the latest version is just the
155
+ # latest commit for the specified branch.
156
+ unless git_commit_checker.pinned?
157
+ return latest_resolvable_commit_with_unchanged_git_source
158
+ end
159
+
160
+ # If the dependency is pinned to a tag that looks like a version then
161
+ # we want to update that tag. The latest version will then be the SHA
162
+ # of the latest tag that looks like a version.
163
+ if git_commit_checker.pinned_ref_looks_like_version? &&
164
+ latest_git_tag_is_resolvable?
165
+ new_tag = git_commit_checker.local_tag_for_latest_version
166
+ return new_tag.fetch(:tag_sha)
167
+ end
168
+
169
+ # If the dependency is pinned to a tag that doesn't look like a
170
+ # version then there's nothing we can do.
171
+ dependency.version
172
+ end
173
+
174
+ def latest_resolvable_version_without_git_source
175
+ return nil unless latest_version.is_a?(Gem::Version)
176
+
177
+ latest_resolvable_version_details(remove_git_source: true)&.
178
+ fetch(:version)
179
+ rescue Dependabot::DependencyFileNotResolvable
180
+ nil
181
+ end
182
+
183
+ def latest_resolvable_commit_with_unchanged_git_source
184
+ details = latest_resolvable_version_details(remove_git_source: false)
185
+
186
+ # If this dependency has a git version in the Gemfile.lock but not in
187
+ # the Gemfile (i.e., because they're out-of-sync) we might not get a
188
+ # commit_sha back from Bundler. In that case, return `nil`.
189
+ return unless details.key?(:commit_sha)
190
+
191
+ details.fetch(:commit_sha)
192
+ rescue Dependabot::DependencyFileNotResolvable
193
+ nil
194
+ end
195
+
196
+ def latest_git_tag_is_resolvable?
197
+ return @git_tag_resolvable if @latest_git_tag_is_resolvable_checked
198
+
199
+ @latest_git_tag_is_resolvable_checked = true
200
+
201
+ return false if git_commit_checker.local_tag_for_latest_version.nil?
202
+
203
+ replacement_tag = git_commit_checker.local_tag_for_latest_version
204
+
205
+ VersionResolver.new(
206
+ dependency: dependency,
207
+ unprepared_dependency_files: dependency_files,
208
+ credentials: credentials,
209
+ ignored_versions: ignored_versions,
210
+ replacement_git_pin: replacement_tag.fetch(:tag)
211
+ ).latest_resolvable_version_details
212
+
213
+ @git_tag_resolvable = true
214
+ rescue Dependabot::DependencyFileNotResolvable
215
+ @git_tag_resolvable = false
216
+ end
217
+
218
+ def git_branch_or_ref_in_release?(release)
219
+ return false unless release
220
+
221
+ git_commit_checker.branch_or_ref_in_release?(release)
222
+ end
223
+
224
+ def updated_source
225
+ # Never need to update source, unless a git_dependency
226
+ return dependency_source_details unless git_dependency?
227
+
228
+ # Source becomes `nil` if switching to default rubygems
229
+ return nil if should_switch_source_from_git_to_rubygems?
230
+
231
+ # Update the git tag if updating a pinned version
232
+ if git_commit_checker.pinned_ref_looks_like_version? &&
233
+ latest_git_tag_is_resolvable?
234
+ new_tag = git_commit_checker.local_tag_for_latest_version
235
+ return dependency_source_details.merge(ref: new_tag.fetch(:tag))
236
+ end
237
+
238
+ # Otherwise return the original source
239
+ dependency_source_details
240
+ end
241
+
242
+ def dependency_source_details
243
+ sources =
244
+ dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
245
+
246
+ raise "Multiple sources! #{sources.join(', ')}" if sources.count > 1
247
+
248
+ sources.first
249
+ end
250
+
251
+ def should_switch_source_from_git_to_rubygems?
252
+ return false unless git_dependency?
253
+ return false if latest_resolvable_version_for_git_dependency.nil?
254
+
255
+ Gem::Version.correct?(latest_resolvable_version_for_git_dependency)
256
+ end
257
+
258
+ def force_updater
259
+ @force_updater ||=
260
+ ForceUpdater.new(
261
+ dependency: dependency,
262
+ dependency_files: dependency_files,
263
+ credentials: credentials,
264
+ target_version: latest_version
265
+ )
266
+ end
267
+
268
+ def git_commit_checker
269
+ @git_commit_checker ||=
270
+ GitCommitChecker.new(
271
+ dependency: dependency,
272
+ credentials: credentials
273
+ )
274
+ end
275
+
276
+ def version_resolver(remove_git_source:, unlock_requirement: true)
277
+ @version_resolver ||= {}
278
+ @version_resolver[remove_git_source] ||= {}
279
+ @version_resolver[remove_git_source][unlock_requirement] ||=
280
+ begin
281
+ VersionResolver.new(
282
+ dependency: dependency,
283
+ unprepared_dependency_files: dependency_files,
284
+ credentials: credentials,
285
+ ignored_versions: ignored_versions,
286
+ remove_git_source: remove_git_source,
287
+ unlock_requirement: unlock_requirement,
288
+ latest_allowable_version: latest_version
289
+ )
290
+ end
291
+ end
292
+
293
+ def latest_version_finder(remove_git_source:)
294
+ @latest_version_finder ||= {}
295
+ @latest_version_finder[remove_git_source] ||=
296
+ begin
297
+ prepared_dependency_files = prepared_dependency_files(
298
+ remove_git_source: remove_git_source,
299
+ unlock_requirement: true
300
+ )
301
+
302
+ LatestVersionFinder.new(
303
+ dependency: dependency,
304
+ dependency_files: prepared_dependency_files,
305
+ credentials: credentials,
306
+ ignored_versions: ignored_versions
307
+ )
308
+ end
309
+ end
310
+
311
+ def prepared_dependency_files(remove_git_source:, unlock_requirement:,
312
+ latest_allowable_version: nil)
313
+ FilePreparer.new(
314
+ dependency: dependency,
315
+ dependency_files: dependency_files,
316
+ remove_git_source: remove_git_source,
317
+ unlock_requirement: unlock_requirement,
318
+ latest_allowable_version: latest_allowable_version
319
+ ).prepared_dependency_files
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/dependency_file"
4
+ require "dependabot/update_checkers/ruby/bundler"
5
+ require "dependabot/file_updaters/ruby/bundler/gemspec_sanitizer"
6
+ require "dependabot/file_updaters/ruby/bundler/git_pin_replacer"
7
+ require "dependabot/file_updaters/ruby/bundler/git_source_remover"
8
+ require "dependabot/file_updaters/ruby/bundler/requirement_replacer"
9
+ require "dependabot/file_updaters/ruby/bundler/gemspec_dependency_name_finder"
10
+ require "dependabot/file_updaters/ruby/bundler/lockfile_updater"
11
+ require "dependabot/update_checkers/ruby/bundler/ruby_requirement_setter"
12
+
13
+ module Dependabot
14
+ module UpdateCheckers
15
+ module Ruby
16
+ class Bundler
17
+ # This class takes a set of dependency files and sanitizes them for use
18
+ # in UpdateCheckers::Ruby::Bundler. In particular, it:
19
+ # - Removes any version requirement on the dependency being updated
20
+ # (in the Gemfile)
21
+ # - Sanitizes any provided gemspecs to remove file imports etc. (since
22
+ # Dependabot doesn't pull down the entire repo). This process is
23
+ # imperfect - an alternative would be to clone the repo
24
+ # - Sets the ruby version in the Gemfile to be the lowest possible
25
+ # version allowed by the gemspec, if the gemspec has a required ruby
26
+ # version range
27
+ class FilePreparer
28
+ VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
29
+
30
+ # Can't be a constant because some of these don't exist in bundler
31
+ # 1.15, which Heroku uses, which causes an exception on boot.
32
+ def gemspec_sources
33
+ [
34
+ ::Bundler::Source::Path,
35
+ ::Bundler::Source::Gemspec
36
+ ]
37
+ end
38
+
39
+ def initialize(dependency_files:, dependency:,
40
+ remove_git_source: false,
41
+ unlock_requirement: true,
42
+ replacement_git_pin: nil,
43
+ latest_allowable_version: nil,
44
+ lock_ruby_version: true)
45
+ @dependency_files = dependency_files
46
+ @dependency = dependency
47
+ @remove_git_source = remove_git_source
48
+ @unlock_requirement = unlock_requirement
49
+ @replacement_git_pin = replacement_git_pin
50
+ @latest_allowable_version = latest_allowable_version
51
+ @lock_ruby_version = lock_ruby_version
52
+ end
53
+
54
+ # rubocop:disable Metrics/AbcSize
55
+ # rubocop:disable Metrics/MethodLength
56
+ def prepared_dependency_files
57
+ files = []
58
+
59
+ if gemfile
60
+ files << DependencyFile.new(
61
+ name: gemfile.name,
62
+ content: gemfile_content_for_update_check(gemfile),
63
+ directory: gemfile.directory
64
+ )
65
+ end
66
+
67
+ top_level_gemspecs.each do |gemspec|
68
+ files << DependencyFile.new(
69
+ name: gemspec.name,
70
+ content: gemspec_content_for_update_check(gemspec),
71
+ directory: gemspec.directory
72
+ )
73
+ end
74
+
75
+ path_gemspecs.each do |file|
76
+ files << DependencyFile.new(
77
+ name: file.name,
78
+ content: sanitize_gemspec_content(file.content),
79
+ directory: file.directory
80
+ )
81
+ end
82
+
83
+ evaled_gemfiles.each do |file|
84
+ files << DependencyFile.new(
85
+ name: file.name,
86
+ content: gemfile_content_for_update_check(file),
87
+ directory: file.directory
88
+ )
89
+ end
90
+
91
+ # No editing required for lockfile or Ruby version file
92
+ files += [lockfile, ruby_version_file, *imported_ruby_files].compact
93
+ end
94
+ # rubocop:enable Metrics/AbcSize
95
+ # rubocop:enable Metrics/MethodLength
96
+
97
+ private
98
+
99
+ attr_reader :dependency_files, :dependency, :replacement_git_pin,
100
+ :latest_allowable_version
101
+
102
+ def remove_git_source?
103
+ @remove_git_source
104
+ end
105
+
106
+ def unlock_requirement?
107
+ @unlock_requirement
108
+ end
109
+
110
+ def replace_git_pin?
111
+ !replacement_git_pin.nil?
112
+ end
113
+
114
+ def gemfile
115
+ dependency_files.find { |f| f.name == "Gemfile" } ||
116
+ dependency_files.find { |f| f.name == "gems.rb" }
117
+ end
118
+
119
+ def evaled_gemfiles
120
+ dependency_files.
121
+ reject { |f| f.name.end_with?(".gemspec") }.
122
+ reject { |f| f.name.end_with?(".lock") }.
123
+ reject { |f| f.name.end_with?(".ruby-version") }.
124
+ reject { |f| f.name == "Gemfile" }.
125
+ reject { |f| f.name == "gems.rb" }.
126
+ reject { |f| f.name == "gems.locked" }
127
+ end
128
+
129
+ def lockfile
130
+ dependency_files.find { |f| f.name == "Gemfile.lock" } ||
131
+ dependency_files.find { |f| f.name == "gems.locked" }
132
+ end
133
+
134
+ def top_level_gemspecs
135
+ dependency_files.select { |f| f.name.match?(%r{^[^/]*\.gemspec$}) }
136
+ end
137
+
138
+ def ruby_version_file
139
+ dependency_files.find { |f| f.name == ".ruby-version" }
140
+ end
141
+
142
+ def path_gemspecs
143
+ all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
144
+ all - top_level_gemspecs
145
+ end
146
+
147
+ def imported_ruby_files
148
+ dependency_files.
149
+ select { |f| f.name.end_with?(".rb") }.
150
+ reject { |f| f.name == "gems.rb" }
151
+ end
152
+
153
+ def gemfile_content_for_update_check(file)
154
+ content = file.content
155
+ content = replace_gemfile_constraint(content, file.name)
156
+ content = remove_git_source(content) if remove_git_source?
157
+ content = replace_git_pin(content) if replace_git_pin?
158
+ content = lock_ruby_version(content) if lock_ruby_version?(file)
159
+ content
160
+ end
161
+
162
+ def gemspec_content_for_update_check(gemspec)
163
+ content = gemspec.content
164
+ content = replace_gemspec_constraint(content, gemspec.name)
165
+ sanitize_gemspec_content(content)
166
+ end
167
+
168
+ def replace_gemfile_constraint(content, filename)
169
+ FileUpdaters::Ruby::Bundler::RequirementReplacer.new(
170
+ dependency: dependency,
171
+ file_type: :gemfile,
172
+ updated_requirement: updated_version_requirement_string(filename),
173
+ insert_if_bare: true
174
+ ).rewrite(content)
175
+ end
176
+
177
+ def replace_gemspec_constraint(content, filename)
178
+ FileUpdaters::Ruby::Bundler::RequirementReplacer.new(
179
+ dependency: dependency,
180
+ file_type: :gemspec,
181
+ updated_requirement: updated_version_requirement_string(filename),
182
+ insert_if_bare: true
183
+ ).rewrite(content)
184
+ end
185
+
186
+ def sanitize_gemspec_content(gemspec_content)
187
+ new_version = replacement_version_for_gemspec(gemspec_content)
188
+
189
+ FileUpdaters::Ruby::Bundler::GemspecSanitizer.
190
+ new(replacement_version: new_version).
191
+ rewrite(gemspec_content)
192
+ end
193
+
194
+ def updated_version_requirement_string(filename)
195
+ lower_bound_req = updated_version_req_lower_bound(filename)
196
+
197
+ return lower_bound_req if latest_allowable_version.nil?
198
+ unless Gem::Version.correct?(latest_allowable_version)
199
+ return lower_bound_req
200
+ end
201
+
202
+ lower_bound_req + ", <= #{latest_allowable_version}"
203
+ end
204
+
205
+ def updated_version_req_lower_bound(filename)
206
+ original_req = dependency.requirements.
207
+ find { |r| r.fetch(:file) == filename }&.
208
+ fetch(:requirement)
209
+
210
+ if original_req && !unlock_requirement? then original_req
211
+ elsif dependency.version&.match?(/^[0-9a-f]{40}$/) then ">= 0"
212
+ elsif dependency.version then ">= #{dependency.version}"
213
+ else
214
+ version_for_requirement =
215
+ dependency.requirements.map { |r| r[:requirement] }.
216
+ reject { |req_string| req_string.start_with?("<") }.
217
+ select { |req_string| req_string.match?(VERSION_REGEX) }.
218
+ map { |req_string| req_string.match(VERSION_REGEX) }.
219
+ select { |version| Gem::Version.correct?(version) }.
220
+ max_by { |version| Gem::Version.new(version) }
221
+
222
+ ">= #{version_for_requirement || 0}"
223
+ end
224
+ end
225
+
226
+ def remove_git_source(content)
227
+ FileUpdaters::Ruby::Bundler::GitSourceRemover.new(
228
+ dependency: dependency
229
+ ).rewrite(content)
230
+ end
231
+
232
+ def replace_git_pin(content)
233
+ FileUpdaters::Ruby::Bundler::GitPinReplacer.new(
234
+ dependency: dependency,
235
+ new_pin: replacement_git_pin
236
+ ).rewrite(content)
237
+ end
238
+
239
+ def lock_ruby_version(gemfile_content)
240
+ top_level_gemspecs.each do |gs|
241
+ gemfile_content =
242
+ RubyRequirementSetter.new(gemspec: gs).rewrite(gemfile_content)
243
+ end
244
+
245
+ gemfile_content
246
+ end
247
+
248
+ def lock_ruby_version?(file)
249
+ @lock_ruby_version && file == gemfile
250
+ end
251
+
252
+ def replacement_version_for_gemspec(gemspec_content)
253
+ return "0.0.1" unless lockfile
254
+
255
+ gemspec_specs =
256
+ ::Bundler::LockfileParser.new(sanitized_lockfile_content).specs.
257
+ select { |s| gemspec_sources.include?(s.source.class) }
258
+
259
+ gem_name =
260
+ FileUpdaters::Ruby::Bundler::GemspecDependencyNameFinder.
261
+ new(gemspec_content: gemspec_content).
262
+ dependency_name
263
+
264
+ return gemspec_specs.first&.version || "0.0.1" unless gem_name
265
+
266
+ spec = gemspec_specs.find { |s| s.name == gem_name }
267
+ spec&.version || gemspec_specs.first&.version || "0.0.1"
268
+ end
269
+
270
+ def sanitized_lockfile_content
271
+ re = FileUpdaters::Ruby::Bundler::LockfileUpdater::LOCKFILE_ENDING
272
+ lockfile.content.gsub(re, "")
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end