summaryrefslogtreecommitdiff
path: root/spec/bundler
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-02 23:07:56 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-02 23:07:56 +0000
commit59c8d50653480bef3f24517296e6ddf937fdf6bc (patch)
treedf10aaf4f3307837fe3d1d129d66f6c0c7586bc5 /spec/bundler
parent7deb37777a230837e865e0a11fb8d7c1dc6d03ce (diff)
Added bundler as default gems. Revisit [Feature #12733]
* bin/*, lib/bundler/*, lib/bundler.rb, spec/bundler, man/*: Merge from latest stable branch of bundler/bundler repository and added workaround patches. I will backport them into upstream. * common.mk, defs/gmake.mk: Added `test-bundler` task for test suite of bundler. * tool/sync_default_gems.rb: Added sync task for bundler. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65509 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/bundler')
-rw-r--r--spec/bundler/bundler/bundler_spec.rb490
-rw-r--r--spec/bundler/bundler/cli_spec.rb173
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb55
-rw-r--r--spec/bundler/bundler/definition_spec.rb358
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb22
-rw-r--r--spec/bundler/bundler/dsl_spec.rb289
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb71
-rw-r--r--spec/bundler/bundler/env_spec.rb151
-rw-r--r--spec/bundler/bundler/environment_preserver_spec.rb79
-rw-r--r--spec/bundler/bundler/fetcher/base_spec.rb76
-rw-r--r--spec/bundler/bundler/fetcher/compact_index_spec.rb103
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb287
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb250
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb99
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb161
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb270
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb351
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb179
-rw-r--r--spec/bundler/bundler/index_spec.rb36
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb29
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb47
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb62
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb153
-rw-r--r--spec/bundler/bundler/mirror_spec.rb329
-rw-r--r--spec/bundler/bundler/plugin/api/source_spec.rb82
-rw-r--r--spec/bundler/bundler/plugin/api_spec.rb83
-rw-r--r--spec/bundler/bundler/plugin/dsl_spec.rb38
-rw-r--r--spec/bundler/bundler/plugin/events_spec.rb18
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb186
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb104
-rw-r--r--spec/bundler/bundler/plugin/source_list_spec.rb25
-rw-r--r--spec/bundler/bundler/plugin_spec.rb309
-rw-r--r--spec/bundler/bundler/psyched_yaml_spec.rb9
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb187
-rw-r--r--spec/bundler/bundler/retry_spec.rb81
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb95
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb524
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb114
-rw-r--r--spec/bundler/bundler/settings/validator_spec.rb111
-rw-r--r--spec/bundler/bundler/settings_spec.rb326
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb504
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb140
-rw-r--r--spec/bundler/bundler/source/git_spec.rb28
-rw-r--r--spec/bundler/bundler/source/path_spec.rb31
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb162
-rw-r--r--spec/bundler/bundler/source/rubygems_spec.rb33
-rw-r--r--spec/bundler/bundler/source_list_spec.rb463
-rw-r--r--spec/bundler/bundler/source_spec.rb154
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb77
-rw-r--r--spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb140
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb24
-rw-r--r--spec/bundler/bundler/ui/shell_spec.rb73
-rw-r--r--spec/bundler/bundler/ui_spec.rb41
-rw-r--r--spec/bundler/bundler/uri_credentials_filter_spec.rb127
-rw-r--r--spec/bundler/bundler/vendored_persistent_spec.rb78
-rw-r--r--spec/bundler/bundler/version_ranges_spec.rb37
-rw-r--r--spec/bundler/bundler/worker_spec.rb22
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb194
-rw-r--r--spec/bundler/cache/cache_path_spec.rb32
-rw-r--r--spec/bundler/cache/gems_spec.rb304
-rw-r--r--spec/bundler/cache/git_spec.rb214
-rw-r--r--spec/bundler/cache/path_spec.rb139
-rw-r--r--spec/bundler/cache/platform_spec.rb49
-rw-r--r--spec/bundler/commands/add_spec.rb217
-rw-r--r--spec/bundler/commands/binstubs_spec.rb453
-rw-r--r--spec/bundler/commands/check_spec.rb354
-rw-r--r--spec/bundler/commands/clean_spec.rb771
-rw-r--r--spec/bundler/commands/config_spec.rb384
-rw-r--r--spec/bundler/commands/console_spec.rb106
-rw-r--r--spec/bundler/commands/doctor_spec.rb110
-rw-r--r--spec/bundler/commands/exec_spec.rb858
-rw-r--r--spec/bundler/commands/help_spec.rb98
-rw-r--r--spec/bundler/commands/info_spec.rb57
-rw-r--r--spec/bundler/commands/init_spec.rb181
-rw-r--r--spec/bundler/commands/inject_spec.rb117
-rw-r--r--spec/bundler/commands/install_spec.rb587
-rw-r--r--spec/bundler/commands/issue_spec.rb16
-rw-r--r--spec/bundler/commands/licenses_spec.rb38
-rw-r--r--spec/bundler/commands/list_spec.rb131
-rw-r--r--spec/bundler/commands/lock_spec.rb322
-rw-r--r--spec/bundler/commands/newgem_spec.rb912
-rw-r--r--spec/bundler/commands/open_spec.rb92
-rw-r--r--spec/bundler/commands/outdated_spec.rb782
-rw-r--r--spec/bundler/commands/package_spec.rb306
-rw-r--r--spec/bundler/commands/pristine_spec.rb192
-rw-r--r--spec/bundler/commands/remove_spec.rb583
-rw-r--r--spec/bundler/commands/show_spec.rb244
-rw-r--r--spec/bundler/commands/update_spec.rb943
-rw-r--r--spec/bundler/commands/version_spec.rb39
-rw-r--r--spec/bundler/commands/viz_spec.rb149
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb92
-rw-r--r--spec/bundler/install/binstubs_spec.rb43
-rw-r--r--spec/bundler/install/bundler_spec.rb177
-rw-r--r--spec/bundler/install/deploy_spec.rb423
-rw-r--r--spec/bundler/install/failure_spec.rb125
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb82
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb672
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb1351
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb384
-rw-r--r--spec/bundler/install/gemfile/install_if.rb44
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb48
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb630
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb426
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb108
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb619
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb114
-rw-r--r--spec/bundler/install/gemfile_spec.rb145
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb940
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb760
-rw-r--r--spec/bundler/install/gems/env_spec.rb107
-rw-r--r--spec/bundler/install/gems/flex_spec.rb351
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb39
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb90
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb150
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb195
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb337
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb178
-rw-r--r--spec/bundler/install/gems/win32_spec.rb26
-rw-r--r--spec/bundler/install/gemspecs_spec.rb154
-rw-r--r--spec/bundler/install/git_spec.rb65
-rw-r--r--spec/bundler/install/global_cache_spec.rb235
-rw-r--r--spec/bundler/install/path_spec.rb256
-rw-r--r--spec/bundler/install/post_bundle_message_spec.rb206
-rw-r--r--spec/bundler/install/prereleases_spec.rb41
-rw-r--r--spec/bundler/install/process_lock_spec.rb35
-rw-r--r--spec/bundler/install/redownload_spec.rb92
-rw-r--r--spec/bundler/install/security_policy_spec.rb77
-rw-r--r--spec/bundler/install/yanked_spec.rb71
-rw-r--r--spec/bundler/lock/git_spec.rb34
-rw-r--r--spec/bundler/lock/lockfile_bundler_1_spec.rb1386
-rw-r--r--spec/bundler/lock/lockfile_spec.rb1425
-rw-r--r--spec/bundler/other/bundle_ruby_spec.rb155
-rw-r--r--spec/bundler/other/cli_dispatch_spec.rb29
-rw-r--r--spec/bundler/other/compatibility_guard_spec.rb25
-rw-r--r--spec/bundler/other/ext_spec.rb66
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb282
-rw-r--r--spec/bundler/other/platform_spec.rb1312
-rw-r--r--spec/bundler/other/ssl_cert_spec.rb18
-rw-r--r--spec/bundler/plugins/command_spec.rb80
-rw-r--r--spec/bundler/plugins/hook_spec.rb109
-rw-r--r--spec/bundler/plugins/install_spec.rb257
-rw-r--r--spec/bundler/plugins/source/example_spec.rb505
-rw-r--r--spec/bundler/plugins/source_spec.rb108
-rw-r--r--spec/bundler/quality_spec.rb266
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb44
-rw-r--r--spec/bundler/realworld/double_check_spec.rb40
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb382
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb53
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb144
-rw-r--r--spec/bundler/realworld/parallel_spec.rb74
-rw-r--r--spec/bundler/resolver/basic_spec.rb308
-rw-r--r--spec/bundler/resolver/platform_spec.rb100
-rw-r--r--spec/bundler/runtime/executable_spec.rb190
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb44
-rw-r--r--spec/bundler/runtime/inline_spec.rb266
-rw-r--r--spec/bundler/runtime/load_spec.rb111
-rw-r--r--spec/bundler/runtime/platform_spec.rb150
-rw-r--r--spec/bundler/runtime/require_spec.rb452
-rw-r--r--spec/bundler/runtime/setup_spec.rb1445
-rw-r--r--spec/bundler/runtime/with_clean_env_spec.rb151
-rw-r--r--spec/bundler/spec_helper.rb161
-rw-r--r--spec/bundler/support/artifice/compact_index.rb122
-rw-r--r--spec/bundler/support/artifice/compact_index_api_missing.rb18
-rw-r--r--spec/bundler/support/artifice/compact_index_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb32
-rw-r--r--spec/bundler/support/artifice/compact_index_creds_diff_host.rb39
-rw-r--r--spec/bundler/support/artifice/compact_index_extra.rb37
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api.rb52
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api_missing.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_missing.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_forbidden.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_no_gem.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb38
-rw-r--r--spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb34
-rw-r--r--spec/bundler/support/artifice/compact_index_redirects.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb20
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_dependencies.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb20
-rw-r--r--spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint.rb100
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb19
-rw-r--r--spec/bundler/support/artifice/endpoint_api_forbidden.rb13
-rw-r--r--spec/bundler/support/artifice/endpoint_api_missing.rb18
-rw-r--r--spec/bundler/support/artifice/endpoint_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint_creds_diff_host.rb39
-rw-r--r--spec/bundler/support/artifice/endpoint_extra.rb33
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_api.rb34
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_missing.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_fallback.rb19
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail.rb13
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint_redirect.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb20
-rw-r--r--spec/bundler/support/artifice/endpoint_timeout.rb15
-rw-r--r--spec/bundler/support/artifice/fail.rb39
-rw-r--r--spec/bundler/support/artifice/vcr.rb158
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/request6
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/response24
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/gems/bundler-1.12.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/specs.4.8.gz/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RedCloth/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/allison/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ansi/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/archive-tar-minitar/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bacon/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-extras/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-git/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rcov/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rspec/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rubyforge/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-zentest/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/camping/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/echoe/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine-le/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fcgi/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/git/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/highline/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http_parser.rb/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/language/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loquacious/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mab/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/markaby/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar-cli/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/newgem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerbar/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/preforker/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rcov/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexical/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubigen/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-openid/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-yadis/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/shotgun/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spicycode-rcov/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/syntax/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tenderlove-frex/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/termios/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-spec/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-1.12.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.8.1.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.0.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.12.1.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.0.1.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.6.5.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.1.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-1.5.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-3.3.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.5.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-1.4.8.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/thread_safe-0.3.6.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.7.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-1.2.3.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request7
-rw-r--r--spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/specs.4.8.gz/GET/request7
-rw-r--r--spec/bundler/support/artifice/windows.rb49
-rw-r--r--spec/bundler/support/builders.rb819
-rw-r--r--spec/bundler/support/code_climate.rb26
-rw-r--r--spec/bundler/support/command_execution.rb57
-rw-r--r--spec/bundler/support/hax.rb67
-rw-r--r--spec/bundler/support/helpers.rb600
-rw-r--r--spec/bundler/support/indexes.rb421
-rw-r--r--spec/bundler/support/less_than_proc.rb20
-rw-r--r--spec/bundler/support/manpages.rb14
-rw-r--r--spec/bundler/support/matchers.rb246
-rw-r--r--spec/bundler/support/path.rb126
-rw-r--r--spec/bundler/support/permissions.rb12
-rw-r--r--spec/bundler/support/platforms.rb116
-rw-r--r--spec/bundler/support/rubygems_ext.rb71
-rw-r--r--spec/bundler/support/silent_logger.rb10
-rw-r--r--spec/bundler/support/sometimes.rb57
-rw-r--r--spec/bundler/support/streams.rb19
-rw-r--r--spec/bundler/support/sudo.rb18
-rw-r--r--spec/bundler/support/the_bundle.rb37
-rw-r--r--spec/bundler/update/gemfile_spec.rb66
-rw-r--r--spec/bundler/update/gems/post_install_spec.rb76
-rw-r--r--spec/bundler/update/git_spec.rb374
-rw-r--r--spec/bundler/update/path_spec.rb18
-rw-r--r--spec/bundler/update/redownload_spec.rb36
552 files changed, 45432 insertions, 0 deletions
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
new file mode 100644
index 0000000000..194d6752b2
--- /dev/null
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -0,0 +1,490 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+require "bundler"
+require "tmpdir"
+
+RSpec.describe Bundler do
+ describe "#load_gemspec_uncached" do
+ let(:app_gemspec_path) { tmp("test.gemspec") }
+ subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
+
+ context "with incorrect YAML file" do
+ before do
+ File.open(app_gemspec_path, "wb") do |f|
+ f.write strip_whitespace(<<-GEMSPEC)
+ ---
+ {:!00 ao=gu\g1= 7~f
+ GEMSPEC
+ end
+ end
+
+ it "catches YAML syntax errors" do
+ expect { subject }.to raise_error(Bundler::GemspecError, /error while loading `test.gemspec`/)
+ end
+
+ context "on Rubies with a settable YAML engine", :if => defined?(YAML::ENGINE) do
+ context "with Syck as YAML::Engine" do
+ it "raises a GemspecError after YAML load throws ArgumentError" do
+ orig_yamler = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = "syck"
+
+ expect { subject }.to raise_error(Bundler::GemspecError)
+
+ YAML::ENGINE.yamler = orig_yamler
+ end
+ end
+
+ context "with Psych as YAML::Engine" do
+ it "raises a GemspecError after YAML load throws Psych::SyntaxError" do
+ orig_yamler = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = "psych"
+
+ expect { subject }.to raise_error(Bundler::GemspecError)
+
+ YAML::ENGINE.yamler = orig_yamler
+ end
+ end
+ end
+ end
+
+ context "with correct YAML file", :if => defined?(Encoding) do
+ it "can load a gemspec with unicode characters with default ruby encoding" do
+ # spec_helper forces the external encoding to UTF-8 but that's not the
+ # default until Ruby 2.0
+ verbose = $VERBOSE
+ $VERBOSE = false
+ encoding = Encoding.default_external
+ Encoding.default_external = "ASCII"
+ $VERBOSE = verbose
+
+ File.open(app_gemspec_path, "wb") do |file|
+ file.puts <<-GEMSPEC.gsub(/^\s+/, "")
+ # -*- encoding: utf-8 -*-
+ Gem::Specification.new do |gem|
+ gem.author = "André the Giant"
+ end
+ GEMSPEC
+ end
+
+ expect(subject.author).to eq("André the Giant")
+
+ verbose = $VERBOSE
+ $VERBOSE = false
+ Encoding.default_external = encoding
+ $VERBOSE = verbose
+ end
+ end
+
+ it "sets loaded_from" do
+ app_gemspec_path.open("w") do |f|
+ f.puts <<-GEMSPEC
+ Gem::Specification.new do |gem|
+ gem.name = "validated"
+ end
+ GEMSPEC
+ end
+
+ expect(subject.loaded_from).to eq(app_gemspec_path.expand_path.to_s)
+ end
+
+ context "validate is true" do
+ subject { Bundler.load_gemspec_uncached(app_gemspec_path, true) }
+
+ it "validates the specification" do
+ app_gemspec_path.open("w") do |f|
+ f.puts <<-GEMSPEC
+ Gem::Specification.new do |gem|
+ gem.name = "validated"
+ end
+ GEMSPEC
+ end
+ expect(Bundler.rubygems).to receive(:validate).with have_attributes(:name => "validated")
+ subject
+ end
+ end
+
+ context "with gemspec containing local variables" do
+ before do
+ File.open(app_gemspec_path, "wb") do |f|
+ f.write strip_whitespace(<<-GEMSPEC)
+ must_not_leak = true
+ Gem::Specification.new do |gem|
+ gem.name = "leak check"
+ end
+ GEMSPEC
+ end
+ end
+
+ it "should not pollute the TOPLEVEL_BINDING" do
+ subject
+ expect(TOPLEVEL_BINDING.eval("local_variables")).to_not include(:must_not_leak)
+ end
+ end
+ end
+
+ describe "#which" do
+ let(:executable) { "executable" }
+ let(:path) { %w[/a /b c ../d /e] }
+ let(:expected) { "executable" }
+
+ before do
+ ENV["PATH"] = path.join(File::PATH_SEPARATOR)
+
+ allow(File).to receive(:file?).and_return(false)
+ allow(File).to receive(:executable?).and_return(false)
+ if expected
+ expect(File).to receive(:file?).with(expected).and_return(true)
+ expect(File).to receive(:executable?).with(expected).and_return(true)
+ end
+ end
+
+ subject { described_class.which(executable) }
+
+ shared_examples_for "it returns the correct executable" do
+ it "returns the expected file" do
+ expect(subject).to eq(expected)
+ end
+ end
+
+ it_behaves_like "it returns the correct executable"
+
+ context "when the executable in inside a quoted path" do
+ let(:expected) { "/e/executable" }
+ it_behaves_like "it returns the correct executable"
+ end
+
+ context "when the executable is not found" do
+ let(:expected) { nil }
+ it_behaves_like "it returns the correct executable"
+ end
+ end
+
+ describe "configuration" do
+ context "disable_shared_gems" do
+ it "should unset GEM_PATH with empty string" do
+ env = {}
+ expect(Bundler).to receive(:use_system_gems?).and_return(false)
+ Bundler.send(:configure_gem_path, env)
+ expect(env.keys).to include("GEM_PATH")
+ expect(env["GEM_PATH"]).to eq ""
+ end
+ end
+ end
+
+ describe "#rm_rf" do
+ context "the directory is world writable" do
+ let(:bundler_ui) { Bundler.ui }
+ it "should raise a friendly error" do
+ allow(File).to receive(:exist?).and_return(true)
+ allow(bundler_fileutils).to receive(:remove_entry_secure).and_raise(ArgumentError)
+ allow(File).to receive(:world_writable?).and_return(true)
+ message = <<EOF
+It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
+You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
+Please refer to http://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
+EOF
+ expect(bundler_ui).to receive(:warn).with(message)
+ expect { Bundler.send(:rm_rf, bundled_app) }.to raise_error(Bundler::PathError)
+ end
+ end
+ end
+
+ describe "#mkdir_p" do
+ it "creates a folder at the given path" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ Bundler.mkdir_p(bundled_app.join("foo", "bar"))
+ expect(bundled_app.join("foo", "bar")).to exist
+ end
+
+ context "when mkdir_p requires sudo" do
+ it "creates a new folder using sudo" do
+ expect(Bundler).to receive(:requires_sudo?).and_return(true)
+ expect(Bundler).to receive(:sudo).and_return true
+ Bundler.mkdir_p(bundled_app.join("foo"))
+ end
+ end
+
+ context "with :no_sudo option" do
+ it "forces mkdir_p to not use sudo" do
+ expect(Bundler).to receive(:requires_sudo?).and_return(true)
+ expect(Bundler).to_not receive(:sudo)
+ Bundler.mkdir_p(bundled_app.join("foo"), :no_sudo => true)
+ end
+ end
+ end
+
+ describe "#user_home" do
+ context "home directory is set" do
+ it "should return the user home" do
+ path = "/home/oggy"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return true
+ expect(Bundler.user_home).to eq(Pathname(path))
+ end
+
+ context "is not a directory" do
+ it "should issue a warning and return a temporary user home" do
+ path = "/home/oggy"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return false
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+`/home/oggy` is not a directory.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+
+ context "is not writable" do
+ let(:path) { "/home/oggy" }
+ let(:dotbundle) { "/home/oggy/.bundle" }
+
+ it "should issue a warning and return a temporary user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return false
+ allow(File).to receive(:directory?).with(dotbundle).and_return false
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+`/home/oggy` is not writable.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+
+ context ".bundle exists and have correct permissions" do
+ it "should return the user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return false
+ allow(File).to receive(:directory?).with(dotbundle).and_return true
+ allow(File).to receive(:writable?).with(dotbundle).and_return true
+ expect(Bundler.user_home).to eq(Pathname(path))
+ end
+ end
+ end
+ end
+
+ context "home directory is not set" do
+ it "should issue warning and return a temporary user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(nil)
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+Your home directory is not set.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+ end
+
+ describe "#tmp_home_path" do
+ it "should create temporary user home" do
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(false)
+ expect(FileUtils).to receive(:mkpath).once.ordered.with("/TMP/bundler/home")
+ expect(FileUtils).to receive(:mkpath).once.ordered.with("/TMP/bundler/home/USER")
+ expect(File).to receive(:chmod).with(0o777, "/TMP/bundler/home")
+ expect(Bundler.tmp_home_path("USER", "")).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+
+ describe "#requires_sudo?" do
+ let!(:tmpdir) { Dir.mktmpdir }
+ let(:bundle_path) { Pathname("#{tmpdir}/bundle") }
+
+ def clear_cached_requires_sudo
+ # Private in ruby 1.8.7
+ return unless Bundler.instance_variable_defined?(:@requires_sudo_ran)
+ Bundler.send(:remove_instance_variable, :@requires_sudo_ran)
+ Bundler.send(:remove_instance_variable, :@requires_sudo)
+ end
+
+ before do
+ clear_cached_requires_sudo
+ allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
+ allow(Bundler).to receive(:bundle_path).and_return(bundle_path)
+ end
+
+ after do
+ FileUtils.rm_rf(tmpdir)
+ clear_cached_requires_sudo
+ end
+
+ subject { Bundler.requires_sudo? }
+
+ context "bundle_path doesn't exist" do
+ it { should be false }
+
+ context "and parent dir can't be written" do
+ before do
+ FileUtils.chmod(0o500, tmpdir)
+ end
+
+ it { should be true }
+ end
+
+ context "with unwritable files in a parent dir" do
+ # Regression test for https://github.com/bundler/bundler/pull/6316
+ # It doesn't matter if there are other unwritable files so long as
+ # bundle_path can be created
+ before do
+ file = File.join(tmpdir, "unrelated_file")
+ FileUtils.touch(file)
+ FileUtils.chmod(0o400, file)
+ end
+
+ it { should be false }
+ end
+ end
+
+ context "bundle_path exists" do
+ before do
+ FileUtils.mkdir_p(bundle_path)
+ end
+
+ it { should be false }
+
+ context "and is unwritable" do
+ before do
+ FileUtils.chmod(0o500, bundle_path)
+ end
+
+ it { should be true }
+ end
+ end
+ end
+
+ describe "#requires_sudo?" do
+ before do
+ allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
+ FileUtils.mkdir_p("tmp/vendor/bundle")
+ FileUtils.mkdir_p("tmp/vendor/bin_dir")
+ end
+ after do
+ FileUtils.rm_rf("tmp/vendor/bundle")
+ FileUtils.rm_rf("tmp/vendor/bin_dir")
+ if Bundler.respond_to?(:remove_instance_variable)
+ Bundler.remove_instance_variable(:@requires_sudo_ran)
+ Bundler.remove_instance_variable(:@requires_sudo)
+ else
+ # TODO: Remove these code when Bundler drops Ruby 1.8.7 support
+ Bundler.send(:remove_instance_variable, :@requires_sudo_ran)
+ Bundler.send(:remove_instance_variable, :@requires_sudo)
+ end
+ end
+ context "writable paths" do
+ it "should return false and display nothing" do
+ allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
+ expect(Bundler.ui).to_not receive(:warn)
+ expect(Bundler.requires_sudo?).to eq(false)
+ end
+ end
+ context "unwritable paths" do
+ before do
+ FileUtils.touch("tmp/vendor/bundle/unwritable1.txt")
+ FileUtils.touch("tmp/vendor/bundle/unwritable2.txt")
+ FileUtils.touch("tmp/vendor/bin_dir/unwritable3.txt")
+ FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable1.txt")
+ FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable2.txt")
+ FileUtils.chmod(0o400, "tmp/vendor/bin_dir/unwritable3.txt")
+ end
+ it "should return true and display warn message" do
+ allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
+ bin_dir = Pathname("tmp/vendor/bin_dir/")
+
+ # allow File#writable? to be called with args other than the stubbed on below
+ allow(File).to receive(:writable?).and_call_original
+
+ # fake make the directory unwritable
+ allow(File).to receive(:writable?).with(bin_dir).and_return(false)
+ allow(Bundler).to receive(:system_bindir).and_return(Pathname("tmp/vendor/bin_dir/"))
+ message = <<-MESSAGE.chomp
+Following files may not be writable, so sudo is needed:
+ tmp/vendor/bin_dir/
+ tmp/vendor/bundle/unwritable1.txt
+ tmp/vendor/bundle/unwritable2.txt
+MESSAGE
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.requires_sudo?).to eq(true)
+ end
+ end
+ end
+
+ context "user cache dir" do
+ let(:home_path) { Pathname.new(ENV["HOME"]) }
+
+ let(:xdg_data_home) { home_path.join(".local") }
+ let(:xdg_cache_home) { home_path.join(".cache") }
+ let(:xdg_config_home) { home_path.join(".config") }
+
+ let(:bundle_user_home_default) { home_path.join(".bundle") }
+ let(:bundle_user_home_custom) { xdg_data_home.join("bundle") }
+
+ let(:bundle_user_cache_default) { bundle_user_home_default.join("cache") }
+ let(:bundle_user_cache_custom) { xdg_cache_home.join("bundle") }
+
+ let(:bundle_user_config_default) { bundle_user_home_default.join("config") }
+ let(:bundle_user_config_custom) { xdg_config_home.join("bundle") }
+
+ let(:bundle_user_plugin_default) { bundle_user_home_default.join("plugin") }
+ let(:bundle_user_plugin_custom) { xdg_data_home.join("bundle").join("plugin") }
+
+ describe "#user_bundle_path" do
+ before do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(home_path)
+ end
+
+ it "should use the default home path" do
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_default)
+ expect(Bundler.user_cache).to eq(bundle_user_cache_default)
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_default)
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_default)
+ end
+
+ it "should use custom home path as root for other paths" do
+ ENV["BUNDLE_USER_HOME"] = bundle_user_home_custom.to_s
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_custom)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_custom)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_home_custom.join("cache"))
+ expect(Bundler.user_cache).to eq(bundle_user_home_custom.join("cache"))
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_home_custom.join("config"))
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_home_custom.join("plugin"))
+ end
+
+ it "should use all custom paths, except home" do
+ ENV.delete("BUNDLE_USER_HOME")
+ ENV["BUNDLE_USER_CACHE"] = bundle_user_cache_custom.to_s
+ ENV["BUNDLE_USER_CONFIG"] = bundle_user_config_custom.to_s
+ ENV["BUNDLE_USER_PLUGIN"] = bundle_user_plugin_custom.to_s
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_custom)
+ expect(Bundler.user_cache).to eq(bundle_user_cache_custom)
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_custom)
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_custom)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
new file mode 100644
index 0000000000..c82d46587e
--- /dev/null
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+
+RSpec.describe "bundle executable" do
+ it "returns non-zero exit status when passed unrecognized options" do
+ bundle "--invalid_argument"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "returns non-zero exit status when passed unrecognized task" do
+ bundle "unrecognized-task"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "looks for a binary and executes it if it's named bundler-<task>" do
+ File.open(tmp("bundler-testtasks"), "w", 0o755) do |f|
+ ruby = ENV["BUNDLE_RUBY"] || "/usr/bin/env ruby"
+ f.puts "#!#{ruby}\nputs 'Hello, world'\n"
+ end
+
+ with_path_added(tmp) do
+ bundle "testtasks"
+ end
+
+ expect(exitstatus).to be_zero if exitstatus
+ expect(out).to eq("Hello, world")
+ end
+
+ context "with no arguments" do
+ it "prints a concise help message", :bundler => "2" do
+ bundle! ""
+ expect(last_command.stderr).to be_empty
+ expect(last_command.stdout).to include("Bundler version #{Bundler::VERSION}").
+ and include("\n\nBundler commands:\n\n").
+ and include("\n\n Primary commands:\n").
+ and include("\n\n Utilities:\n").
+ and include("\n\nOptions:\n")
+ end
+ end
+
+ context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do
+ it "ignores it" do
+ gemfile bundled_app("Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :env => { "BUNDLE_GEMFILE" => "" }
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "when ENV['RUBYGEMS_GEMDEPS'] is set" do
+ it "displays a warning" do
+ gemfile bundled_app("Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "foo" }
+ expect(out).to include("RUBYGEMS_GEMDEPS")
+ expect(out).to include("conflict with Bundler")
+
+ bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "" }
+ expect(out).not_to include("RUBYGEMS_GEMDEPS")
+ end
+ end
+
+ context "with --verbose" do
+ it "prints the running command" do
+ gemfile ""
+ bundle! "info bundler", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
+ end
+
+ it "doesn't print defaults" do
+ install_gemfile! "", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ end
+
+ it "doesn't print defaults" do
+ install_gemfile! "", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ end
+ end
+
+ describe "printing the outdated warning" do
+ shared_examples_for "no warning" do
+ it "prints no warning" do
+ bundle "fail"
+ expect(last_command.stdboth).to eq("Could not find command \"fail\".")
+ end
+ end
+
+ let(:bundler_version) { "1.1" }
+ let(:latest_version) { nil }
+ before do
+ bundle! "config --global disable_version_check false"
+
+ simulate_bundler_version(bundler_version)
+ if latest_version
+ info_path = home(".bundle/cache/compact_index/rubygems.org.443.29b0360b937aa4d161703e6160654e47/info/bundler")
+ info_path.parent.mkpath
+ info_path.open("w") {|f| f.write "#{latest_version}\n" }
+ end
+ end
+
+ context "when there is no latest version" do
+ include_examples "no warning"
+ end
+
+ context "when the latest version is equal to the current version" do
+ let(:latest_version) { bundler_version }
+ include_examples "no warning"
+ end
+
+ context "when the latest version is less than the current version" do
+ let(:latest_version) { "0.9" }
+ include_examples "no warning"
+ end
+
+ context "when the latest version is greater than the current version" do
+ let(:latest_version) { "222.0" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(last_command.stdout).to start_with(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To install the latest version, run `gem install bundler`
+ EOS
+ end
+
+ context "and disable_version_check is set" do
+ before { bundle! "config disable_version_check true" }
+ include_examples "no warning"
+ end
+
+ context "running a parseable command" do
+ it "prints no warning" do
+ bundle! "config --parseable foo"
+ expect(last_command.stdboth).to eq ""
+
+ bundle "platform --ruby"
+ expect(last_command.stdboth).to eq "Could not locate Gemfile"
+ end
+ end
+
+ context "and is a pre-release" do
+ let(:latest_version) { "222.0.0.pre.4" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(last_command.stdout).to start_with(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To install the latest version, run `gem install bundler --pre`
+ EOS
+ end
+ end
+ end
+ end
+end
+
+RSpec.describe "bundler executable" do
+ it "shows the bundler version just as the `bundle` executable does", :bundler => "< 2" do
+ bundler "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "shows the bundler version just as the `bundle` executable does", :bundler => "2" do
+ bundler "--version"
+ expect(out).to eq(Bundler::VERSION)
+ end
+end
diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb
new file mode 100644
index 0000000000..fd554a7b0d
--- /dev/null
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "net/http"
+require "bundler/compact_index_client"
+require "bundler/compact_index_client/updater"
+
+RSpec.describe Bundler::CompactIndexClient::Updater do
+ let(:fetcher) { double(:fetcher) }
+ let(:local_path) { Pathname("/tmp/localpath") }
+ let(:remote_path) { double(:remote_path) }
+
+ subject(:updater) { described_class.new(fetcher) }
+
+ context "when the ETag header is missing" do
+ # Regression test for https://github.com/bundler/bundler/issues/5463
+
+ let(:response) { double(:response, :body => "") }
+
+ it "MisMatchedChecksumError is raised" do
+ # Twice: #update retries on failure
+ expect(response).to receive(:[]).with("Content-Encoding").twice { "" }
+ expect(response).to receive(:[]).with("ETag").twice { nil }
+ expect(fetcher).to receive(:call).twice { response }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::CompactIndexClient::Updater::MisMatchedChecksumError)
+ end
+ end
+
+ context "when the download is corrupt" do
+ let(:response) { double(:response, :body => "") }
+
+ it "raises HTTPError" do
+ expect(response).to receive(:[]).with("Content-Encoding") { "gzip" }
+ expect(fetcher).to receive(:call) { response }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::HTTPError)
+ end
+ end
+
+ context "when bundler doesn't have permissions on Dir.tmpdir" do
+ let(:response) { double(:response, :body => "") }
+
+ it "Errno::EACCES is raised" do
+ allow(Dir).to receive(:mktmpdir) { raise Errno::EACCES }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::PermissionError)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
new file mode 100644
index 0000000000..2ed87ec81d
--- /dev/null
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -0,0 +1,358 @@
+# frozen_string_literal: true
+
+require "bundler/definition"
+
+RSpec.describe Bundler::Definition do
+ describe "#lock" do
+ before do
+ allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
+ allow(Bundler).to receive(:ui) { double("UI", :info => "", :debug => "") }
+ end
+ context "when it's not possible to write to the file" do
+ subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+
+ it "raises an PermissionError with explanation" do
+ expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ and_raise(Errno::EACCES)
+ expect { subject.lock("Gemfile.lock") }.
+ to raise_error(Bundler::PermissionError, /Gemfile\.lock/)
+ end
+ end
+ context "when a temporary resource access issue occurs" do
+ subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+
+ it "raises a TemporaryResourceError with explanation" do
+ expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ and_raise(Errno::EAGAIN)
+ expect { subject.lock("Gemfile.lock") }.
+ to raise_error(Bundler::TemporaryResourceError, /temporarily unavailable/)
+ end
+ end
+ end
+
+ describe "detects changes" do
+ it "for a path gem with changes", :bundler => "< 2" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ bundle :install, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/re-resolving dependencies/)
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with changes", :bundler => "2" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ bundle :install, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/re-resolving dependencies/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with deps and no changes", :bundler => "< 2" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ s.add_development_dependency "net-ssh", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with deps and no changes", :bundler => "2" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ s.add_development_dependency "net-ssh", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a rubygems gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+ end
+
+ describe "initialize" do
+ context "gem version promoter" do
+ context "with lockfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+ end
+
+ it "should get a locked specs list when updating all" do
+ definition = Bundler::Definition.new(bundled_app("Gemfile.lock"), [], Bundler::SourceList.new, true)
+ locked_specs = definition.gem_version_promoter.locked_specs
+ expect(locked_specs.to_a.map(&:name)).to eq ["foo"]
+ expect(definition.instance_variable_get("@locked_specs").empty?).to eq true
+ end
+ end
+
+ context "without gemfile or lockfile" do
+ it "should not attempt to parse empty lockfile contents" do
+ definition = Bundler::Definition.new(nil, [], mock_source_list, true)
+ expect(definition.gem_version_promoter.locked_specs.to_a).to eq []
+ end
+ end
+
+ context "eager unlock" do
+ let(:source_list) do
+ Bundler::SourceList.new.tap do |source_list|
+ source_list.global_rubygems_source = "file://#{gem_repo4}"
+ end
+ end
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
+
+ gem 'shared_owner_a'
+ gem 'shared_owner_b'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.1)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.1)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.1)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ shared_owner_a
+ shared_owner_b
+ isolated_owner
+
+ BUNDLED WITH
+ 1.13.0
+ L
+ end
+
+ it "should not eagerly unlock shared dependency with bundle install conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", "3.0.2"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ unlock_hash_for_bundle_install = {}
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ source_list,
+ unlock_hash_for_bundle_install
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked).to include "shared_dep"
+ end
+
+ it "should not eagerly unlock shared dependency with bundle update conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", ">= 0"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ source_list,
+ :gems => ["shared_owner_a"], :lock_shared_dependencies => true
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked).to eq %w[isolated_dep isolated_owner shared_dep shared_owner_b]
+ expect(locked.include?("shared_dep")).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe "find_resolved_spec" do
+ it "with no platform set in SpecSet" do
+ ss = Bundler::SpecSet.new([build_stub_spec("a", "1.0"), build_stub_spec("b", "1.0")])
+ dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
+ dfn.instance_variable_set("@specs", ss)
+ found = dfn.find_resolved_spec(build_spec("a", "0.9", "ruby").first)
+ expect(found.name).to eq "a"
+ expect(found.version.to_s).to eq "1.0"
+ end
+ end
+
+ describe "find_indexed_specs" do
+ it "with no platform set in indexed specs" do
+ index = Bundler::Index.new
+ %w[1.0.0 1.0.1 1.1.0].each {|v| index << build_stub_spec("foo", v) }
+
+ dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
+ dfn.instance_variable_set("@index", index)
+ found = dfn.find_indexed_specs(build_spec("foo", "0.9", "ruby").first)
+ expect(found.length).to eq 3
+ end
+ end
+
+ def build_stub_spec(name, version)
+ Bundler::StubSpecification.new(name, version, nil, nil)
+ end
+
+ def mock_source_list
+ Class.new do
+ def all_sources
+ []
+ end
+
+ def path_sources
+ []
+ end
+
+ def rubygems_remotes
+ []
+ end
+
+ def replace_sources!(arg)
+ nil
+ end
+ end.new
+ end
+end
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
new file mode 100644
index 0000000000..0f8d6b1076
--- /dev/null
+++ b/spec/bundler/bundler/dep_proxy_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::DepProxy do
+ let(:dep) { Bundler::Dependency.new("rake", ">= 0") }
+ subject { described_class.new(dep, Gem::Platform::RUBY) }
+ let(:same) { subject }
+ let(:other) { subject.dup }
+ let(:different) { described_class.new(dep, Gem::Platform::JAVA) }
+
+ describe "#eql?" do
+ it { expect(subject.eql?(same)).to be true }
+ it { expect(subject.eql?(other)).to be true }
+ it { expect(subject.eql?(different)).to be false }
+ it { expect(subject.eql?(nil)).to be false }
+ it { expect(subject.eql?("foobar")).to be false }
+ end
+
+ describe "#hash" do
+ it { expect(subject.hash).to eq(same.hash) }
+ it { expect(subject.hash).to eq(other.hash) }
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
new file mode 100644
index 0000000000..bffe4f1608
--- /dev/null
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -0,0 +1,289 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Dsl do
+ before do
+ @rubygems = double("rubygems")
+ allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
+ end
+
+ describe "#git_source" do
+ it "registers custom hosts" do
+ subject.git_source(:example) {|repo_name| "git@git.example.com:#{repo_name}.git" }
+ subject.git_source(:foobar) {|repo_name| "git@foobar.com:#{repo_name}.git" }
+ subject.gem("dobry-pies", :example => "strzalek/dobry-pies")
+ example_uri = "git@git.example.com:strzalek/dobry-pies.git"
+ expect(subject.dependencies.first.source.uri).to eq(example_uri)
+ end
+
+ it "raises exception on invalid hostname" do
+ expect do
+ subject.git_source(:group) {|repo_name| "git@git.example.com:#{repo_name}.git" }
+ end.to raise_error(Bundler::InvalidOption)
+ end
+
+ it "expects block passed" do
+ expect { subject.git_source(:example) }.to raise_error(Bundler::InvalidOption)
+ end
+
+ context "default hosts (git, gist)", :bundler => "< 2" do
+ it "converts :github to :git" do
+ subject.gem("sparks", :github => "indirect/sparks")
+ github_uri = "git://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts numeric :gist to :git" do
+ subject.gem("not-really-a-gem", :gist => 2_859_988)
+ github_uri = "https://gist.github.com/2859988.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts :gist to :git" do
+ subject.gem("not-really-a-gem", :gist => "2859988")
+ github_uri = "https://gist.github.com/2859988.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts 'rails' to 'rails/rails'" do
+ subject.gem("rails", :github => "rails")
+ github_uri = "git://github.com/rails/rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts :bitbucket to :git" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/flatlab-rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
+
+ it "converts 'mcorp' to 'mcorp/mcorp'" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/mcorp.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
+ end
+
+ context "default git sources", :bundler => "2" do
+ it "has none" do
+ expect(subject.instance_variable_get(:@git_sources)).to eq({})
+ end
+ end
+ end
+
+ describe "#method_missing" do
+ it "raises an error for unknown DSL methods" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).
+ and_return("unknown")
+
+ error_msg = "There was an error parsing `Gemfile`: Undefined local variable or method `unknown' for Gemfile. Bundler cannot continue."
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, Regexp.new(error_msg))
+ end
+ end
+
+ describe "#eval_gemfile" do
+ it "handles syntax errors with a useful message" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).and_return("}")
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '\}'). Bundler cannot continue./)
+ end
+
+ it "distinguishes syntax errors from evaluation errors" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).and_return(
+ "ruby '2.1.5', :engine => 'ruby', :engine_version => '1.2.4'"
+ )
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, /There was an error evaluating `Gemfile`: ruby_version must match the :engine_version for MRI/)
+ end
+ end
+
+ describe "#gem" do
+ [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :mri, :mri_18, :mri_19,
+ :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :jruby, :rbx, :truffleruby].each do |platform|
+ it "allows #{platform} as a valid platform" do
+ subject.gem("foo", :platform => platform)
+ end
+ end
+
+ it "rejects invalid platforms" do
+ expect { subject.gem("foo", :platform => :bogus) }.
+ to raise_error(Bundler::GemfileError, /is not a valid platform/)
+ end
+
+ it "rejects empty gem name" do
+ expect { subject.gem("") }.
+ to raise_error(Bundler::GemfileError, /an empty gem name is not valid/)
+ end
+
+ it "rejects with a leading space in the name" do
+ expect { subject.gem(" foo") }.
+ to raise_error(Bundler::GemfileError, /' foo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a trailing space in the name" do
+ expect { subject.gem("foo ") }.
+ to raise_error(Bundler::GemfileError, /'foo ' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a space in the gem name" do
+ expect { subject.gem("fo o") }.
+ to raise_error(Bundler::GemfileError, /'fo o' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a tab in the gem name" do
+ expect { subject.gem("fo\to") }.
+ to raise_error(Bundler::GemfileError, /'fo\to' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a newline in the gem name" do
+ expect { subject.gem("fo\no") }.
+ to raise_error(Bundler::GemfileError, /'fo\no' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a carriage return in the gem name" do
+ expect { subject.gem("fo\ro") }.
+ to raise_error(Bundler::GemfileError, /'fo\ro' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a form feed in the gem name" do
+ expect { subject.gem("fo\fo") }.
+ to raise_error(Bundler::GemfileError, /'fo\fo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects symbols as gem name" do
+ expect { subject.gem(:foo) }.
+ to raise_error(Bundler::GemfileError, /You need to specify gem names as Strings. Use 'gem "foo"' instead/)
+ end
+
+ it "rejects branch option on non-git gems" do
+ expect { subject.gem("foo", :branch => "test") }.
+ to raise_error(Bundler::GemfileError, /The `branch` option for `gem 'foo'` is not allowed. Only gems with a git source can specify a branch/)
+ end
+
+ it "allows specifying a branch on git gems" do
+ subject.gem("foo", :branch => "test", :git => "http://mytestrepo")
+ dep = subject.dependencies.last
+ expect(dep.name).to eq "foo"
+ end
+
+ it "allows specifying a branch on git gems with a git_source" do
+ subject.git_source(:test_source) {|n| "https://github.com/#{n}" }
+ subject.gem("foo", :branch => "test", :test_source => "bundler/bundler")
+ dep = subject.dependencies.last
+ expect(dep.name).to eq "foo"
+ end
+ end
+
+ describe "#gemspec" do
+ let(:spec) do
+ Gem::Specification.new do |gem|
+ gem.name = "example"
+ gem.platform = platform
+ end
+ end
+
+ before do
+ allow(Dir).to receive(:[]).and_return(["spec_path"])
+ allow(Bundler).to receive(:load_gemspec).with("spec_path").and_return(spec)
+ allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
+ end
+
+ context "with a ruby platform" do
+ let(:platform) { "ruby" }
+
+ it "keeps track of the ruby platforms in the dependency" do
+ subject.gemspec
+ expect(subject.dependencies.last.platforms).to eq(Bundler::Dependency::REVERSE_PLATFORM_MAP[Gem::Platform::RUBY])
+ end
+ end
+
+ context "with a jruby platform" do
+ let(:platform) { "java" }
+
+ it "keeps track of the jruby platforms in the dependency" do
+ allow(Gem::Platform).to receive(:local).and_return(java)
+ subject.gemspec
+ expect(subject.dependencies.last.platforms).to eq(Bundler::Dependency::REVERSE_PLATFORM_MAP[Gem::Platform::JAVA])
+ end
+ end
+ end
+
+ context "can bundle groups of gems with" do
+ # git "https://github.com/rails/rails.git" do
+ # gem "railties"
+ # gem "action_pack"
+ # gem "active_model"
+ # end
+ describe "#git" do
+ it "from a single repo" do
+ rails_gems = %w[railties action_pack active_model]
+ subject.git "https://github.com/rails/rails.git" do
+ rails_gems.each {|rails_gem| subject.send :gem, rails_gem }
+ end
+ expect(subject.dependencies.map(&:name)).to match_array rails_gems
+ end
+ end
+
+ # github 'spree' do
+ # gem 'spree_core'
+ # gem 'spree_api'
+ # gem 'spree_backend'
+ # end
+ describe "#github", :bundler => "< 2" do
+ it "from github" do
+ spree_gems = %w[spree_core spree_api spree_backend]
+ subject.github "spree" do
+ spree_gems.each {|spree_gem| subject.send :gem, spree_gem }
+ end
+
+ subject.dependencies.each do |d|
+ expect(d.source.uri).to eq("git://github.com/spree/spree.git")
+ end
+ end
+ end
+
+ describe "#github", :bundler => "2" do
+ it "from github" do
+ expect do
+ spree_gems = %w[spree_core spree_api spree_backend]
+ subject.github "spree" do
+ spree_gems.each {|spree_gem| subject.send :gem, spree_gem }
+ end
+ end.to raise_error(Bundler::DeprecatedError, /github method has been removed/)
+ end
+ end
+ end
+
+ describe "syntax errors" do
+ it "will raise a Bundler::GemfileError" do
+ gemfile "gem 'foo', :path => /unquoted/string/syntax/error"
+ expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)? unknown regexp options - trg. Bundler cannot continue./)
+ end
+ end
+
+ describe "Runtime errors", :unless => Bundler.current_ruby.on_18? do
+ it "will raise a Bundler::GemfileError" do
+ gemfile "s = 'foo'.freeze; s.strip!"
+ expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: can't modify frozen String. Bundler cannot continue./i)
+ end
+ end
+
+ describe "#with_source" do
+ context "if there was a rubygem source already defined" do
+ it "restores it after it's done" do
+ other_source = double("other-source")
+ allow(Bundler::Source::Rubygems).to receive(:new).and_return(other_source)
+ allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
+
+ subject.source("https://other-source.org") do
+ subject.gem("dobry-pies", :path => "foo")
+ subject.gem("foo")
+ end
+
+ expect(subject.dependencies.last.source).to eq(other_source)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
new file mode 100644
index 0000000000..a9371f6617
--- /dev/null
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::EndpointSpecification do
+ let(:name) { "foo" }
+ let(:version) { "1.0.0" }
+ let(:platform) { Gem::Platform::RUBY }
+ let(:dependencies) { [] }
+ let(:metadata) { nil }
+
+ subject(:spec) { described_class.new(name, version, platform, dependencies, metadata) }
+
+ describe "#build_dependency" do
+ let(:name) { "foo" }
+ let(:requirement1) { "~> 1.1" }
+ let(:requirement2) { ">= 1.1.7" }
+
+ it "should return a Gem::Dependency" do
+ expect(subject.send(:build_dependency, name, [requirement1, requirement2])).
+ to eq(Gem::Dependency.new(name, requirement1, requirement2))
+ end
+
+ context "when an ArgumentError occurs" do
+ before do
+ allow(Gem::Dependency).to receive(:new).with(name, [requirement1, requirement2]) {
+ raise ArgumentError.new("Some error occurred")
+ }
+ end
+
+ it "should raise the original error" do
+ expect { subject.send(:build_dependency, name, [requirement1, requirement2]) }.to raise_error(
+ ArgumentError, "Some error occurred"
+ )
+ end
+ end
+
+ context "when there is an ill formed requirement" do
+ before do
+ allow(Gem::Dependency).to receive(:new).with(name, [requirement1, requirement2]) {
+ raise ArgumentError.new("Ill-formed requirement [\"#<YAML::Syck::DefaultKey")
+ }
+ # Eliminate extra line break in rspec output due to `puts` in `#build_dependency`
+ allow(subject).to receive(:puts) {}
+ end
+
+ it "should raise a Bundler::GemspecError with invalid gemspec message" do
+ expect { subject.send(:build_dependency, name, [requirement1, requirement2]) }.to raise_error(
+ Bundler::GemspecError, /Unfortunately, the gem foo \(1\.0\.0\) has an invalid gemspec/
+ )
+ end
+ end
+ end
+
+ describe "#parse_metadata" do
+ context "when the metadata has malformed requirements" do
+ let(:metadata) { { "rubygems" => ">\n" } }
+ it "raises a helpful error message" do
+ expect { subject }.to raise_error(
+ Bundler::GemspecError,
+ a_string_including("There was an error parsing the metadata for the gem foo (1.0.0)").
+ and(a_string_including('The metadata was {"rubygems"=>">\n"}'))
+ )
+ end
+ end
+ end
+
+ it "supports equality comparison" do
+ other_spec = described_class.new("bar", version, platform, dependencies, metadata)
+ expect(spec).to eql(spec)
+ expect(spec).to_not eql(other_spec)
+ end
+end
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
new file mode 100644
index 0000000000..20bd38b021
--- /dev/null
+++ b/spec/bundler/bundler/env_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+require "bundler/settings"
+
+RSpec.describe Bundler::Env do
+ let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
+
+ describe "#report" do
+ it "prints the environment" do
+ out = described_class.report
+
+ expect(out).to include("Environment")
+ expect(out).to include(Bundler::VERSION)
+ expect(out).to include(Gem::VERSION)
+ expect(out).to include(described_class.send(:ruby_version))
+ expect(out).to include(described_class.send(:git_version))
+ expect(out).to include(OpenSSL::OPENSSL_VERSION)
+ end
+
+ context "when there is a Gemfile and a lockfile and print_gemfile is true" do
+ before do
+ gemfile "gem 'rack', '1.0.0'"
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+ end
+
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the Gemfile" do
+ expect(output).to include("Gemfile")
+ expect(output).to include("'rack', '1.0.0'")
+ end
+
+ it "prints the lockfile" do
+ expect(output).to include("Gemfile.lock")
+ expect(output).to include("rack (1.0.0)")
+ end
+ end
+
+ context "when there no Gemfile and print_gemfile is true" do
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the environment" do
+ expect(output).to start_with("## Environment")
+ end
+ end
+
+ context "when Gemfile contains a gemspec and print_gemspecs is true" do
+ let(:gemspec) do
+ strip_whitespace(<<-GEMSPEC)
+ Gem::Specification.new do |gem|
+ gem.name = "foo"
+ gem.author = "Fumofu"
+ end
+ GEMSPEC
+ end
+
+ before do
+ gemfile("gemspec")
+
+ File.open(bundled_app.join("foo.gemspec"), "wb") do |f|
+ f.write(gemspec)
+ end
+ end
+
+ it "prints the gemspec" do
+ output = described_class.report(:print_gemspecs => true)
+
+ expect(output).to include("foo.gemspec")
+ expect(output).to include(gemspec)
+ end
+ end
+
+ context "when eval_gemfile is used" do
+ it "prints all gemfiles" do
+ create_file "other/Gemfile-other", "gem 'rack'"
+ create_file "other/Gemfile", "eval_gemfile 'Gemfile-other'"
+ create_file "Gemfile-alt", <<-G
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ G
+ gemfile "eval_gemfile #{File.expand_path("Gemfile-alt").dump}"
+
+ output = described_class.report(:print_gemspecs => true)
+ expect(output).to include(strip_whitespace(<<-ENV))
+ ## Gemfile
+
+ ### Gemfile
+
+ ```ruby
+ eval_gemfile #{File.expand_path("Gemfile-alt").dump}
+ ```
+
+ ### Gemfile-alt
+
+ ```ruby
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ ```
+
+ ### other/Gemfile
+
+ ```ruby
+ eval_gemfile 'Gemfile-other'
+ ```
+
+ ### other/Gemfile-other
+
+ ```ruby
+ gem 'rack'
+ ```
+
+ ### Gemfile.lock
+
+ ```
+ <No #{bundled_app("Gemfile.lock")} found>
+ ```
+ ENV
+ end
+ end
+
+ context "when the git version is OS specific" do
+ it "includes OS specific information with the version number" do
+ expect(git_proxy_stub).to receive(:git).with("--version").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
+
+ expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
+ end
+ end
+ end
+
+ describe ".version_of", :ruby_repo do
+ let(:parsed_version) { described_class.send(:version_of, "ruby") }
+
+ it "strips version of new line characters" do
+ expect(parsed_version).to_not include("\n")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/environment_preserver_spec.rb b/spec/bundler/bundler/environment_preserver_spec.rb
new file mode 100644
index 0000000000..530ca6f835
--- /dev/null
+++ b/spec/bundler/bundler/environment_preserver_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::EnvironmentPreserver do
+ let(:preserver) { described_class.new(env, ["foo"]) }
+
+ describe "#backup" do
+ let(:env) { { "foo" => "my-foo", "bar" => "my-bar" } }
+ subject { preserver.backup }
+
+ it "should create backup entries" do
+ expect(subject["BUNDLER_ORIG_foo"]).to eq("my-foo")
+ end
+
+ it "should keep the original entry" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+
+ it "should not create backup entries for unspecified keys" do
+ expect(subject.key?("BUNDLER_ORIG_bar")).to eq(false)
+ end
+
+ it "should not affect the original env" do
+ subject
+ expect(env.keys.sort).to eq(%w[bar foo])
+ end
+
+ context "when a key is empty" do
+ let(:env) { { "foo" => "" } }
+
+ it "should not create backup entries" do
+ expect(subject).not_to have_key "BUNDLER_ORIG_foo"
+ end
+ end
+
+ context "when an original key is set" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "orig-foo" } }
+
+ it "should keep the original value in the BUNDLER_ORIG_ variable" do
+ expect(subject["BUNDLER_ORIG_foo"]).to eq("orig-foo")
+ end
+
+ it "should keep the variable" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+ end
+
+ describe "#restore" do
+ subject { preserver.restore }
+
+ context "when an original key is set" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "orig-foo" } }
+
+ it "should restore the original value" do
+ expect(subject["foo"]).to eq("orig-foo")
+ end
+
+ it "should delete the backup value" do
+ expect(subject.key?("BUNDLER_ORIG_foo")).to eq(false)
+ end
+ end
+
+ context "when no original key is set" do
+ let(:env) { { "foo" => "my-foo" } }
+
+ it "should keep the current value" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+
+ context "when the original key is empty" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "" } }
+
+ it "should keep the current value" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/base_spec.rb b/spec/bundler/bundler/fetcher/base_spec.rb
new file mode 100644
index 0000000000..df1245d44d
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/base_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Base do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote) }
+ let(:display_uri) { "http://sample_uri.com" }
+
+ class TestClass < described_class; end
+
+ subject { TestClass.new(downloader, remote, display_uri) }
+
+ describe "#initialize" do
+ context "with the abstract Base class" do
+ it "should raise an error" do
+ expect { described_class.new(downloader, remote, display_uri) }.to raise_error(RuntimeError, "Abstract class")
+ end
+ end
+
+ context "with a class that inherits the Base class" do
+ it "should set the passed attributes" do
+ expect(subject.downloader).to eq(downloader)
+ expect(subject.remote).to eq(remote)
+ expect(subject.display_uri).to eq("http://sample_uri.com")
+ end
+ end
+ end
+
+ describe "#remote_uri" do
+ let(:remote_uri_obj) { double(:remote_uri_obj) }
+
+ before { allow(remote).to receive(:uri).and_return(remote_uri_obj) }
+
+ it "should return the remote's uri" do
+ expect(subject.remote_uri).to eq(remote_uri_obj)
+ end
+ end
+
+ describe "#fetch_uri" do
+ let(:remote_uri_obj) { URI("http://rubygems.org") }
+
+ before { allow(subject).to receive(:remote_uri).and_return(remote_uri_obj) }
+
+ context "when the remote uri's host is rubygems.org" do
+ it "should create a copy of the remote uri with index.rubygems.org as the host" do
+ fetched_uri = subject.fetch_uri
+ expect(fetched_uri.host).to eq("index.rubygems.org")
+ expect(fetched_uri).to_not be(remote_uri_obj)
+ end
+ end
+
+ context "when the remote uri's host is not rubygems.org" do
+ let(:remote_uri_obj) { URI("http://otherhost.org") }
+
+ it "should return the remote uri" do
+ expect(subject.fetch_uri).to eq(URI("http://otherhost.org"))
+ end
+ end
+
+ it "memoizes the fetched uri" do
+ expect(remote_uri_obj).to receive(:host).once
+ 2.times { subject.fetch_uri }
+ end
+ end
+
+ describe "#available?" do
+ it "should return whether the api is available" do
+ expect(subject.available?).to be_truthy
+ end
+ end
+
+ describe "#api_fetcher?" do
+ it "should return false" do
+ expect(subject.api_fetcher?).to be_falsey
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/compact_index_spec.rb b/spec/bundler/bundler/fetcher/compact_index_spec.rb
new file mode 100644
index 0000000000..e0f58766ea
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/compact_index_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::CompactIndex do
+ let(:downloader) { double(:downloader) }
+ let(:display_uri) { URI("http://sampleuri.com") }
+ let(:remote) { double(:remote, :cache_slug => "lsjdf", :uri => display_uri) }
+ let(:compact_index) { described_class.new(downloader, remote, display_uri) }
+
+ before do
+ allow(compact_index).to receive(:log_specs) {}
+ end
+
+ describe "#specs_for_names" do
+ it "has only one thread open at the end of the run" do
+ compact_index.specs_for_names(["lskdjf"])
+
+ thread_count = Thread.list.count {|thread| thread.status == "run" }
+ expect(thread_count).to eq 1
+ end
+
+ it "calls worker#stop during the run" do
+ expect_any_instance_of(Bundler::Worker).to receive(:stop).at_least(:once)
+
+ compact_index.specs_for_names(["lskdjf"])
+ end
+
+ describe "#available?" do
+ before do
+ allow(compact_index).to receive(:compact_index_client).
+ and_return(double(:compact_index_client, :update_and_parse_checksums! => true))
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+
+ context "when OpenSSL is not available" do
+ before do
+ allow(compact_index).to receive(:require).with("openssl").and_raise(LoadError)
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+ end
+
+ context "when OpenSSL is FIPS-enabled", :ruby => ">= 2.0.0" do
+ def remove_cached_md5_availability
+ return unless Bundler::SharedHelpers.instance_variable_defined?(:@md5_available)
+ Bundler::SharedHelpers.remove_instance_variable(:@md5_available)
+ end
+
+ before do
+ remove_cached_md5_availability
+ stub_const("OpenSSL::OPENSSL_FIPS", true)
+ end
+
+ after { remove_cached_md5_availability }
+
+ context "when FIPS-mode is active" do
+ before do
+ allow(OpenSSL::Digest::MD5).to receive(:digest).
+ and_raise(OpenSSL::Digest::DigestError)
+ end
+
+ it "returns false" do
+ expect(compact_index).to_not be_available
+ end
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+ end
+ end
+
+ context "logging" do
+ before { allow(compact_index).to receive(:log_specs).and_call_original }
+
+ context "with debug on" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(true)
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with('Looking up gems ["lskdjf"]')
+ compact_index.specs_for_names(["lskdjf"])
+ end
+ end
+
+ context "with debug off" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false)
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ compact_index.specs_for_names(["lskdjf"])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
new file mode 100644
index 0000000000..081fdff34d
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -0,0 +1,287 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Dependency do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, :uri => URI("http://localhost:5000")) }
+ let(:display_uri) { "http://sample_uri.com" }
+
+ subject { described_class.new(downloader, remote, display_uri) }
+
+ describe "#available?" do
+ let(:dependency_api_uri) { double(:dependency_api_uri) }
+ let(:fetched_spec) { double(:fetched_spec) }
+
+ before do
+ allow(subject).to receive(:dependency_api_uri).and_return(dependency_api_uri)
+ allow(downloader).to receive(:fetch).with(dependency_api_uri).and_return(fetched_spec)
+ end
+
+ it "should be truthy" do
+ expect(subject.available?).to be_truthy
+ end
+
+ context "when there is no network access" do
+ before do
+ allow(downloader).to receive(:fetch).with(dependency_api_uri) {
+ raise Bundler::Fetcher::NetworkDownError.new("Network Down Message")
+ }
+ end
+
+ it "should raise an HTTPError with the original message" do
+ expect { subject.available? }.to raise_error(Bundler::HTTPError, "Network Down Message")
+ end
+ end
+
+ context "when authentication is required" do
+ let(:remote_uri) { "http://remote_uri.org" }
+
+ before do
+ allow(downloader).to receive(:fetch).with(dependency_api_uri) {
+ raise Bundler::Fetcher::AuthenticationRequiredError.new(remote_uri)
+ }
+ end
+
+ it "should raise the original error" do
+ expect { subject.available? }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ %r{Authentication is required for http://remote_uri.org})
+ end
+ end
+
+ context "when there is an http error" do
+ before { allow(downloader).to receive(:fetch).with(dependency_api_uri) { raise Bundler::HTTPError.new } }
+
+ it "should be falsey" do
+ expect(subject.available?).to be_falsey
+ end
+ end
+ end
+
+ describe "#api_fetcher?" do
+ it "should return true" do
+ expect(subject.api_fetcher?).to be_truthy
+ end
+ end
+
+ describe "#specs" do
+ let(:gem_names) { %w[foo bar] }
+ let(:full_dependency_list) { ["bar"] }
+ let(:last_spec_list) { [["boulder", gem_version1, "ruby", resque]] }
+ let(:fail_errors) { double(:fail_errors) }
+ let(:bundler_retry) { double(:bundler_retry) }
+ let(:gem_version1) { double(:gem_version1) }
+ let(:resque) { double(:resque) }
+ let(:remote_uri) { "http://remote-uri.org" }
+
+ before do
+ stub_const("Bundler::Fetcher::FAIL_ERRORS", fail_errors)
+ allow(Bundler::Retry).to receive(:new).with("dependency api", fail_errors).and_return(bundler_retry)
+ allow(bundler_retry).to receive(:attempts) {|&block| block.call }
+ allow(subject).to receive(:log_specs) {}
+ allow(subject).to receive(:remote_uri).and_return(remote_uri)
+ allow(Bundler).to receive_message_chain(:ui, :debug?)
+ allow(Bundler).to receive_message_chain(:ui, :info)
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ end
+
+ context "when there are given gem names that are not in the full dependency list" do
+ let(:spec_list) { [["top", gem_version2, "ruby", faraday]] }
+ let(:deps_list) { [] }
+ let(:dependency_specs) { [spec_list, deps_list] }
+ let(:gem_version2) { double(:gem_version2) }
+ let(:faraday) { double(:faraday) }
+
+ before { allow(subject).to receive(:dependency_specs).with(["foo"]).and_return(dependency_specs) }
+
+ it "should return a hash with the remote_uri and the list of specs" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to eq([
+ ["top", gem_version2, "ruby", faraday],
+ ["boulder", gem_version1, "ruby", resque],
+ ])
+ end
+ end
+
+ context "when all given gem names are in the full dependency list" do
+ let(:gem_names) { ["foo"] }
+ let(:full_dependency_list) { %w[foo bar] }
+ let(:last_spec_list) { ["boulder"] }
+
+ it "should return a hash with the remote_uri and the last spec list" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to eq(["boulder"])
+ end
+ end
+
+ context "logging" do
+ before { allow(subject).to receive(:log_specs).and_call_original }
+
+ context "with debug on" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(true)
+ allow(subject).to receive(:dependency_specs).with(["foo"]).and_return([[], []])
+ end
+
+ it "should log the query list at debug level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("Query List: [\"foo\"]")
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("Query List: []")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+
+ context "with debug off" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false)
+ allow(subject).to receive(:dependency_specs).with(["foo"]).and_return([[], []])
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+ end
+
+ shared_examples_for "the error is properly handled" do
+ it "should return nil" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to be_nil
+ end
+
+ context "debug logging is not on" do
+ before { allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false) }
+
+ it "should log a new line to info" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with("")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+ end
+
+ shared_examples_for "the error suggests retrying with the full index" do
+ it "should log the inability to fetch from API at debug level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+
+ context "when an HTTPError occurs" do
+ before { allow(subject).to receive(:dependency_specs) { raise Bundler::HTTPError.new } }
+
+ it_behaves_like "the error is properly handled"
+ it_behaves_like "the error suggests retrying with the full index"
+ end
+
+ context "when a GemspecError occurs" do
+ before { allow(subject).to receive(:dependency_specs) { raise Bundler::GemspecError.new } }
+
+ it_behaves_like "the error is properly handled"
+ it_behaves_like "the error suggests retrying with the full index"
+ end
+
+ context "when a MarshalError occurs" do
+ before { allow(subject).to receive(:dependency_specs) { raise Bundler::MarshalError.new } }
+
+ it_behaves_like "the error is properly handled"
+
+ it "should log the inability to fetch from API and mention retrying" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+ end
+
+ describe "#dependency_specs" do
+ let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
+ let(:gem_list) { double(:gem_list) }
+ let(:formatted_specs_and_deps) { double(:formatted_specs_and_deps) }
+
+ before do
+ allow(subject).to receive(:unmarshalled_dep_gems).with(gem_names).and_return(gem_list)
+ allow(subject).to receive(:get_formatted_specs_and_deps).with(gem_list).and_return(formatted_specs_and_deps)
+ end
+
+ it "should log the query list at debug level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with(
+ "Query Gemcutter Dependency Endpoint API: foo,bar,bundler,rubocop"
+ )
+ subject.dependency_specs(gem_names)
+ end
+
+ it "should return formatted specs and a unique list of dependencies" do
+ expect(subject.dependency_specs(gem_names)).to eq(formatted_specs_and_deps)
+ end
+ end
+
+ describe "#unmarshalled_dep_gems" do
+ let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
+ let(:dep_api_uri) { double(:dep_api_uri) }
+ let(:unmarshalled_gems) { double(:unmarshalled_gems) }
+ let(:fetch_response) { double(:fetch_response, :body => double(:body)) }
+ let(:rubygems_limit) { 50 }
+
+ before { allow(subject).to receive(:dependency_api_uri).with(gem_names).and_return(dep_api_uri) }
+
+ it "should fetch dependencies from RubyGems and unmarshal them" do
+ expect(gem_names).to receive(:each_slice).with(rubygems_limit).and_call_original
+ expect(downloader).to receive(:fetch).with(dep_api_uri).and_return(fetch_response)
+ expect(Bundler).to receive(:load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
+ expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
+ end
+ end
+
+ describe "#get_formatted_specs_and_deps" do
+ let(:gem_list) do
+ [
+ {
+ :dependencies => {
+ "resque" => "req3,req4",
+ },
+ :name => "typhoeus",
+ :number => "1.0.1",
+ :platform => "ruby",
+ },
+ {
+ :dependencies => {
+ "faraday" => "req1,req2",
+ },
+ :name => "grape",
+ :number => "2.0.2",
+ :platform => "jruby",
+ },
+ ]
+ end
+
+ it "should return formatted specs and a unique list of dependencies" do
+ spec_list, deps_list = subject.get_formatted_specs_and_deps(gem_list)
+ expect(spec_list).to eq([["typhoeus", "1.0.1", "ruby", [["resque", ["req3,req4"]]]],
+ ["grape", "2.0.2", "jruby", [["faraday", ["req1,req2"]]]]])
+ expect(deps_list).to eq(%w[resque faraday])
+ end
+ end
+
+ describe "#dependency_api_uri" do
+ let(:uri) { URI("http://gem-api.com") }
+
+ context "with gem names" do
+ let(:gem_names) { %w[foo bar bundler rubocop] }
+
+ before { allow(subject).to receive(:fetch_uri).and_return(uri) }
+
+ it "should return an api calling uri with the gems in the query" do
+ expect(subject.dependency_api_uri(gem_names).to_s).to eq(
+ "http://gem-api.com/api/v1/dependencies?gems=bar%2Cbundler%2Cfoo%2Crubocop"
+ )
+ end
+ end
+
+ context "with no gem names" do
+ let(:gem_names) { [] }
+
+ before { allow(subject).to receive(:fetch_uri).and_return(uri) }
+
+ it "should return an api calling uri with no query" do
+ expect(subject.dependency_api_uri(gem_names).to_s).to eq(
+ "http://gem-api.com/api/v1/dependencies"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
new file mode 100644
index 0000000000..c9b4fa662a
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -0,0 +1,250 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Downloader do
+ let(:connection) { double(:connection) }
+ let(:redirect_limit) { 5 }
+ let(:uri) { URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:options) { double(:options) }
+
+ subject { described_class.new(connection, redirect_limit) }
+
+ describe "fetch" do
+ let(:counter) { 0 }
+ let(:httpv) { "1.1" }
+ let(:http_response) { double(:response) }
+
+ before do
+ allow(subject).to receive(:request).with(uri, options).and_return(http_response)
+ allow(http_response).to receive(:body).and_return("Body with info")
+ end
+
+ context "when the # requests counter is greater than the redirect limit" do
+ let(:counter) { redirect_limit + 1 }
+
+ it "should raise a Bundler::HTTPError specifying too many redirects" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Too many redirects")
+ end
+ end
+
+ context "logging" do
+ let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+
+ it "should log the HTTP response code and message to debug" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP 200 Success #{uri}")
+ subject.fetch(uri, options, counter)
+ end
+ end
+
+ context "when the request response is a Net::HTTPRedirection" do
+ let(:http_response) { Net::HTTPRedirection.new(httpv, 308, "Moved") }
+
+ before { http_response["location"] = "http://www.redirect-uri.com/api/v2/endpoint" }
+
+ it "should try to fetch the redirect uri and iterate the # requests counter" do
+ expect(subject).to receive(:fetch).with(URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
+ subject.fetch(uri, options, counter)
+ end
+
+ context "when the redirect uri and original uri are the same" do
+ let(:uri) { URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ before { http_response["location"] = "ssh://www.uri-to-fetch.com/api/v1/endpoint" }
+
+ it "should set the same user and password for the redirect uri" do
+ expect(subject).to receive(:fetch).with(URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
+ subject.fetch(uri, options, counter)
+ end
+ end
+ end
+
+ context "when the request response is a Net::HTTPSuccess" do
+ let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+
+ it "should return the response body" do
+ expect(subject.fetch(uri, options, counter)).to eq(http_response)
+ end
+ end
+
+ context "when the request response is a Net::HTTPRequestEntityTooLarge" do
+ let(:http_response) { Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
+
+ it "should raise a Bundler::Fetcher::FallbackError with the response body" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Body with info")
+ end
+ end
+
+ context "when the request response is a Net::HTTPUnauthorized" do
+ let(:http_response) { Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
+
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ /Authentication is required for www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when the request response is a Net::HTTPNotFound" do
+ let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }
+
+ it "should raise a Bundler::Fetcher::FallbackError with Net::HTTPNotFound" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound")
+ end
+ end
+
+ context "when the request response is some other type" do
+ let(:http_response) { Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
+
+ it "should raise a Bundler::HTTPError with the response class and body" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Net::HTTPBadGateway: Body with info")
+ end
+ end
+ end
+
+ describe "request" do
+ let(:net_http_get) { double(:net_http_get) }
+ let(:response) { double(:response) }
+
+ before do
+ allow(Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
+ allow(connection).to receive(:request).with(uri, net_http_get).and_return(response)
+ end
+
+ it "should log the HTTP GET request to debug" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://www.uri-to-fetch.com/api/v2/endpoint")
+ subject.request(uri, options)
+ end
+
+ context "when there is a user provided in the request" do
+ context "and there is also a password provided" do
+ context "that contains cgi escaped characters" do
+ let(:uri) { URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with the username and password" do
+ expect(net_http_get).to receive(:basic_auth).with("username", "password$")
+ subject.request(uri, options)
+ end
+ end
+
+ context "that is all unescaped characters" do
+ let(:uri) { URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ it "should request basic authentication with the username and proper cgi compliant password" do
+ expect(net_http_get).to receive(:basic_auth).with("username", "password")
+ subject.request(uri, options)
+ end
+ end
+ end
+
+ context "and there is no password provided" do
+ let(:uri) { URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with just the user" do
+ expect(net_http_get).to receive(:basic_auth).with("username", nil)
+ subject.request(uri, options)
+ end
+ end
+
+ context "that contains cgi escaped characters" do
+ let(:uri) { URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with the proper cgi compliant password user" do
+ expect(net_http_get).to receive(:basic_auth).with("username$", nil)
+ subject.request(uri, options)
+ end
+ end
+ end
+
+ context "when the request response causes a NoMethodError" do
+ before { allow(connection).to receive(:request).with(uri, net_http_get) { raise NoMethodError.new(message) } }
+
+ context "and the error message is about use_ssl=" do
+ let(:message) { "undefined method 'use_ssl='" }
+
+ it "should raise a LoadError about openssl" do
+ expect { subject.request(uri, options) }.to raise_error(LoadError, "cannot load such file -- openssl")
+ end
+ end
+
+ context "and the error message is not about use_ssl=" do
+ let(:message) { "undefined method 'undefined_method_call'" }
+
+ it "should raise the original NoMethodError" do
+ expect { subject.request(uri, options) }.to raise_error(NoMethodError, "undefined method 'undefined_method_call'")
+ end
+ end
+ end
+
+ context "when the request response causes a OpenSSL::SSL::SSLError" do
+ before { allow(connection).to receive(:request).with(uri, net_http_get) { raise OpenSSL::SSL::SSLError.new } }
+
+ it "should raise a LoadError about openssl" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
+ %r{Could not verify the SSL certificate for http://www.uri-to-fetch.com/api/v2/endpoint})
+ end
+ end
+
+ context "when the request response causes an error included in HTTP_ERRORS" do
+ let(:message) { nil }
+ let(:error) { RuntimeError.new(message) }
+
+ before do
+ stub_const("Bundler::Fetcher::HTTP_ERRORS", [RuntimeError])
+ allow(connection).to receive(:request).with(uri, net_http_get) { raise error }
+ end
+
+ it "should trace log the error" do
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
+ end
+
+ context "when error message is about the host being down" do
+ let(:message) { "host down: http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error message is about getaddrinfo issues" do
+ let(:message) { "getaddrinfo: nodename nor servname provided for http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error message is about neither host down or getaddrinfo" do
+ let(:message) { "other error about network" }
+
+ it "should raise a Bundler::HTTPError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ end
+
+ context "when the there are credentials provided in the request" do
+ let(:uri) { URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ before do
+ allow(net_http_get).to receive(:basic_auth).with("username", "password")
+ end
+
+ it "should raise a Bundler::HTTPError that doesn't contain the password" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ end
+ end
+ end
+
+ context "when error message is about no route to host" do
+ let(:message) { "Failed to open TCP connection to www.uri-to-fetch.com:443 " }
+
+ it "should raise a Bundler::Fetcher::HTTPError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (#{message})")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
new file mode 100644
index 0000000000..0cf0ae764e
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Index do
+ let(:downloader) { nil }
+ let(:remote) { nil }
+ let(:display_uri) { "http://sample_uri.com" }
+ let(:rubygems) { double(:rubygems) }
+ let(:gem_names) { %w[foo bar] }
+
+ subject { described_class.new(downloader, remote, display_uri) }
+
+ before { allow(Bundler).to receive(:rubygems).and_return(rubygems) }
+
+ it "fetches and returns the list of remote specs" do
+ expect(rubygems).to receive(:fetch_all_remote_specs) { nil }
+ subject.specs(gem_names)
+ end
+
+ context "error handling" do
+ shared_examples_for "the error is properly handled" do
+ let(:remote_uri) { URI("http://remote-uri.org") }
+ before do
+ allow(subject).to receive(:remote_uri).and_return(remote_uri)
+ end
+
+ context "when certificate verify failed" do
+ let(:error_message) { "certificate verify failed" }
+
+ it "should raise a Bundler::Fetcher::CertificateFailureError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
+ %r{Could not verify the SSL certificate for http://sample_uri.com})
+ end
+ end
+
+ context "when a 401 response occurs" do
+ let(:error_message) { "401" }
+
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ %r{Authentication is required for http://remote-uri.org})
+ end
+ end
+
+ context "when a 403 response occurs" do
+ let(:error_message) { "403" }
+
+ before do
+ allow(remote_uri).to receive(:userinfo).and_return(userinfo)
+ end
+
+ context "and there was userinfo" do
+ let(:userinfo) { double(:userinfo) }
+
+ it "should raise a Bundler::Fetcher::BadAuthenticationError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
+ %r{Bad username or password for http://remote-uri.org})
+ end
+ end
+
+ context "and there was no userinfo" do
+ let(:userinfo) { nil }
+
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ %r{Authentication is required for http://remote-uri.org})
+ end
+ end
+ end
+
+ context "any other message is returned" do
+ let(:error_message) { "You get an error, you get an error!" }
+
+ before { allow(Bundler).to receive(:ui).and_return(double(:trace => nil)) }
+
+ it "should raise a Bundler::HTTPError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::HTTPError, "Could not fetch specs from http://sample_uri.com")
+ end
+ end
+ end
+
+ context "when a Gem::RemoteFetcher::FetchError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Gem::RemoteFetcher::FetchError.new(error_message, nil) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+
+ context "when a OpenSSL::SSL::SSLError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise OpenSSL::SSL::SSLError.new(error_message) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+
+ context "when a Net::HTTPFatalError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Net::HTTPFatalError.new(error_message, 404) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
new file mode 100644
index 0000000000..184b9efa64
--- /dev/null
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require "bundler/fetcher"
+
+RSpec.describe Bundler::Fetcher do
+ let(:uri) { URI("https://example.com") }
+ let(:remote) { double("remote", :uri => uri, :original_uri => nil) }
+
+ subject(:fetcher) { Bundler::Fetcher.new(remote) }
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "#connection" do
+ context "when Gem.configuration doesn't specify http_proxy" do
+ it "specify no http_proxy" do
+ expect(fetcher.http_proxy).to be_nil
+ end
+ it "consider environment vars when determine proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example.com")
+ end
+ end
+ end
+ context "when Gem.configuration specifies http_proxy " do
+ let(:proxy) { "http://proxy-example2.com" }
+ before do
+ allow(Bundler.rubygems.configuration).to receive(:[]).with(:http_proxy).and_return(proxy)
+ end
+ it "consider Gem.configuration when determine proxy" do
+ expect(fetcher.http_proxy).to match("http://proxy-example2.com")
+ end
+ it "consider Gem.configuration when determine proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example2.com")
+ end
+ end
+ context "when the proxy is :no_proxy" do
+ let(:proxy) { :no_proxy }
+ it "does not set a proxy" do
+ expect(fetcher.http_proxy).to be_nil
+ end
+ end
+ end
+
+ context "when a rubygems source mirror is set" do
+ let(:orig_uri) { URI("http://zombo.com") }
+ let(:remote_with_mirror) do
+ double("remote", :uri => uri, :original_uri => orig_uri, :anonymized_uri => uri)
+ end
+
+ let(:fetcher) { Bundler::Fetcher.new(remote_with_mirror) }
+
+ it "sets the 'X-Gemfile-Source' header containing the original source" do
+ expect(
+ fetcher.send(:connection).override_headers["X-Gemfile-Source"]
+ ).to eq("http://zombo.com")
+ end
+ end
+
+ context "when there is no rubygems source mirror set" do
+ let(:remote_no_mirror) do
+ double("remote", :uri => uri, :original_uri => nil, :anonymized_uri => uri)
+ end
+
+ let(:fetcher) { Bundler::Fetcher.new(remote_no_mirror) }
+
+ it "does not set the 'X-Gemfile-Source' header" do
+ expect(fetcher.send(:connection).override_headers["X-Gemfile-Source"]).to be_nil
+ end
+ end
+
+ context "when there are proxy environment variable(s) set" do
+ it "consider http_proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example3.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example3.com")
+ end
+ end
+ it "consider no_proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example4.com", "NO_PROXY" => ".example.com,.example.net") do
+ expect(
+ fetcher.send(:connection).no_proxy
+ ).to eq([".example.com", ".example.net"])
+ end
+ end
+ end
+
+ context "when no ssl configuration is set" do
+ it "no cert" do
+ expect(fetcher.send(:connection).cert).to be_nil
+ expect(fetcher.send(:connection).key).to be_nil
+ end
+ end
+
+ context "when bunder ssl ssl configuration is set" do
+ before do
+ cert = File.join(Spec::Path.tmpdir, "cert")
+ File.open(cert, "w") {|f| f.write "PEM" }
+ allow(Bundler.settings).to receive(:[]).and_return(nil)
+ allow(Bundler.settings).to receive(:[]).with(:ssl_client_cert).and_return(cert)
+ expect(OpenSSL::X509::Certificate).to receive(:new).with("PEM").and_return("cert")
+ expect(OpenSSL::PKey::RSA).to receive(:new).with("PEM").and_return("key")
+ end
+ after do
+ FileUtils.rm File.join(Spec::Path.tmpdir, "cert")
+ end
+ it "use bundler configuration" do
+ expect(fetcher.send(:connection).cert).to eq("cert")
+ expect(fetcher.send(:connection).key).to eq("key")
+ end
+ end
+
+ context "when gem ssl configuration is set" do
+ before do
+ allow(Bundler.rubygems.configuration).to receive_messages(
+ :http_proxy => nil,
+ :ssl_client_cert => "cert",
+ :ssl_ca_cert => "ca"
+ )
+ expect(File).to receive(:read).and_return("")
+ expect(OpenSSL::X509::Certificate).to receive(:new).and_return("cert")
+ expect(OpenSSL::PKey::RSA).to receive(:new).and_return("key")
+ store = double("ca store")
+ expect(store).to receive(:add_file)
+ expect(OpenSSL::X509::Store).to receive(:new).and_return(store)
+ end
+ it "use gem configuration" do
+ expect(fetcher.send(:connection).cert).to eq("cert")
+ expect(fetcher.send(:connection).key).to eq("key")
+ end
+ end
+ end
+
+ describe "#user_agent" do
+ it "builds user_agent with current ruby version and Bundler settings" do
+ allow(Bundler.settings).to receive(:all).and_return(%w[foo bar])
+ expect(fetcher.user_agent).to match(%r{bundler/(\d.)})
+ expect(fetcher.user_agent).to match(%r{rubygems/(\d.)})
+ expect(fetcher.user_agent).to match(%r{ruby/(\d.)})
+ expect(fetcher.user_agent).to match(%r{options/foo,bar})
+ end
+
+ describe "include CI information" do
+ it "from one CI" do
+ with_env_vars("JENKINS_URL" => "foo") do
+ ci_part = fetcher.user_agent.split(" ").find {|x| x.match(%r{\Aci/}) }
+ expect(ci_part).to match("jenkins")
+ end
+ end
+
+ it "from many CI" do
+ with_env_vars("TRAVIS" => "foo", "CI_NAME" => "my_ci") do
+ ci_part = fetcher.user_agent.split(" ").find {|x| x.match(%r{\Aci/}) }
+ expect(ci_part).to match("travis")
+ expect(ci_part).to match("my_ci")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
new file mode 100644
index 0000000000..2a1be491ef
--- /dev/null
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+require "bundler"
+require "bundler/friendly_errors"
+require "cgi"
+
+RSpec.describe Bundler, "friendly errors" do
+ context "with invalid YAML in .gemrc" do
+ before do
+ File.open(Gem.configuration.config_file_name, "w") do |f|
+ f.write "invalid: yaml: hah"
+ end
+ end
+
+ after do
+ FileUtils.rm(Gem.configuration.config_file_name)
+ end
+
+ it "reports a relevant friendly error message", :ruby => ">= 1.9", :rubygems => "< 2.5.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "DEBUG" => true }
+
+ expect(out).to include("Your RubyGems configuration")
+ expect(out).to include("invalid YAML syntax")
+ expect(out).to include("Psych::SyntaxError")
+ expect(out).not_to include("ERROR REPORT TEMPLATE")
+ expect(exitstatus).to eq(25) if exitstatus
+ end
+
+ it "reports a relevant friendly error message", :ruby => ">= 1.9", :rubygems => ">= 2.5.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "DEBUG" => true }
+
+ expect(last_command.stderr).to include("Failed to load #{home(".gemrc")}")
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ it "calls log_error in case of exception" do
+ exception = Exception.new
+ expect(Bundler::FriendlyErrors).to receive(:exit_status).with(exception).and_return(1)
+ expect do
+ Bundler.with_friendly_errors do
+ raise exception
+ end
+ end.to raise_error(SystemExit)
+ end
+
+ it "calls exit_status on exception" do
+ exception = Exception.new
+ expect(Bundler::FriendlyErrors).to receive(:log_error).with(exception)
+ expect do
+ Bundler.with_friendly_errors do
+ raise exception
+ end
+ end.to raise_error(SystemExit)
+ end
+
+ describe "#log_error" do
+ shared_examples "Bundler.ui receive error" do |error, message|
+ it "" do
+ expect(Bundler.ui).to receive(:error).with(message || error.message)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ shared_examples "Bundler.ui receive trace" do |error|
+ it "" do
+ expect(Bundler.ui).to receive(:trace).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "YamlSyntaxError" do
+ it_behaves_like "Bundler.ui receive error", Bundler::YamlSyntaxError.new(StandardError.new, "sample_message")
+
+ it "Bundler.ui receive trace" do
+ std_error = StandardError.new
+ exception = Bundler::YamlSyntaxError.new(std_error, "sample_message")
+ expect(Bundler.ui).to receive(:trace).with(std_error)
+ Bundler::FriendlyErrors.log_error(exception)
+ end
+ end
+
+ context "Dsl::DSLError, GemspecError" do
+ it_behaves_like "Bundler.ui receive error", Bundler::Dsl::DSLError.new("description", "dsl_path", "backtrace")
+ it_behaves_like "Bundler.ui receive error", Bundler::GemspecError.new
+ end
+
+ context "GemRequireError" do
+ let(:orig_error) { StandardError.new }
+ let(:error) { Bundler::GemRequireError.new(orig_error, "sample_message") }
+
+ before do
+ allow(orig_error).to receive(:backtrace).and_return([])
+ end
+
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with(error.message)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "writes to Bundler.ui.trace" do
+ expect(Bundler.ui).to receive(:trace).with(orig_error, nil, true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "BundlerError" do
+ it "Bundler.ui receive error" do
+ error = Bundler::BundlerError.new
+ expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ it_behaves_like "Bundler.ui receive trace", Bundler::BundlerError.new
+ end
+
+ context "Thor::Error" do
+ it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new
+ end
+
+ context "LoadError" do
+ let(:error) { LoadError.new("cannot load such file -- openssl") }
+
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL.")
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "Bundler.ui receive warn" do
+ expect(Bundler.ui).to receive(:warn).with(any_args, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "Bundler.ui receive trace" do
+ expect(Bundler.ui).to receive(:trace).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "Interrupt" do
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with("\nQuitting...")
+ Bundler::FriendlyErrors.log_error(Interrupt.new)
+ end
+ it_behaves_like "Bundler.ui receive trace", Interrupt.new
+ end
+
+ context "Gem::InvalidSpecificationException" do
+ it "Bundler.ui receive error" do
+ error = Gem::InvalidSpecificationException.new
+ expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "SystemExit" do
+ # Does nothing
+ end
+
+ context "Java::JavaLang::OutOfMemoryError" do
+ module Java
+ module JavaLang
+ class OutOfMemoryError < StandardError; end
+ end
+ end
+
+ it "Bundler.ui receive error" do
+ error = Java::JavaLang::OutOfMemoryError.new
+ expect(Bundler.ui).to receive(:error).with(/JVM has run out of memory/)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "unexpected error" do
+ it "calls request_issue_report_for with error" do
+ error = StandardError.new
+ expect(Bundler::FriendlyErrors).to receive(:request_issue_report_for).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+ end
+
+ describe "#exit_status" do
+ it "calls status_code for BundlerError" do
+ error = Bundler::BundlerError.new
+ expect(error).to receive(:status_code).and_return("sample_status_code")
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status_code")
+ end
+
+ it "returns 15 for Thor::Error" do
+ error = Bundler::Thor::Error.new
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq(15)
+ end
+
+ it "calls status for SystemExit" do
+ error = SystemExit.new
+ expect(error).to receive(:status).and_return("sample_status")
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status")
+ end
+
+ it "returns 1 in other cases" do
+ error = StandardError.new
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq(1)
+ end
+ end
+
+ describe "#request_issue_report_for" do
+ it "calls relevant methods for Bundler.ui" do
+ expect(Bundler.ui).to receive(:info)
+ expect(Bundler.ui).to receive(:error)
+ expect(Bundler.ui).to receive(:warn)
+ Bundler::FriendlyErrors.request_issue_report_for(StandardError.new)
+ end
+
+ it "includes error class, message and backlog" do
+ error = StandardError.new
+ allow(Bundler::FriendlyErrors).to receive(:issues_url).and_return("")
+
+ expect(error).to receive(:class).at_least(:once)
+ expect(error).to receive(:message).at_least(:once)
+ expect(error).to receive(:backtrace).at_least(:once)
+ Bundler::FriendlyErrors.request_issue_report_for(error)
+ end
+ end
+
+ describe "#issues_url" do
+ it "generates a search URL for the exception message" do
+ exception = Exception.new("Exception message")
+
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=Exception+message&type=Issues")
+ end
+
+ it "generates a search URL for only the first line of a multi-line exception message" do
+ exception = Exception.new(<<END)
+First line of the exception message
+Second line of the exception message
+END
+
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=First+line+of+the+exception+message&type=Issues")
+ end
+
+ it "generates the url without colons" do
+ exception = Exception.new(<<END)
+Exception ::: with ::: colons :::
+END
+ issues_url = Bundler::FriendlyErrors.issues_url(exception)
+ expect(issues_url).not_to include("%3A")
+ expect(issues_url).to eq("https://github.com/bundler/bundler/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
+ end
+
+ it "removes information after - for Errono::EACCES" do
+ exception = Exception.new(<<END)
+Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/foo/bar/
+END
+ allow(exception).to receive(:is_a?).with(Errno).and_return(true)
+ issues_url = Bundler::FriendlyErrors.issues_url(exception)
+ expect(issues_url).not_to include("/Users/foo/bar")
+ expect(issues_url).to eq("https://github.com/bundler/bundler/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
new file mode 100644
index 0000000000..a627129fe3
--- /dev/null
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -0,0 +1,351 @@
+# frozen_string_literal: true
+
+require "rake"
+require "bundler/gem_helper"
+
+RSpec.describe Bundler::GemHelper do
+ let(:app_name) { "lorem__ipsum" }
+ let(:app_path) { bundled_app app_name }
+ let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") }
+
+ before(:each) do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ bundle "gem #{app_name}"
+ end
+
+ context "determining gemspec" do
+ subject { Bundler::GemHelper.new(app_path) }
+
+ context "fails" do
+ it "when there is no gemspec" do
+ FileUtils.rm app_gemspec_path
+ expect { subject }.to raise_error(/Unable to determine name/)
+ end
+
+ it "when there are two gemspecs and the name isn't specified" do
+ FileUtils.touch app_path.join("#{app_name}-2.gemspec")
+ expect { subject }.to raise_error(/Unable to determine name/)
+ end
+ end
+
+ context "interpolates the name" do
+ before do
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ content = File.read(app_gemspec_path)
+ content.sub!(/raise "RubyGems 2\.0 or newer.*/, "")
+ File.open(app_gemspec_path, "w") {|f| f.write(content) }
+ end
+ end
+
+ it "when there is only one gemspec" do
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+
+ it "for a hidden gemspec" do
+ FileUtils.mv app_gemspec_path, app_path.join(".gemspec")
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+ end
+
+ it "handles namespaces and converts them to CamelCase" do
+ bundle "gem #{app_name}-foo_bar"
+ underscore_path = bundled_app "#{app_name}-foo_bar"
+
+ lib = underscore_path.join("lib/#{app_name}/foo_bar.rb").read
+ expect(lib).to include("module LoremIpsum")
+ expect(lib).to include("module FooBar")
+ end
+ end
+
+ context "gem management" do
+ def mock_confirm_message(message)
+ expect(Bundler.ui).to receive(:confirm).with(message)
+ end
+
+ def mock_build_message(name, version)
+ message = "#{name} #{version} built to pkg/#{name}-#{version}.gem."
+ mock_confirm_message message
+ end
+
+ subject! { Bundler::GemHelper.new(app_path) }
+ let(:app_version) { "0.1.0" }
+ let(:app_gem_dir) { app_path.join("pkg") }
+ let(:app_gem_path) { app_gem_dir.join("#{app_name}-#{app_version}.gem") }
+ let(:app_gemspec_content) { remove_push_guard(File.read(app_gemspec_path)) }
+
+ before(:each) do
+ content = app_gemspec_content.gsub("TODO: ", "")
+ content.sub!(/homepage\s+= ".*"/, 'homepage = ""')
+ content.gsub!(/spec\.metadata.+\n/, "")
+ File.open(app_gemspec_path, "w") {|file| file << content }
+ end
+
+ def remove_push_guard(gemspec_content)
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ gemspec_content.sub!(/raise "RubyGems 2\.0 or newer.*/, "")
+ end
+ gemspec_content
+ end
+
+ it "uses a shell UI for output" do
+ expect(Bundler.ui).to be_a(Bundler::UI::Shell)
+ end
+
+ describe "#install" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ context "defines Rake tasks" do
+ let(:task_names) do
+ %w[build install release release:guard_clean
+ release:source_control_push release:rubygem_push]
+ end
+
+ context "before installation" do
+ it "raises an error with appropriate message" do
+ task_names.each do |name|
+ expect { Rake.application[name] }.
+ to raise_error(/^Don't know how to build task '#{name}'/)
+ end
+ end
+ end
+
+ context "after installation" do
+ before do
+ subject.install
+ end
+
+ it "adds Rake tasks successfully" do
+ task_names.each do |name|
+ expect { Rake.application[name] }.not_to raise_error
+ expect(Rake.application[name]).to be_instance_of Rake::Task
+ end
+ end
+
+ it "provides a way to access the gemspec object" do
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+ end
+ end
+ end
+
+ describe "#build_gem" do
+ context "when build failed" do
+ it "raises an error with appropriate message" do
+ # break the gemspec by adding back the TODOs
+ File.open(app_gemspec_path, "w") {|file| file << app_gemspec_content }
+ expect { subject.build_gem }.to raise_error(/TODO/)
+ end
+ end
+
+ context "when build was successful" do
+ it "creates .gem file" do
+ mock_build_message app_name, app_version
+ subject.build_gem
+ expect(app_gem_path).to exist
+ end
+ end
+ end
+
+ describe "#install_gem" do
+ context "when installation was successful" do
+ it "gem is installed" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "#{app_name} (#{app_version}) installed."
+ subject.install_gem(nil, :local)
+ expect(app_gem_path).to exist
+ gem_command! :list
+ expect(out).to include("#{app_name} (#{app_version})")
+ end
+ end
+
+ context "when installation fails" do
+ it "raises an error with appropriate message" do
+ # create empty gem file in order to simulate install failure
+ allow(subject).to receive(:build_gem) do
+ FileUtils.mkdir_p(app_gem_dir)
+ FileUtils.touch app_gem_path
+ app_gem_path
+ end
+ expect { subject.install_gem }.to raise_error(/Couldn't install gem/)
+ end
+ end
+ end
+
+ describe "rake release" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ subject.install
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ before do
+ Dir.chdir(app_path) do
+ `git init`
+ `git config user.email "you@example.com"`
+ `git config user.name "name"`
+ `git config push.default simple`
+ end
+
+ # silence messages
+ allow(Bundler.ui).to receive(:confirm)
+ allow(Bundler.ui).to receive(:error)
+ end
+
+ context "fails" do
+ it "when there are unstaged files" do
+ expect { Rake.application["release"].invoke }.
+ to raise_error("There are files that need to be committed first.")
+ end
+
+ it "when there are uncommitted files" do
+ Dir.chdir(app_path) { `git add .` }
+ expect { Rake.application["release"].invoke }.
+ to raise_error("There are files that need to be committed first.")
+ end
+
+ it "when there is no git remote" do
+ Dir.chdir(app_path) { `git commit -a -m "initial commit"` }
+ expect { Rake.application["release"].invoke }.to raise_error(RuntimeError)
+ end
+ end
+
+ context "succeeds" do
+ before do
+ Dir.chdir(gem_repo1) { `git init --bare` }
+ Dir.chdir(app_path) do
+ `git remote add origin file://#{gem_repo1}`
+ `git commit -a -m "initial commit"`
+ end
+ end
+
+ it "on releasing" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "Tagged v#{app_version}."
+ mock_confirm_message "Pushed git commits and tags."
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Dir.chdir(app_path) { sys_exec("git push -u origin master") }
+
+ Rake.application["release"].invoke
+ end
+
+ it "even if tag already exists" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "Tag v#{app_version} has already been created."
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Dir.chdir(app_path) do
+ `git tag -a -m \"Version #{app_version}\" v#{app_version}`
+ end
+
+ Rake.application["release"].invoke
+ end
+ end
+ end
+
+ describe "release:rubygem_push" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ subject.install
+ allow(subject).to receive(:sh)
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ before do
+ Dir.chdir(app_path) do
+ `git init`
+ `git config user.email "you@example.com"`
+ `git config user.name "name"`
+ `git config push.default simple`
+ end
+
+ # silence messages
+ allow(Bundler.ui).to receive(:confirm)
+ allow(Bundler.ui).to receive(:error)
+
+ credentials = double("credentials", "file?" => true)
+ allow(Bundler.user_home).to receive(:join).
+ with(".gem/credentials").and_return(credentials)
+ end
+
+ describe "success messaging" do
+ context "No allowed_push_host set" do
+ before do
+ allow(subject).to receive(:allowed_push_host).and_return(nil)
+ end
+
+ around do |example|
+ orig_host = ENV["RUBYGEMS_HOST"]
+ ENV["RUBYGEMS_HOST"] = rubygems_host_env
+
+ example.run
+
+ ENV["RUBYGEMS_HOST"] = orig_host
+ end
+
+ context "RUBYGEMS_HOST env var is set" do
+ let(:rubygems_host_env) { "https://custom.env.gemhost.com" }
+
+ it "should report successful push to the host from the environment" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to #{rubygems_host_env}"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+
+ context "RUBYGEMS_HOST env var is not set" do
+ let(:rubygems_host_env) { nil }
+
+ it "should report successful push to rubygems.org" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+
+ context "RUBYGEMS_HOST env var is an empty string" do
+ let(:rubygems_host_env) { "" }
+
+ it "should report successful push to rubygems.org" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+ end
+
+ context "allowed_push_host set in gemspec" do
+ before do
+ allow(subject).to receive(:allowed_push_host).and_return("https://my.gemhost.com")
+ end
+
+ it "should report successful push to the allowed gem host" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to https://my.gemhost.com"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
new file mode 100644
index 0000000000..01e0232fba
--- /dev/null
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::GemVersionPromoter do
+ context "conservative resolver" do
+ def versions(result)
+ result.flatten.map(&:version).map(&:to_s)
+ end
+
+ def make_instance(*args)
+ @gvp = Bundler::GemVersionPromoter.new(*args).tap do |gvp|
+ gvp.class.class_eval { public :filter_dep_specs, :sort_dep_specs }
+ end
+ end
+
+ def unlocking(options)
+ make_instance(Bundler::SpecSet.new([]), ["foo"]).tap do |p|
+ p.level = options[:level] if options[:level]
+ p.strict = options[:strict] if options[:strict]
+ end
+ end
+
+ def keep_locked(options)
+ make_instance(Bundler::SpecSet.new([]), ["bar"]).tap do |p|
+ p.level = options[:level] if options[:level]
+ p.strict = options[:strict] if options[:strict]
+ end
+ end
+
+ def build_spec_groups(name, versions)
+ versions.map do |v|
+ Bundler::Resolver::SpecGroup.new(build_spec(name, v))
+ end
+ end
+
+ # Rightmost (highest array index) in result is most preferred.
+ # Leftmost (lowest array index) in result is least preferred.
+ # `build_spec_groups` has all versions of gem in index.
+ # `build_spec` is the version currently in the .lock file.
+ #
+ # In default (not strict) mode, all versions in the index will
+ # be returned, allowing Bundler the best chance to resolve all
+ # dependencies, but sometimes resulting in upgrades that some
+ # would not consider conservative.
+ context "filter specs (strict) level patch" do
+ it "when keeping build_spec, keep current, next release" do
+ keep_locked(:level => :patch)
+ res = @gvp.filter_dep_specs(
+ build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
+ build_spec("foo", "1.7.8").first
+ )
+ expect(versions(res)).to eq %w[1.7.9 1.7.8]
+ end
+
+ it "when unlocking prefer next release first" do
+ unlocking(:level => :patch)
+ res = @gvp.filter_dep_specs(
+ build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
+ build_spec("foo", "1.7.8").first
+ )
+ expect(versions(res)).to eq %w[1.7.8 1.7.9]
+ end
+
+ it "when unlocking keep current when already at latest release" do
+ unlocking(:level => :patch)
+ res = @gvp.filter_dep_specs(
+ build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
+ build_spec("foo", "1.7.9").first
+ )
+ expect(versions(res)).to eq %w[1.7.9]
+ end
+ end
+
+ context "filter specs (strict) level minor" do
+ it "when unlocking favor next releases, remove minor and major increases" do
+ unlocking(:level => :minor)
+ res = @gvp.filter_dep_specs(
+ build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
+ build_spec("foo", "0.2.0").first
+ )
+ expect(versions(res)).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0]
+ end
+
+ it "when keep locked, keep current, then favor next release, remove minor and major increases" do
+ keep_locked(:level => :minor)
+ res = @gvp.filter_dep_specs(
+ build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
+ build_spec("foo", "0.2.0").first
+ )
+ expect(versions(res)).to eq %w[0.3.0 0.3.1 0.9.0 0.2.0]
+ end
+ end
+
+ context "sort specs (not strict) level patch" do
+ it "when not unlocking, same order but make sure build_spec version is most preferred to stay put" do
+ keep_locked(:level => :patch)
+ res = @gvp.sort_dep_specs(
+ build_spec_groups("foo", %w[1.5.4 1.6.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 2.0.0 2.0.1]),
+ build_spec("foo", "1.7.7").first
+ )
+ expect(versions(res)).to eq %w[1.5.4 1.6.5 1.7.6 2.0.0 2.0.1 1.8.0 1.8.1 1.7.8 1.7.9 1.7.7]
+ end
+
+ it "when unlocking favor next release, then current over minor increase" do
+ unlocking(:level => :patch)
+ res = @gvp.sort_dep_specs(
+ build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.8.0]),
+ build_spec("foo", "1.7.8").first
+ )
+ expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9]
+ end
+
+ it "when unlocking do proper integer comparison, not string" do
+ unlocking(:level => :patch)
+ res = @gvp.sort_dep_specs(
+ build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]),
+ build_spec("foo", "1.7.8").first
+ )
+ expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9 1.7.15]
+ end
+
+ it "leave current when unlocking but already at latest release" do
+ unlocking(:level => :patch)
+ res = @gvp.sort_dep_specs(
+ build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
+ build_spec("foo", "1.7.9").first
+ )
+ expect(versions(res)).to eq %w[2.0.0 1.8.0 1.7.9]
+ end
+ end
+
+ context "sort specs (not strict) level minor" do
+ it "when unlocking favor next release, then minor increase over current" do
+ unlocking(:level => :minor)
+ res = @gvp.sort_dep_specs(
+ build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
+ build_spec("foo", "0.2.0").first
+ )
+ expect(versions(res)).to eq %w[2.0.0 2.0.1 1.0.0 0.2.0 0.3.0 0.3.1 0.9.0]
+ end
+ end
+
+ context "level error handling" do
+ subject { Bundler::GemVersionPromoter.new }
+
+ it "should raise if not major, minor or patch is passed" do
+ expect { subject.level = :minjor }.to raise_error ArgumentError
+ end
+
+ it "should raise if invalid classes passed" do
+ [123, nil].each do |value|
+ expect { subject.level = value }.to raise_error ArgumentError
+ end
+ end
+
+ it "should accept major, minor patch symbols" do
+ [:major, :minor, :patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value
+ end
+ end
+
+ it "should accept major, minor patch strings" do
+ %w[major minor patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value.to_sym
+ end
+ end
+ end
+
+ context "debug output" do
+ it "should not kerblooie on its own debug output" do
+ gvp = unlocking(:level => :patch)
+ dep = Bundler::DepProxy.new(dep("foo", "1.2.0").first, "ruby")
+ result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0]))
+ expect(result.class).to eq Array
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/index_spec.rb b/spec/bundler/bundler/index_spec.rb
new file mode 100644
index 0000000000..0f3f6e4944
--- /dev/null
+++ b/spec/bundler/bundler/index_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Index do
+ let(:specs) { [] }
+ subject { described_class.build {|i| i.use(specs) } }
+
+ context "specs with a nil platform" do
+ let(:spec) do
+ Gem::Specification.new do |s|
+ s.name = "json"
+ s.version = "1.8.3"
+ allow(s).to receive(:platform).and_return(nil)
+ end
+ end
+ let(:specs) { [spec] }
+
+ describe "#search_by_spec" do
+ it "finds the spec when a nil platform is specified" do
+ expect(subject.search(spec)).to eq([spec])
+ end
+
+ it "finds the spec when a ruby platform is specified" do
+ query = spec.dup.tap {|s| s.platform = "ruby" }
+ expect(subject.search(query)).to eq([spec])
+ end
+ end
+ end
+
+ context "with specs that include development dependencies" do
+ let(:specs) { [*build_spec("a", "1.0.0") {|s| s.development("b", "~> 1.0") }] }
+
+ it "does not include b in #dependency_names" do
+ expect(subject.dependency_names).not_to include("b")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
new file mode 100644
index 0000000000..7340a3acc0
--- /dev/null
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "bundler/installer/gem_installer"
+
+RSpec.describe Bundler::GemInstaller do
+ let(:installer) { instance_double("Installer") }
+ let(:spec_source) { instance_double("SpecSource") }
+ let(:spec) { instance_double("Specification", :name => "dummy", :version => "0.0.1", :loaded_from => "dummy", :source => spec_source) }
+
+ subject { described_class.new(spec, installer) }
+
+ context "spec_settings is nil" do
+ it "invokes install method with empty build_args", :rubygems => ">= 2" do
+ allow(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => [])
+ subject.install_from_spec
+ end
+ end
+
+ context "spec_settings is build option" do
+ it "invokes install method with build_args", :rubygems => ">= 2" do
+ allow(Bundler.settings).to receive(:[]).with(:bin)
+ allow(Bundler.settings).to receive(:[]).with(:inline)
+ allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
+ allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
+ expect(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"])
+ subject.install_from_spec
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
new file mode 100644
index 0000000000..ace5c1a23a
--- /dev/null
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require "bundler/installer/parallel_installer"
+
+RSpec.describe Bundler::ParallelInstaller do
+ let(:installer) { instance_double("Installer") }
+ let(:all_specs) { [] }
+ let(:size) { 1 }
+ let(:standalone) { false }
+ let(:force) { false }
+
+ subject { described_class.new(installer, all_specs, size, standalone, force) }
+
+ context "when dependencies that are not on the overall installation list are the only ones not installed" do
+ let(:all_specs) do
+ [
+ build_spec("alpha", "1.0") {|s| s.runtime "a", "1" },
+ ].flatten
+ end
+
+ it "prints a warning" do
+ expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
+Your lockfile was created by an old Bundler that left some things out.
+You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
+The missing gems are:
+* a depended upon by alpha
+ W
+ subject.check_for_corrupt_lockfile
+ end
+
+ context "when size > 1" do
+ let(:size) { 500 }
+
+ it "prints a warning and sets size to 1" do
+ expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
+Your lockfile was created by an old Bundler that left some things out.
+Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time.
+You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
+The missing gems are:
+* a depended upon by alpha
+ W
+ subject.check_for_corrupt_lockfile
+ expect(subject.size).to eq(1)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/spec_installation_spec.rb b/spec/bundler/bundler/installer/spec_installation_spec.rb
new file mode 100644
index 0000000000..a9cf09a372
--- /dev/null
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require "bundler/installer/parallel_installer"
+
+RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
+ let!(:dep) do
+ a_spec = Object.new
+ def a_spec.name
+ "I like tests"
+ end
+ a_spec
+ end
+
+ describe "#ready_to_enqueue?" do
+ context "when in enqueued state" do
+ it "is falsey" do
+ spec = described_class.new(dep)
+ spec.state = :enqueued
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ context "when in installed state" do
+ it "returns falsey" do
+ spec = described_class.new(dep)
+ spec.state = :installed
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ it "returns truthy" do
+ spec = described_class.new(dep)
+ expect(spec.ready_to_enqueue?).to be_truthy
+ end
+ end
+
+ describe "#dependencies_installed?" do
+ context "when all dependencies are installed" do
+ it "returns true" do
+ dependencies = []
+ dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => true, :all_dependencies => [], :type => :production)
+ dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ spec = described_class.new(dep)
+ allow(spec).to receive(:all_dependencies).and_return(dependencies)
+ expect(spec.dependencies_installed?(all_specs)).to be_truthy
+ end
+ end
+
+ context "when all dependencies are not installed" do
+ it "returns false" do
+ dependencies = []
+ dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => false, :all_dependencies => [], :type => :production)
+ dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ spec = described_class.new(dep)
+ allow(spec).to receive(:all_dependencies).and_return(dependencies)
+ expect(spec.dependencies_installed?(all_specs)).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
new file mode 100644
index 0000000000..3a6d61336f
--- /dev/null
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+require "bundler/lockfile_parser"
+
+RSpec.describe Bundler::LockfileParser do
+ let(:lockfile_contents) { strip_whitespace(<<-L) }
+ GIT
+ remote: https://github.com/alloy/peiji-san.git
+ revision: eca485d8dc95f12aaec1a434b49d295c7e91844b
+ specs:
+ peiji-san (1.2.0)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.3.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ peiji-san!
+ rake
+
+ RUBY VERSION
+ ruby 2.1.3p242
+
+ BUNDLED WITH
+ 1.12.0.rc.2
+ L
+
+ describe ".sections_in_lockfile" do
+ it "returns the attributes" do
+ attributes = described_class.sections_in_lockfile(lockfile_contents)
+ expect(attributes).to contain_exactly(
+ "BUNDLED WITH", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
+ )
+ end
+ end
+
+ describe ".unknown_sections_in_lockfile" do
+ let(:lockfile_contents) { strip_whitespace(<<-L) }
+ UNKNOWN ATTR
+
+ UNKNOWN ATTR 2
+ random contents
+ L
+
+ it "returns the unknown attributes" do
+ attributes = described_class.unknown_sections_in_lockfile(lockfile_contents)
+ expect(attributes).to contain_exactly("UNKNOWN ATTR", "UNKNOWN ATTR 2")
+ end
+ end
+
+ describe ".sections_to_ignore" do
+ subject { described_class.sections_to_ignore(base_version) }
+
+ context "with a nil base version" do
+ let(:base_version) { nil }
+
+ it "returns the same as > 1.0" do
+ expect(subject).to contain_exactly(
+ described_class::BUNDLED, described_class::RUBY, described_class::PLUGIN
+ )
+ end
+ end
+
+ context "with a prerelease base version" do
+ let(:base_version) { Gem::Version.create("1.11.0.rc.1") }
+
+ it "returns the same as for the release version" do
+ expect(subject).to contain_exactly(
+ described_class::RUBY, described_class::PLUGIN
+ )
+ end
+ end
+
+ context "with a current version" do
+ let(:base_version) { Gem::Version.create(Bundler::VERSION) }
+
+ it "returns an empty array" do
+ expect(subject).to eq([])
+ end
+ end
+
+ context "with a future version" do
+ let(:base_version) { Gem::Version.create("5.5.5") }
+
+ it "returns an empty array" do
+ expect(subject).to eq([])
+ end
+ end
+ end
+
+ describe "#initialize" do
+ before { allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app("gems.rb")) }
+ subject { described_class.new(lockfile_contents) }
+
+ let(:sources) do
+ [Bundler::Source::Git.new("uri" => "https://github.com/alloy/peiji-san.git", "revision" => "eca485d8dc95f12aaec1a434b49d295c7e91844b"),
+ Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"])]
+ end
+ let(:dependencies) do
+ {
+ "peiji-san" => Bundler::Dependency.new("peiji-san", ">= 0"),
+ "rake" => Bundler::Dependency.new("rake", ">= 0"),
+ }
+ end
+ let(:specs) do
+ [
+ Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb),
+ Bundler::LazySpecification.new("rake", v("10.3.2"), rb),
+ ]
+ end
+ let(:platforms) { [rb] }
+ let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
+ let(:ruby_version) { "ruby 2.1.3p242" }
+
+ shared_examples_for "parsing" do
+ it "parses correctly" do
+ expect(subject.sources).to eq sources
+ expect(subject.dependencies).to eq dependencies
+ expect(subject.specs).to eq specs
+ expect(Hash[subject.specs.map {|s| [s, s.dependencies] }]).to eq Hash[subject.specs.map {|s| [s, s.dependencies] }]
+ expect(subject.platforms).to eq platforms
+ expect(subject.bundler_version).to eq bundler_version
+ expect(subject.ruby_version).to eq ruby_version
+ end
+ end
+
+ include_examples "parsing"
+
+ context "when an extra section is at the end" do
+ let(:lockfile_contents) { super() + "\n\nFOO BAR\n baz\n baa\n qux\n" }
+ include_examples "parsing"
+ end
+
+ context "when an extra section is at the start" do
+ let(:lockfile_contents) { "FOO BAR\n baz\n baa\n qux\n\n" + super() }
+ include_examples "parsing"
+ end
+
+ context "when an extra section is in the middle" do
+ let(:lockfile_contents) { super().split(/(?=GEM)/).insert(1, "FOO BAR\n baz\n baa\n qux\n\n").join }
+ include_examples "parsing"
+ end
+
+ context "when a dependency has options" do
+ let(:lockfile_contents) { super().sub("peiji-san!", "peiji-san!\n foo: bar") }
+ include_examples "parsing"
+ end
+ end
+end
diff --git a/spec/bundler/bundler/mirror_spec.rb b/spec/bundler/bundler/mirror_spec.rb
new file mode 100644
index 0000000000..acd0895f2f
--- /dev/null
+++ b/spec/bundler/bundler/mirror_spec.rb
@@ -0,0 +1,329 @@
+# frozen_string_literal: true
+
+require "bundler/mirror"
+
+RSpec.describe Bundler::Settings::Mirror do
+ let(:mirror) { Bundler::Settings::Mirror.new }
+
+ it "returns zero when fallback_timeout is not set" do
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a number as a fallback_timeout" do
+ mirror.fallback_timeout = 1
+ expect(mirror.fallback_timeout).to eq(1)
+ end
+
+ it "takes truthy as a default fallback timeout" do
+ mirror.fallback_timeout = true
+ expect(mirror.fallback_timeout).to eq(0.1)
+ end
+
+ it "takes falsey as a zero fallback timeout" do
+ mirror.fallback_timeout = false
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a string with 'true' as a default fallback timeout" do
+ mirror.fallback_timeout = "true"
+ expect(mirror.fallback_timeout).to eq(0.1)
+ end
+
+ it "takes a string with 'false' as a zero fallback timeout" do
+ mirror.fallback_timeout = "false"
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a string for the uri but returns an uri object" do
+ mirror.uri = "http://localhost:9292"
+ expect(mirror.uri).to eq(URI("http://localhost:9292"))
+ end
+
+ it "takes an uri object for the uri" do
+ mirror.uri = URI("http://localhost:9293")
+ expect(mirror.uri).to eq(URI("http://localhost:9293"))
+ end
+
+ context "without a uri" do
+ it "invalidates the mirror" do
+ mirror.validate!
+ expect(mirror.valid?).to be_falsey
+ end
+ end
+
+ context "with an uri" do
+ before { mirror.uri = "http://localhost:9292" }
+
+ context "without a fallback timeout" do
+ it "is not valid by default" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ context "when probed" do
+ let(:probe) { double }
+
+ context "with a replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(true)
+ mirror.validate!(probe)
+ end
+
+ it "is valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+
+ context "with a non replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(false)
+ mirror.validate!(probe)
+ end
+
+ it "is still valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirror.fallback_timeout = 1 }
+
+ it "is not valid by default" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ context "when probed" do
+ let(:probe) { double }
+
+ context "with a replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(true)
+ mirror.validate!(probe)
+ end
+
+ it "is valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+
+ it "is validated only once" do
+ allow(probe).to receive(:replies?).and_raise("Only once!")
+ mirror.validate!(probe)
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+
+ context "with a non replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(false)
+ mirror.validate!(probe)
+ end
+
+ it "is not valid" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ it "is validated only once" do
+ allow(probe).to receive(:replies?).and_raise("Only once!")
+ mirror.validate!(probe)
+ expect(mirror.valid?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe "#==" do
+ it "returns true if uri and fallback timeout are the same" do
+ uri = "https://ruby.taobao.org"
+ mirror = Bundler::Settings::Mirror.new(uri, 1)
+ another_mirror = Bundler::Settings::Mirror.new(uri, 1)
+
+ expect(mirror == another_mirror).to be true
+ end
+ end
+ end
+end
+
+RSpec.describe Bundler::Settings::Mirrors do
+ let(:localhost_uri) { URI("http://localhost:9292") }
+
+ context "with a just created mirror" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(true)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ it "returns a mirror that contains the source uri for an unknown uri" do
+ mirror = mirrors.for("http://rubygems.org/")
+ expect(mirror).to eq(Bundler::Settings::Mirror.new("http://rubygems.org/"))
+ end
+
+ it "parses a mirror key and returns a mirror for the parsed uri" do
+ mirrors.parse("mirror.http://rubygems.org/", localhost_uri)
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ it "parses a relative mirror key and returns a mirror for the parsed http uri" do
+ mirrors.parse("mirror.rubygems.org", localhost_uri)
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ it "parses a relative mirror key and returns a mirror for the parsed https uri" do
+ mirrors.parse("mirror.rubygems.org", localhost_uri)
+ expect(mirrors.for("https://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ context "with a uri parsed already" do
+ before { mirrors.parse("mirror.http://rubygems.org/", localhost_uri) }
+
+ it "takes a mirror fallback_timeout and assigns the timeout" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "2")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(2)
+ end
+
+ it "parses a 'true' fallback timeout and sets the default timeout" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "true")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(0.1)
+ end
+
+ it "parses a 'false' fallback timeout and sets it to zero" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "false")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(0)
+ end
+ end
+ end
+
+ context "with a mirror prober that replies on time" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(true)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ context "with a default fallback_timeout for rubygems.org" do
+ before do
+ mirrors.parse("mirror.http://rubygems.org/", localhost_uri)
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "true")
+ end
+
+ it "returns localhost" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a mirror for all" do
+ before do
+ mirrors.parse("mirror.all", localhost_uri)
+ end
+
+ context "without a fallback timeout" do
+ it "returns localhost uri for rubygems" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+
+ it "returns localhost for any other url" do
+ expect(mirrors.for("http://whatever.com/").uri).to eq(localhost_uri)
+ end
+ end
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.all.fallback_timeout", "1") }
+
+ it "returns localhost uri for rubygems" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+
+ it "returns localhost for any other url" do
+ expect(mirrors.for("http://whatever.com/").uri).to eq(localhost_uri)
+ end
+ end
+ end
+ end
+
+ context "with a mirror prober that does not reply on time" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(false)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ context "with a localhost mirror for all" do
+ before { mirrors.parse("mirror.all", localhost_uri) }
+
+ context "without a fallback timeout" do
+ it "returns localhost" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.all.fallback_timeout", "true") }
+
+ it "returns the source uri, not localhost" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+ end
+ end
+
+ context "with localhost as a mirror for rubygems.org" do
+ before { mirrors.parse("mirror.http://rubygems.org/", localhost_uri) }
+
+ context "without a fallback timeout" do
+ it "returns the uri that is not mirrored" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+
+ it "returns localhost for rubygems.org" do
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.http://rubygems.org/.fallback_timeout", "true") }
+
+ it "returns the uri that is not mirrored" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+
+ it "returns rubygems.org for rubygems.org" do
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(URI("http://rubygems.org/"))
+ end
+ end
+ end
+ end
+end
+
+RSpec.describe Bundler::Settings::TCPSocketProbe do
+ let(:probe) { Bundler::Settings::TCPSocketProbe.new }
+
+ context "with a listening TCP Server" do
+ def with_server_and_mirror
+ server = TCPServer.new("127.0.0.1", 0)
+ mirror = Bundler::Settings::Mirror.new("http://localhost:#{server.addr[1]}", 1)
+ yield server, mirror
+ server.close unless server.closed?
+ end
+
+ it "probes the server correctly", :ruby_repo do
+ with_server_and_mirror do |server, mirror|
+ expect(server.closed?).to be_falsey
+ expect(probe.replies?(mirror)).to be_truthy
+ end
+ end
+
+ it "probes falsey when the server is down" do
+ with_server_and_mirror do |server, mirror|
+ server.close
+ expect(probe.replies?(mirror)).to be_falsey
+ end
+ end
+ end
+
+ context "with an invalid mirror" do
+ let(:mirror) { Bundler::Settings::Mirror.new("http://127.0.0.127:9292", true) }
+
+ it "fails with a timeout when there is nothing to tcp handshake" do
+ expect(probe.replies?(mirror)).to be_falsey
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/api/source_spec.rb b/spec/bundler/bundler/plugin/api/source_spec.rb
new file mode 100644
index 0000000000..2c50ff56a4
--- /dev/null
+++ b/spec/bundler/bundler/plugin/api/source_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::API::Source do
+ let(:uri) { "uri://to/test" }
+ let(:type) { "spec_type" }
+
+ subject(:source) do
+ klass = Class.new
+ klass.send :include, Bundler::Plugin::API::Source
+ klass.new("uri" => uri, "type" => type)
+ end
+
+ describe "attributes" do
+ it "allows access to uri" do
+ expect(source.uri).to eq("uri://to/test")
+ end
+
+ it "allows access to name" do
+ expect(source.name).to eq("spec_type at uri://to/test")
+ end
+ end
+
+ context "post_install" do
+ let(:installer) { double(:installer) }
+
+ before do
+ allow(Bundler::Source::Path::Installer).to receive(:new) { installer }
+ end
+
+ it "calls Path::Installer's post_install" do
+ expect(installer).to receive(:post_install).once
+
+ source.post_install(double(:spec))
+ end
+ end
+
+ context "install_path" do
+ let(:uri) { "uri://to/a/repository-name" }
+ let(:hash) { Digest(:SHA1).hexdigest(uri) }
+ let(:install_path) { Pathname.new "/bundler/install/path" }
+
+ before do
+ allow(Bundler).to receive(:install_path) { install_path }
+ end
+
+ it "returns basename with uri_hash" do
+ expected = Pathname.new "#{install_path}/repository-name-#{hash[0..11]}"
+ expect(source.install_path).to eq(expected)
+ end
+ end
+
+ context "to_lock" do
+ it "returns the string with remote and type" do
+ expected = strip_whitespace <<-L
+ PLUGIN SOURCE
+ remote: #{uri}
+ type: #{type}
+ specs:
+ L
+
+ expect(source.to_lock).to eq(expected)
+ end
+
+ context "with additional options to lock" do
+ before do
+ allow(source).to receive(:options_to_lock) { { "first" => "option" } }
+ end
+
+ it "includes them" do
+ expected = strip_whitespace <<-L
+ PLUGIN SOURCE
+ remote: #{uri}
+ type: #{type}
+ first: option
+ specs:
+ L
+
+ expect(source.to_lock).to eq(expected)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/api_spec.rb b/spec/bundler/bundler/plugin/api_spec.rb
new file mode 100644
index 0000000000..58fb908572
--- /dev/null
+++ b/spec/bundler/bundler/plugin/api_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::API do
+ context "plugin declarations" do
+ before do
+ stub_const "UserPluginClass", Class.new(Bundler::Plugin::API)
+ end
+
+ describe "#command" do
+ it "declares a command plugin with same class as handler" do
+ expect(Bundler::Plugin).
+ to receive(:add_command).with("meh", UserPluginClass).once
+
+ UserPluginClass.command "meh"
+ end
+
+ it "accepts another class as argument that handles the command" do
+ stub_const "NewClass", Class.new
+ expect(Bundler::Plugin).to receive(:add_command).with("meh", NewClass).once
+
+ UserPluginClass.command "meh", NewClass
+ end
+ end
+
+ describe "#source" do
+ it "declares a source plugin with same class as handler" do
+ expect(Bundler::Plugin).
+ to receive(:add_source).with("a_source", UserPluginClass).once
+
+ UserPluginClass.source "a_source"
+ end
+
+ it "accepts another class as argument that handles the command" do
+ stub_const "NewClass", Class.new
+ expect(Bundler::Plugin).to receive(:add_source).with("a_source", NewClass).once
+
+ UserPluginClass.source "a_source", NewClass
+ end
+ end
+
+ describe "#hook" do
+ it "accepts a block and passes it to Plugin module" do
+ foo = double("tester")
+ expect(foo).to receive(:called)
+
+ expect(Bundler::Plugin).to receive(:add_hook).with("post-foo").and_yield
+
+ Bundler::Plugin::API.hook("post-foo") { foo.called }
+ end
+ end
+ end
+
+ context "bundler interfaces provided" do
+ before do
+ stub_const "UserPluginClass", Class.new(Bundler::Plugin::API)
+ end
+
+ subject(:api) { UserPluginClass.new }
+
+ # A test of delegation
+ it "provides the Bundler's functions" do
+ expect(Bundler).to receive(:an_unknown_function).once
+
+ api.an_unknown_function
+ end
+
+ it "includes Bundler::SharedHelpers' functions" do
+ expect(Bundler::SharedHelpers).to receive(:an_unknown_helper).once
+
+ api.an_unknown_helper
+ end
+
+ context "#tmp" do
+ it "provides a tmp dir" do
+ expect(api.tmp("mytmp")).to be_directory
+ end
+
+ it "accepts multiple names for suffix" do
+ expect(api.tmp("myplugin", "download")).to be_directory
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/dsl_spec.rb b/spec/bundler/bundler/plugin/dsl_spec.rb
new file mode 100644
index 0000000000..be23db3bba
--- /dev/null
+++ b/spec/bundler/bundler/plugin/dsl_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::DSL do
+ DSL = Bundler::Plugin::DSL
+
+ subject(:dsl) { Bundler::Plugin::DSL.new }
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "/" }
+ end
+
+ describe "it ignores only the methods defined in Bundler::Dsl" do
+ it "doesn't raises error for Dsl methods" do
+ expect { dsl.install_if }.not_to raise_error
+ end
+
+ it "raises error for other methods" do
+ expect { dsl.no_method }.to raise_error(DSL::PluginGemfileError)
+ end
+ end
+
+ describe "source block" do
+ it "adds #source with :type to list and also inferred_plugins list" do
+ expect(dsl).to receive(:plugin).with("bundler-source-news").once
+
+ dsl.source("some_random_url", :type => "news") {}
+
+ expect(dsl.inferred_plugins).to eq(["bundler-source-news"])
+ end
+
+ it "registers a source type plugin only once for multiple declataions" do
+ expect(dsl).to receive(:plugin).with("bundler-source-news").and_call_original.once
+
+ dsl.source("some_random_url", :type => "news") {}
+ dsl.source("another_random_url", :type => "news") {}
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/events_spec.rb b/spec/bundler/bundler/plugin/events_spec.rb
new file mode 100644
index 0000000000..b09e915682
--- /dev/null
+++ b/spec/bundler/bundler/plugin/events_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Events do
+ context "plugin events" do
+ describe "#define" do
+ it "raises when redefining a constant" do
+ expect do
+ Bundler::Plugin::Events.send(:define, :GEM_BEFORE_INSTALL_ALL, "another-value")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "can define a new constant" do
+ Bundler::Plugin::Events.send(:define, :NEW_CONSTANT, "value")
+ expect(Bundler::Plugin::Events::NEW_CONSTANT).to eq("value")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
new file mode 100644
index 0000000000..ca3476ea2a
--- /dev/null
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Index do
+ Index = Bundler::Plugin::Index
+
+ before do
+ gemfile ""
+ path = lib_path(plugin_name)
+ index.register_plugin("new-plugin", path.to_s, [path.join("lib").to_s], commands, sources, hooks)
+ end
+
+ let(:plugin_name) { "new-plugin" }
+ let(:commands) { [] }
+ let(:sources) { [] }
+ let(:hooks) { [] }
+
+ subject(:index) { Index.new }
+
+ describe "#register plugin" do
+ it "is available for retrieval" do
+ expect(index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
+ end
+
+ it "load_paths is available for retrival" do
+ expect(index.load_paths(plugin_name)).to eq([lib_path(plugin_name).join("lib").to_s])
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
+ end
+
+ it "load_paths are persistent" do
+ new_index = Index.new
+ expect(new_index.load_paths(plugin_name)).to eq([lib_path(plugin_name).join("lib").to_s])
+ end
+ end
+
+ describe "commands" do
+ let(:commands) { ["newco"] }
+
+ it "returns the plugins name on query" do
+ expect(index.command_plugin("newco")).to eq(plugin_name)
+ end
+
+ it "raises error on conflict" do
+ expect do
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, ["newco"], [], [])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.command_plugin("newco")).to eq(plugin_name)
+ end
+ end
+
+ describe "source" do
+ let(:sources) { ["new_source"] }
+
+ it "returns the plugins name on query" do
+ expect(index.source_plugin("new_source")).to eq(plugin_name)
+ end
+
+ it "raises error on conflict" do
+ expect do
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, [], ["new_source"], [])
+ end.to raise_error(Index::SourceConflict)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.source_plugin("new_source")).to eq(plugin_name)
+ end
+ end
+
+ describe "hook" do
+ let(:hooks) { ["after-bar"] }
+
+ it "returns the plugins name on query" do
+ expect(index.hook_plugins("after-bar")).to include(plugin_name)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.hook_plugins("after-bar")).to eq([plugin_name])
+ end
+
+ context "that are not registered", :focused do
+ let(:file) { double("index-file") }
+
+ before do
+ index.hook_plugins("not-there")
+ allow(File).to receive(:open).and_yield(file)
+ end
+
+ it "should not save it with next registered hook" do
+ expect(file).to receive(:puts) do |content|
+ expect(content).not_to include("not-there")
+ end
+
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, [], [], [])
+ end
+ end
+ end
+
+ describe "global index" do
+ before do
+ Dir.chdir(tmp) do
+ Bundler::Plugin.reset!
+ path = lib_path("gplugin")
+ index.register_plugin("gplugin", path.to_s, [path.join("lib").to_s], [], ["glb_source"], [])
+ end
+ end
+
+ it "skips sources" do
+ new_index = Index.new
+ expect(new_index.source_plugin("glb_source")).to be_falsy
+ end
+ end
+
+ describe "after conflict" do
+ let(:commands) { ["foo"] }
+ let(:sources) { ["bar"] }
+ let(:hooks) { ["hoook"] }
+
+ shared_examples "it cleans up" do
+ it "the path" do
+ expect(index.installed?("cplugin")).to be_falsy
+ end
+
+ it "the command" do
+ expect(index.command_plugin("xfoo")).to be_falsy
+ end
+
+ it "the source" do
+ expect(index.source_plugin("xbar")).to be_falsy
+ end
+
+ it "the hook" do
+ expect(index.hook_plugins("xhoook")).to be_empty
+ end
+ end
+
+ context "on command conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xhoook"])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+
+ context "on source conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xhoook"])
+ end.to raise_error(Index::SourceConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+
+ context "on command and source conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xhoook"])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+ end
+
+ describe "readonly disk without home" do
+ it "ignores being unable to create temp home dir" do
+ expect_any_instance_of(Bundler::Plugin::Index).to receive(:global_index_file).
+ and_raise(Bundler::GenericSystemCallError.new("foo", "bar"))
+ Bundler::Plugin::Index.new
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb
new file mode 100644
index 0000000000..f8bf8450c9
--- /dev/null
+++ b/spec/bundler/bundler/plugin/installer_spec.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Installer do
+ subject(:installer) { Bundler::Plugin::Installer.new }
+
+ before do
+ # allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(Pathname.new("/Gemfile"))
+ end
+
+ describe "cli install" do
+ it "uses Gem.sources when non of the source is provided" do
+ sources = double(:sources)
+ Bundler.settings # initialize it before we have to touch rubygems.ext_lock
+ allow(Bundler).to receive_message_chain("rubygems.sources") { sources }
+
+ allow(installer).to receive(:install_rubygems).
+ with("new-plugin", [">= 0"], sources).once
+
+ installer.install("new-plugin", {})
+ end
+
+ describe "with mocked installers" do
+ let(:spec) { double(:spec) }
+ it "returns the installed spec after installing git plugins" do
+ allow(installer).to receive(:install_git).
+ and_return("new-plugin" => spec)
+
+ expect(installer.install(["new-plugin"], :git => "https://some.ran/dom")).
+ to eq("new-plugin" => spec)
+ end
+
+ it "returns the installed spec after installing rubygems plugins" do
+ allow(installer).to receive(:install_rubygems).
+ and_return("new-plugin" => spec)
+
+ expect(installer.install(["new-plugin"], :source => "https://some.ran/dom")).
+ to eq("new-plugin" => spec)
+ end
+ end
+
+ describe "with actual installers" do
+ before do
+ build_repo2 do
+ build_plugin "re-plugin"
+ build_plugin "ma-plugin"
+ end
+ end
+
+ context "git plugins" do
+ before do
+ build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ s.write "plugins.rb"
+ end
+ end
+
+ let(:result) do
+ installer.install(["ga-plugin"], :git => "file://#{lib_path("ga-plugin")}")
+ end
+
+ it "returns the installed spec after installing" do
+ spec = result["ga-plugin"]
+ expect(spec.full_name).to eq "ga-plugin-1.0"
+ end
+
+ it "has expected full gem path" do
+ rev = revision_for(lib_path("ga-plugin"))
+ expect(result["ga-plugin"].full_gem_path).
+ to eq(Bundler::Plugin.root.join("bundler", "gems", "ga-plugin-#{rev[0..11]}").to_s)
+ end
+ end
+
+ context "rubygems plugins" do
+ let(:result) do
+ installer.install(["re-plugin"], :source => "file://#{gem_repo2}")
+ end
+
+ it "returns the installed spec after installing " do
+ expect(result["re-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ end
+
+ it "has expected full_gem)path" do
+ expect(result["re-plugin"].full_gem_path).
+ to eq(global_plugin_gem("re-plugin-1.0").to_s)
+ end
+ end
+
+ context "multiple plugins" do
+ let(:result) do
+ installer.install(["re-plugin", "ma-plugin"], :source => "file://#{gem_repo2}")
+ end
+
+ it "returns the installed spec after installing " do
+ expect(result["re-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ expect(result["ma-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ end
+
+ it "has expected full_gem)path" do
+ expect(result["re-plugin"].full_gem_path).to eq(global_plugin_gem("re-plugin-1.0").to_s)
+ expect(result["ma-plugin"].full_gem_path).to eq(global_plugin_gem("ma-plugin-1.0").to_s)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/source_list_spec.rb b/spec/bundler/bundler/plugin/source_list_spec.rb
new file mode 100644
index 0000000000..64a1233dd1
--- /dev/null
+++ b/spec/bundler/bundler/plugin/source_list_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::SourceList do
+ SourceList = Bundler::Plugin::SourceList
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "/" }
+ end
+
+ subject(:source_list) { SourceList.new }
+
+ describe "adding sources uses classes for plugin" do
+ it "uses Plugin::Installer::Rubygems for rubygems sources" do
+ source = source_list.
+ add_rubygems_source("remotes" => ["https://existing-rubygems.org"])
+ expect(source).to be_instance_of(Bundler::Plugin::Installer::Rubygems)
+ end
+
+ it "uses Plugin::Installer::Git for git sources" do
+ source = source_list.
+ add_git_source("uri" => "git://existing-git.org/path.git")
+ expect(source).to be_instance_of(Bundler::Plugin::Installer::Git)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
new file mode 100644
index 0000000000..9266fad1eb
--- /dev/null
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -0,0 +1,309 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin do
+ Plugin = Bundler::Plugin
+
+ let(:installer) { double(:installer) }
+ let(:index) { double(:index) }
+ let(:spec) { double(:spec) }
+ let(:spec2) { double(:spec2) }
+
+ before do
+ build_lib "new-plugin", :path => lib_path("new-plugin") do |s|
+ s.write "plugins.rb"
+ end
+
+ build_lib "another-plugin", :path => lib_path("another-plugin") do |s|
+ s.write "plugins.rb"
+ end
+
+ allow(spec).to receive(:full_gem_path).
+ and_return(lib_path("new-plugin").to_s)
+ allow(spec).to receive(:load_paths).
+ and_return([lib_path("new-plugin").join("lib").to_s])
+
+ allow(spec2).to receive(:full_gem_path).
+ and_return(lib_path("another-plugin").to_s)
+ allow(spec2).to receive(:load_paths).
+ and_return([lib_path("another-plugin").join("lib").to_s])
+
+ allow(Plugin::Installer).to receive(:new) { installer }
+ allow(Plugin).to receive(:index) { index }
+ allow(index).to receive(:register_plugin)
+ end
+
+ describe "install command" do
+ let(:opts) { { "version" => "~> 1.0", "source" => "foo" } }
+
+ before do
+ allow(installer).to receive(:install).with(["new-plugin"], opts) do
+ { "new-plugin" => spec }
+ end
+ end
+
+ it "passes the name and options to installer" do
+ allow(installer).to receive(:install).with(["new-plugin"], opts) do
+ { "new-plugin" => spec }
+ end.once
+
+ subject.install ["new-plugin"], opts
+ end
+
+ it "validates the installed plugin" do
+ allow(subject).
+ to receive(:validate_plugin!).with(lib_path("new-plugin")).once
+
+ subject.install ["new-plugin"], opts
+ end
+
+ it "registers the plugin with index" do
+ allow(index).to receive(:register_plugin).
+ with("new-plugin", lib_path("new-plugin").to_s, [lib_path("new-plugin").join("lib").to_s], []).once
+ subject.install ["new-plugin"], opts
+ end
+
+ context "multiple plugins" do
+ it do
+ allow(installer).to receive(:install).
+ with(["new-plugin", "another-plugin"], opts) do
+ {
+ "new-plugin" => spec,
+ "another-plugin" => spec2,
+ }
+ end.once
+
+ allow(subject).to receive(:validate_plugin!).twice
+ allow(index).to receive(:register_plugin).twice
+ subject.install ["new-plugin", "another-plugin"], opts
+ end
+ end
+ end
+
+ describe "evaluate gemfile for plugins" do
+ let(:definition) { double("definition") }
+ let(:builder) { double("builder") }
+ let(:gemfile) { bundled_app("Gemfile") }
+
+ before do
+ allow(Plugin::DSL).to receive(:new) { builder }
+ allow(builder).to receive(:eval_gemfile).with(gemfile)
+ allow(builder).to receive(:to_definition) { definition }
+ allow(builder).to receive(:inferred_plugins) { [] }
+ end
+
+ it "doesn't calls installer without any plugins" do
+ allow(definition).to receive(:dependencies) { [] }
+ allow(installer).to receive(:install_definition).never
+
+ subject.gemfile_install(gemfile)
+ end
+
+ context "with dependencies" do
+ let(:plugin_specs) do
+ {
+ "new-plugin" => spec,
+ "another-plugin" => spec2,
+ }
+ end
+
+ before do
+ allow(index).to receive(:installed?) { nil }
+ allow(definition).to receive(:dependencies) { [Bundler::Dependency.new("new-plugin", ">=0"), Bundler::Dependency.new("another-plugin", ">=0")] }
+ allow(installer).to receive(:install_definition) { plugin_specs }
+ end
+
+ it "should validate and register the plugins" do
+ expect(subject).to receive(:validate_plugin!).twice
+ expect(subject).to receive(:register_plugin).twice
+
+ subject.gemfile_install(gemfile)
+ end
+
+ it "should pass the optional plugins to #register_plugin" do
+ allow(builder).to receive(:inferred_plugins) { ["another-plugin"] }
+
+ expect(subject).to receive(:register_plugin).
+ with("new-plugin", spec, false).once
+
+ expect(subject).to receive(:register_plugin).
+ with("another-plugin", spec2, true).once
+
+ subject.gemfile_install(gemfile)
+ end
+ end
+ end
+
+ describe "#command?" do
+ it "returns true value for commands in index" do
+ allow(index).
+ to receive(:command_plugin).with("newcommand") { "my-plugin" }
+ result = subject.command? "newcommand"
+ expect(result).to be_truthy
+ end
+
+ it "returns false value for commands not in index" do
+ allow(index).to receive(:command_plugin).with("newcommand") { nil }
+ result = subject.command? "newcommand"
+ expect(result).to be_falsy
+ end
+ end
+
+ describe "#exec_command" do
+ it "raises UndefinedCommandError when command is not found" do
+ allow(index).to receive(:command_plugin).with("newcommand") { nil }
+ expect { subject.exec_command("newcommand", []) }.
+ to raise_error(Plugin::UndefinedCommandError)
+ end
+ end
+
+ describe "#source?" do
+ it "returns true value for sources in index" do
+ allow(index).
+ to receive(:command_plugin).with("foo-source") { "my-plugin" }
+ result = subject.command? "foo-source"
+ expect(result).to be_truthy
+ end
+
+ it "returns false value for source not in index" do
+ allow(index).to receive(:command_plugin).with("foo-source") { nil }
+ result = subject.command? "foo-source"
+ expect(result).to be_falsy
+ end
+ end
+
+ describe "#source" do
+ it "raises UnknownSourceError when source is not found" do
+ allow(index).to receive(:source_plugin).with("bar") { nil }
+ expect { subject.source("bar") }.
+ to raise_error(Plugin::UnknownSourceError)
+ end
+
+ it "loads the plugin, if not loaded" do
+ allow(index).to receive(:source_plugin).with("foo-bar") { "plugin_name" }
+
+ expect(subject).to receive(:load_plugin).with("plugin_name")
+ subject.source("foo-bar")
+ end
+
+ it "returns the class registered with #add_source" do
+ allow(index).to receive(:source_plugin).with("foo") { "plugin_name" }
+ stub_const "NewClass", Class.new
+
+ subject.add_source("foo", NewClass)
+ expect(subject.source("foo")).to be(NewClass)
+ end
+ end
+
+ describe "#source_from_lock" do
+ it "returns instance of registered class initialized with locked opts" do
+ opts = { "type" => "l_source", "remote" => "xyz", "other" => "random" }
+ allow(index).to receive(:source_plugin).with("l_source") { "plugin_name" }
+
+ stub_const "SClass", Class.new
+ s_instance = double(:s_instance)
+ subject.add_source("l_source", SClass)
+
+ expect(SClass).to receive(:new).
+ with(hash_including("type" => "l_source", "uri" => "xyz", "other" => "random")) { s_instance }
+ expect(subject.source_from_lock(opts)).to be(s_instance)
+ end
+ end
+
+ describe "#root" do
+ context "in app dir" do
+ before do
+ gemfile ""
+ end
+
+ it "returns plugin dir in app .bundle path" do
+ expect(subject.root).to eq(bundled_app.join(".bundle/plugin"))
+ end
+ end
+
+ context "outside app dir" do
+ it "returns plugin dir in global bundle path" do
+ Dir.chdir tmp
+ expect(subject.root).to eq(home.join(".bundle/plugin"))
+ end
+ end
+ end
+
+ describe "#add_hook" do
+ it "raises an ArgumentError on an unregistered event" do
+ ran = false
+ expect do
+ Plugin.add_hook("unregistered-hook") { ran = true }
+ end.to raise_error(ArgumentError)
+ expect(ran).to be(false)
+ end
+ end
+
+ describe "#hook" do
+ before do
+ path = lib_path("foo-plugin")
+ build_lib "foo-plugin", :path => path do |s|
+ s.write "plugins.rb", code
+ end
+
+ Bundler::Plugin::Events.send(:reset)
+ Bundler::Plugin::Events.send(:define, :EVENT_1, "event-1")
+ Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2")
+
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1).
+ and_return(["foo-plugin"])
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2).
+ and_return(["foo-plugin"])
+ allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path)
+ allow(index).to receive(:load_paths).with("foo-plugin").and_return([])
+ end
+
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" }
+ RUBY
+
+ it "raises an ArgumentError on an unregistered event" do
+ expect do
+ Plugin.hook("unregistered-hook")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "executes the hook" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1)
+ end.strip
+
+ expect(out).to eq("hook for event 1")
+ end
+
+ context "single plugin declaring more than one hook" do
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) {}
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_2) {}
+ puts "loaded"
+ RUBY
+
+ it "evals plugins.rb once" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1)
+ Plugin.hook(Bundler::Plugin::Events::EVENT_2)
+ end.strip
+
+ expect(out).to eq("loaded")
+ end
+ end
+
+ context "a block is passed" do
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) { |&blk| blk.call }
+ RUBY
+
+ it "is passed to the hook" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1) { puts "win" }
+ end.strip
+
+ expect(out).to eq("win")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/psyched_yaml_spec.rb b/spec/bundler/bundler/psyched_yaml_spec.rb
new file mode 100644
index 0000000000..d5d68c5cc3
--- /dev/null
+++ b/spec/bundler/bundler/psyched_yaml_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "bundler/psyched_yaml"
+
+RSpec.describe "Bundler::YamlLibrarySyntaxError" do
+ it "is raised on YAML parse errors" do
+ expect { YAML.parse "{foo" }.to raise_error(Bundler::YamlLibrarySyntaxError)
+ end
+end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
new file mode 100644
index 0000000000..8115e026d8
--- /dev/null
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -0,0 +1,187 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::RemoteSpecification do
+ let(:name) { "foo" }
+ let(:version) { Gem::Version.new("1.0.0") }
+ let(:platform) { Gem::Platform::RUBY }
+ let(:spec_fetcher) { double(:spec_fetcher) }
+
+ subject { described_class.new(name, version, platform, spec_fetcher) }
+
+ it "is Comparable" do
+ expect(described_class.ancestors).to include(Comparable)
+ end
+
+ it "can match platforms" do
+ expect(described_class.ancestors).to include(Bundler::MatchPlatform)
+ end
+
+ describe "#fetch_platform" do
+ let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+
+ before { allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec) }
+
+ it "should return the spec platform" do
+ expect(subject.fetch_platform).to eq("jruby")
+ end
+ end
+
+ describe "#full_name" do
+ context "when platform is ruby" do
+ it "should return the spec name and version" do
+ expect(subject.full_name).to eq("foo-1.0.0")
+ end
+ end
+
+ context "when platform is nil" do
+ let(:platform) { nil }
+
+ it "should return the spec name and version" do
+ expect(subject.full_name).to eq("foo-1.0.0")
+ end
+ end
+
+ context "when platform is a non-ruby platform" do
+ let(:platform) { "jruby" }
+
+ it "should return the spec name, version, and platform" do
+ expect(subject.full_name).to eq("foo-1.0.0-jruby")
+ end
+ end
+ end
+
+ describe "#<=>" do
+ let(:other_name) { name }
+ let(:other_version) { version }
+ let(:other_platform) { platform }
+ let(:other_spec_fetcher) { spec_fetcher }
+
+ shared_examples_for "a comparison" do
+ context "which exactly matches" do
+ it "returns 0" do
+ expect(subject <=> other).to eq(0)
+ end
+ end
+
+ context "which is different by name" do
+ let(:other_name) { "a" }
+ it "returns 1" do
+ expect(subject <=> other).to eq(1)
+ end
+ end
+
+ context "which has a lower version" do
+ let(:other_version) { Gem::Version.new("0.9.0") }
+ it "returns 1" do
+ expect(subject <=> other).to eq(1)
+ end
+ end
+
+ context "which has a higher version" do
+ let(:other_version) { Gem::Version.new("1.1.0") }
+ it "returns -1" do
+ expect(subject <=> other).to eq(-1)
+ end
+ end
+
+ context "which has a different platform" do
+ let(:other_platform) { Gem::Platform.new("x86-mswin32") }
+ it "returns -1" do
+ expect(subject <=> other).to eq(-1)
+ end
+ end
+ end
+
+ context "comparing another Bundler::RemoteSpecification" do
+ let(:other) do
+ Bundler::RemoteSpecification.new(other_name, other_version,
+ other_platform, nil)
+ end
+
+ it_should_behave_like "a comparison"
+ end
+
+ context "comparing a Gem::Specification" do
+ let(:other) do
+ Gem::Specification.new(other_name, other_version).tap do |s|
+ s.platform = other_platform
+ end
+ end
+
+ it_should_behave_like "a comparison"
+ end
+
+ context "comparing a non sortable object" do
+ let(:other) { Object.new }
+ let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+
+ before do
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ allow(remote_spec).to receive(:<=>).and_return(nil)
+ end
+
+ it "should use default object comparison" do
+ expect(subject <=> other).to eq(nil)
+ end
+ end
+ end
+
+ describe "#__swap__" do
+ let(:spec) { double(:spec, :dependencies => []) }
+ let(:new_spec) { double(:new_spec, :dependencies => [], :runtime_dependencies => []) }
+
+ before { subject.instance_variable_set(:@_remote_specification, spec) }
+
+ it "should replace remote specification with the passed spec" do
+ expect(subject.instance_variable_get(:@_remote_specification)).to be(spec)
+ subject.__swap__(new_spec)
+ expect(subject.instance_variable_get(:@_remote_specification)).to be(new_spec)
+ end
+ end
+
+ describe "#sort_obj" do
+ context "when platform is ruby" do
+ it "should return a sorting delegate array with name, version, and -1" do
+ expect(subject.sort_obj).to match_array(["foo", version, -1])
+ end
+ end
+
+ context "when platform is not ruby" do
+ let(:platform) { "jruby" }
+
+ it "should return a sorting delegate array with name, version, and 1" do
+ expect(subject.sort_obj).to match_array(["foo", version, 1])
+ end
+ end
+ end
+
+ describe "method missing" do
+ context "and is present in Gem::Specification" do
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+
+ before do
+ allow(subject).to receive(:_remote_specification).and_return(remote_spec)
+ expect(subject.methods.map(&:to_sym)).not_to include(:authors)
+ end
+
+ it "should send through to Gem::Specification" do
+ expect(subject.authors).to eq("abcd")
+ end
+ end
+ end
+
+ describe "respond to missing?" do
+ context "and is present in Gem::Specification" do
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+
+ before do
+ allow(subject).to receive(:_remote_specification).and_return(remote_spec)
+ expect(subject.methods.map(&:to_sym)).not_to include(:authors)
+ end
+
+ it "should send through to Gem::Specification" do
+ expect(subject.respond_to?(:authors)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/retry_spec.rb b/spec/bundler/bundler/retry_spec.rb
new file mode 100644
index 0000000000..b893580d72
--- /dev/null
+++ b/spec/bundler/bundler/retry_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Retry do
+ it "return successful result if no errors" do
+ attempts = 0
+ result = Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ :success
+ end
+ expect(result).to eq(:success)
+ expect(attempts).to eq(1)
+ end
+
+ it "returns the first valid result" do
+ jobs = [proc { raise "foo" }, proc { :bar }, proc { raise "foo" }]
+ attempts = 0
+ result = Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ jobs.shift.call
+ end
+ expect(result).to eq(:bar)
+ expect(attempts).to eq(2)
+ end
+
+ it "raises the last error" do
+ errors = [StandardError, StandardError, StandardError, Bundler::GemfileNotFound]
+ attempts = 0
+ expect do
+ Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ raise errors.shift
+ end
+ end.to raise_error(Bundler::GemfileNotFound)
+ expect(attempts).to eq(4)
+ end
+
+ it "raises exceptions" do
+ error = Bundler::GemfileNotFound
+ attempts = 0
+ expect do
+ Bundler::Retry.new(nil, error).attempt do
+ attempts += 1
+ raise error
+ end
+ end.to raise_error(error)
+ expect(attempts).to eq(1)
+ end
+
+ context "logging" do
+ let(:error) { Bundler::GemfileNotFound }
+ let(:failure_message) { "Retrying test due to error (2/2): #{error} #{error}" }
+
+ context "with debugging on" do
+ it "print error message with newline" do
+ allow(Bundler.ui).to receive(:debug?).and_return(true)
+ expect(Bundler.ui).to_not receive(:info)
+ expect(Bundler.ui).to receive(:warn).with(failure_message, true)
+
+ expect do
+ Bundler::Retry.new("test", [], 1).attempt do
+ raise error
+ end
+ end.to raise_error(error)
+ end
+ end
+
+ context "with debugging off" do
+ it "print error message with newlines" do
+ allow(Bundler.ui).to receive(:debug?).and_return(false)
+ expect(Bundler.ui).to receive(:info).with("").twice
+ expect(Bundler.ui).to receive(:warn).with(failure_message, false)
+
+ expect do
+ Bundler::Retry.new("test", [], 1).attempt do
+ raise error
+ end
+ end.to raise_error(error)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
new file mode 100644
index 0000000000..bc1ca98457
--- /dev/null
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require "bundler/ruby_dsl"
+
+RSpec.describe Bundler::RubyDsl do
+ class MockDSL
+ include Bundler::RubyDsl
+
+ attr_reader :ruby_version
+ end
+
+ let(:dsl) { MockDSL.new }
+ let(:ruby_version) { "2.0.0" }
+ let(:version) { "2.0.0" }
+ let(:engine) { "jruby" }
+ let(:engine_version) { "9000" }
+ let(:patchlevel) { "100" }
+ let(:options) do
+ { :patchlevel => patchlevel,
+ :engine => engine,
+ :engine_version => engine_version }
+ end
+
+ let(:invoke) do
+ proc do
+ args = Array(ruby_version) + [options]
+ dsl.ruby(*args)
+ end
+ end
+
+ subject do
+ invoke.call
+ dsl.ruby_version
+ end
+
+ describe "#ruby_version" do
+ shared_examples_for "it stores the ruby version" do
+ it "stores the version" do
+ expect(subject.versions).to eq(Array(ruby_version))
+ expect(subject.gem_version.version).to eq(version)
+ end
+
+ it "stores the engine details" do
+ expect(subject.engine).to eq(engine)
+ expect(subject.engine_versions).to eq(Array(engine_version))
+ end
+
+ it "stores the patchlevel" do
+ expect(subject.patchlevel).to eq(patchlevel)
+ end
+ end
+
+ context "with a plain version" do
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with a single requirement" do
+ let(:ruby_version) { ">= 2.0.0" }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with two requirements in the same string" do
+ let(:ruby_version) { ">= 2.0.0, < 3.0" }
+ it "raises an error" do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "with two requirements" do
+ let(:ruby_version) { ["~> 2.0.0", "> 2.0.1"] }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with multiple engine versions" do
+ let(:engine_version) { ["> 200", "< 300"] }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with no options hash" do
+ let(:invoke) { proc { dsl.ruby(ruby_version) } }
+
+ let(:patchlevel) { nil }
+ let(:engine) { "ruby" }
+ let(:engine_version) { version }
+
+ it_behaves_like "it stores the ruby version"
+
+ context "and with multiple requirements" do
+ let(:ruby_version) { ["~> 2.0.0", "> 2.0.1"] }
+ let(:engine_version) { ruby_version }
+ it_behaves_like "it stores the ruby version"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
new file mode 100644
index 0000000000..46a1b2918b
--- /dev/null
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -0,0 +1,524 @@
+# frozen_string_literal: true
+
+require "bundler/ruby_version"
+
+RSpec.describe "Bundler::RubyVersion and its subclasses" do
+ let(:version) { "2.0.0" }
+ let(:patchlevel) { "645" }
+ let(:engine) { "jruby" }
+ let(:engine_version) { "2.0.1" }
+
+ describe Bundler::RubyVersion do
+ subject { Bundler::RubyVersion.new(version, patchlevel, engine, engine_version) }
+
+ let(:ruby_version) { subject }
+ let(:other_version) { version }
+ let(:other_patchlevel) { patchlevel }
+ let(:other_engine) { engine }
+ let(:other_engine_version) { engine_version }
+ let(:other_ruby_version) { Bundler::RubyVersion.new(other_version, other_patchlevel, other_engine, other_engine_version) }
+
+ describe "#initialize" do
+ context "no engine is passed" do
+ let(:engine) { nil }
+
+ it "should set ruby as the engine" do
+ expect(subject.engine).to eq("ruby")
+ end
+ end
+
+ context "no engine_version is passed" do
+ let(:engine_version) { nil }
+
+ it "should set engine version as the passed version" do
+ expect(subject.engine_versions).to eq(["2.0.0"])
+ end
+ end
+
+ context "with engine in symbol" do
+ let(:engine) { :jruby }
+
+ it "should coerce engine to string" do
+ expect(subject.engine).to eq("jruby")
+ end
+ end
+
+ context "is called with multiple requirements" do
+ let(:version) { ["<= 2.0.0", "> 1.9.3"] }
+ let(:engine_version) { nil }
+
+ it "sets the versions" do
+ expect(subject.versions).to eq(version)
+ end
+
+ it "sets the engine versions" do
+ expect(subject.engine_versions).to eq(version)
+ end
+ end
+
+ context "is called with multiple engine requirements" do
+ let(:engine_version) { [">= 2.0", "< 2.3"] }
+
+ it "sets the engine versions" do
+ expect(subject.engine_versions).to eq(engine_version)
+ end
+ end
+ end
+
+ describe ".from_string" do
+ shared_examples_for "returning" do
+ it "returns the original RubyVersion" do
+ expect(described_class.from_string(subject.to_s)).to eq(subject)
+ end
+ end
+
+ include_examples "returning"
+
+ context "no patchlevel" do
+ let(:patchlevel) { nil }
+
+ include_examples "returning"
+ end
+
+ context "engine is ruby" do
+ let(:engine) { "ruby" }
+ let(:engine_version) { version }
+
+ include_examples "returning"
+ end
+
+ context "with multiple requirements" do
+ let(:engine_version) { ["> 9", "< 11"] }
+ let(:version) { ["> 8", "< 10"] }
+ let(:patchlevel) { nil }
+
+ it "returns nil" do
+ expect(described_class.from_string(subject.to_s)).to be_nil
+ end
+ end
+ end
+
+ describe "#to_s" do
+ it "should return info string with the ruby version, patchlevel, engine, and engine version" do
+ expect(subject.to_s).to eq("ruby 2.0.0p645 (jruby 2.0.1)")
+ end
+
+ context "no patchlevel" do
+ let(:patchlevel) { nil }
+
+ it "should return info string with the version, engine, and engine version" do
+ expect(subject.to_s).to eq("ruby 2.0.0 (jruby 2.0.1)")
+ end
+ end
+
+ context "engine is ruby" do
+ let(:engine) { "ruby" }
+
+ it "should return info string with the ruby version and patchlevel" do
+ expect(subject.to_s).to eq("ruby 2.0.0p645")
+ end
+ end
+
+ context "with multiple requirements" do
+ let(:engine_version) { ["> 9", "< 11"] }
+ let(:version) { ["> 8", "< 10"] }
+ let(:patchlevel) { nil }
+
+ it "should return info string with all requirements" do
+ expect(subject.to_s).to eq("ruby > 8, < 10 (jruby > 9, < 11)")
+ end
+ end
+ end
+
+ describe "#==" do
+ shared_examples_for "two ruby versions are not equal" do
+ it "should return false" do
+ expect(subject).to_not eq(other_ruby_version)
+ end
+ end
+
+ context "the versions, pathlevels, engines, and engine_versions match" do
+ it "should return true" do
+ expect(subject).to eq(other_ruby_version)
+ end
+ end
+
+ context "the versions do not match" do
+ let(:other_version) { "1.21.6" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the patchlevels do not match" do
+ let(:other_patchlevel) { "21" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the engines do not match" do
+ let(:other_engine) { "ruby" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the engine versions do not match" do
+ let(:other_engine_version) { "1.11.2" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+ end
+
+ describe "#host" do
+ before do
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_cpu").and_return("x86_64")
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_vendor").and_return("apple")
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("darwin14.5.0")
+ end
+
+ it "should return an info string with the host cpu, vendor, and os" do
+ expect(subject.host).to eq("x86_64-apple-darwin14.5.0")
+ end
+
+ it "memoizes the info string with the host cpu, vendor, and os" do
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_cpu").once.and_call_original
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_vendor").once.and_call_original
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_os").once.and_call_original
+ 2.times { ruby_version.host }
+ end
+ end
+
+ describe "#gem_version" do
+ let(:gem_version) { "2.0.0" }
+ let(:gem_version_obj) { Gem::Version.new(gem_version) }
+
+ shared_examples_for "it parses the version from the requirement string" do |version|
+ let(:version) { version }
+ it "should return the underlying version" do
+ expect(ruby_version.gem_version).to eq(gem_version_obj)
+ expect(ruby_version.gem_version.version).to eq(gem_version)
+ end
+ end
+
+ it_behaves_like "it parses the version from the requirement string", "2.0.0"
+ it_behaves_like "it parses the version from the requirement string", ">= 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "~> 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "< 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "= 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", ["> 2.0.0", "< 2.4.5"]
+ end
+
+ describe "#diff" do
+ let(:engine) { "ruby" }
+
+ shared_examples_for "there is a difference in the engines" do
+ it "should return a tuple with :engine and the two different engines" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:engine, engine, other_engine])
+ end
+ end
+
+ shared_examples_for "there is a difference in the versions" do
+ it "should return a tuple with :version and the two different versions" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:version, Array(version).join(", "), Array(other_version).join(", ")])
+ end
+ end
+
+ shared_examples_for "there is a difference in the engine versions" do
+ it "should return a tuple with :engine_version and the two different engine versions" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:engine_version, Array(engine_version).join(", "), Array(other_engine_version).join(", ")])
+ end
+ end
+
+ shared_examples_for "there is a difference in the patchlevels" do
+ it "should return a tuple with :patchlevel and the two different patchlevels" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:patchlevel, patchlevel, other_patchlevel])
+ end
+ end
+
+ shared_examples_for "there are no differences" do
+ it "should return nil" do
+ expect(ruby_version.diff(other_ruby_version)).to be_nil
+ end
+ end
+
+ context "all things match exactly" do
+ it_behaves_like "there are no differences"
+ end
+
+ context "detects engine discrepancies first" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine) { "rbx" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the engines"
+ end
+
+ context "detects version discrepancies second" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "detects version discrepancies with multiple requirements second" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ let(:version) { ["> 2.0.0", "< 1.0.0"] }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "detects engine version discrepancies third" do
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "detects engine version discrepancies with multiple requirements third" do
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ let(:engine_version) { ["> 2.0.0", "< 1.0.0"] }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "detects patchlevel discrepancies last" do
+ let(:other_patchlevel) { "643" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+
+ context "successfully matches gem requirements" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "successfully matches multiple gem requirements" do
+ let(:version) { [">= 2.0.0", "< 2.4.5"] }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { ["~> 2.0.1", "< 2.4.5"] }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "successfully detects bad gem requirements with versions with multiple requirements" do
+ let(:version) { ["~> 2.0.0", "< 2.0.5"] }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.5" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "successfully detects bad gem requirements with versions" do
+ let(:version) { "~> 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.1.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "successfully detects bad gem requirements with patchlevels" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "645" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+
+ context "successfully detects bad gem requirements with engine versions" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.1.0" }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "with a patchlevel of -1" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "-1" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { version }
+ let(:other_engine) { engine }
+ let(:other_engine_version) { engine_version }
+
+ context "and comparing with another patchlevel of -1" do
+ let(:other_patchlevel) { patchlevel }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "and comparing with a patchlevel that is not -1" do
+ let(:other_patchlevel) { "642" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+ end
+ end
+
+ describe "#system" do
+ subject { Bundler::RubyVersion.system }
+
+ let(:bundler_system_ruby_version) { subject }
+
+ before do
+ Bundler::RubyVersion.instance_variable_set("@ruby_version", nil)
+ end
+
+ it "should return an instance of Bundler::RubyVersion" do
+ expect(subject).to be_kind_of(Bundler::RubyVersion)
+ end
+
+ it "memoizes the instance of Bundler::RubyVersion" do
+ expect(Bundler::RubyVersion).to receive(:new).once.and_call_original
+ 2.times { subject }
+ end
+
+ describe "#version" do
+ it "should return a copy of the value of RUBY_VERSION" do
+ expect(subject.versions).to eq([RUBY_VERSION])
+ expect(subject.versions.first).to_not be(RUBY_VERSION)
+ end
+ end
+
+ describe "#engine" do
+ context "RUBY_ENGINE is defined" do
+ before { stub_const("RUBY_ENGINE", "jruby") }
+ before { stub_const("JRUBY_VERSION", "2.1.1") }
+
+ it "should return a copy of the value of RUBY_ENGINE" do
+ expect(subject.engine).to eq("jruby")
+ expect(subject.engine).to_not be(RUBY_ENGINE)
+ end
+ end
+
+ context "RUBY_ENGINE is not defined" do
+ before { stub_const("RUBY_ENGINE", nil) }
+
+ it "should return the string 'ruby'" do
+ expect(subject.engine).to eq("ruby")
+ end
+ end
+ end
+
+ describe "#engine_version" do
+ context "engine is ruby" do
+ before do
+ stub_const("RUBY_VERSION", "2.2.4")
+ stub_const("RUBY_ENGINE", "ruby")
+ end
+
+ it "should return a copy of the value of RUBY_VERSION" do
+ expect(bundler_system_ruby_version.engine_versions).to eq(["2.2.4"])
+ expect(bundler_system_ruby_version.engine_versions.first).to_not be(RUBY_VERSION)
+ end
+ end
+
+ context "engine is rbx" do
+ before do
+ stub_const("RUBY_ENGINE", "rbx")
+ stub_const("Rubinius::VERSION", "2.0.0")
+ end
+
+ it "should return a copy of the value of Rubinius::VERSION" do
+ expect(bundler_system_ruby_version.engine_versions).to eq(["2.0.0"])
+ expect(bundler_system_ruby_version.engine_versions.first).to_not be(Rubinius::VERSION)
+ end
+ end
+
+ context "engine is jruby" do
+ before do
+ stub_const("RUBY_ENGINE", "jruby")
+ stub_const("JRUBY_VERSION", "2.1.1")
+ end
+
+ it "should return a copy of the value of JRUBY_VERSION" do
+ expect(subject.engine_versions).to eq(["2.1.1"])
+ expect(bundler_system_ruby_version.engine_versions.first).to_not be(JRUBY_VERSION)
+ end
+ end
+
+ context "engine is some other ruby engine" do
+ before do
+ stub_const("RUBY_ENGINE", "not_supported_ruby_engine")
+ stub_const("RUBY_ENGINE_VERSION", "1.2.3")
+ end
+
+ it "returns RUBY_ENGINE_VERSION" do
+ expect(bundler_system_ruby_version.engine_versions).to eq(["1.2.3"])
+ end
+ end
+ end
+
+ describe "#patchlevel" do
+ it "should return a string with the value of RUBY_PATCHLEVEL" do
+ expect(subject.patchlevel).to eq(RUBY_PATCHLEVEL.to_s)
+ end
+ end
+ end
+
+ describe "#to_gem_version_with_patchlevel" do
+ shared_examples_for "the patchlevel is omitted" do
+ it "does not include a patch level" do
+ expect(subject.to_gem_version_with_patchlevel.to_s).to eq(version)
+ end
+ end
+
+ context "with nil patch number" do
+ let(:patchlevel) { nil }
+
+ it_behaves_like "the patchlevel is omitted"
+ end
+
+ context "with negative patch number" do
+ let(:patchlevel) { -1 }
+
+ it_behaves_like "the patchlevel is omitted"
+ end
+
+ context "with a valid patch number" do
+ it "uses the specified patchlevel as patchlevel" do
+ expect(subject.to_gem_version_with_patchlevel.to_s).to eq("#{version}.#{patchlevel}")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
new file mode 100644
index 0000000000..b1b15d9e5d
--- /dev/null
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::RubygemsIntegration do
+ it "uses the same chdir lock as rubygems", :rubygems => "2.1" do
+ expect(Bundler.rubygems.ext_lock).to eq(Gem::Ext::Builder::CHDIR_MONITOR)
+ end
+
+ context "#validate" do
+ let(:spec) do
+ Gem::Specification.new do |s|
+ s.name = "to-validate"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ end
+ end
+ subject { Bundler.rubygems.validate(spec) }
+
+ it "skips overly-strict gemspec validation", :rubygems => "< 1.7" do
+ expect(spec).to_not receive(:validate)
+ subject
+ end
+
+ it "validates with packaging mode disabled", :rubygems => "1.7" do
+ expect(spec).to receive(:validate).with(false)
+ subject
+ end
+
+ it "should set a summary to avoid an overly-strict error", :rubygems => "~> 1.7.0" do
+ spec.summary = nil
+ expect { subject }.not_to raise_error
+ expect(spec.summary).to eq("")
+ end
+
+ context "with an invalid spec" do
+ before do
+ expect(spec).to receive(:validate).with(false).
+ and_raise(Gem::InvalidSpecificationException.new("TODO is not an author"))
+ end
+
+ it "should raise a Gem::InvalidSpecificationException and produce a helpful warning message",
+ :rubygems => "1.7" do
+ expect { subject }.to raise_error(Gem::InvalidSpecificationException,
+ "The gemspec at #{__FILE__} is not valid. "\
+ "Please fix this gemspec.\nThe validation error was 'TODO is not an author'\n")
+ end
+ end
+ end
+
+ describe "#configuration" do
+ it "handles Gem::SystemExitException errors" do
+ allow(Gem).to receive(:configuration) { raise Gem::SystemExitException.new(1) }
+ expect { Bundler.rubygems.configuration }.to raise_error(Gem::SystemExitException)
+ end
+ end
+
+ describe "#download_gem", :rubygems => ">= 2.0" do
+ let(:bundler_retry) { double(Bundler::Retry) }
+ let(:retry) { double("Bundler::Retry") }
+ let(:uri) { URI.parse("https://foo.bar") }
+ let(:path) { Gem.path.first }
+ let(:spec) do
+ spec = Bundler::RemoteSpecification.new("Foo", Gem::Version.new("2.5.2"),
+ Gem::Platform::RUBY, nil)
+ spec.remote = Bundler::Source::Rubygems::Remote.new(uri.to_s)
+ spec
+ end
+ let(:fetcher) { double("gem_remote_fetcher") }
+
+ it "successfully downloads gem with retries" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).and_return(fetcher)
+ expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "https://foo.bar")
+ expect(Bundler::Retry).to receive(:new).with("download gem from #{uri}/").
+ and_return(bundler_retry)
+ expect(bundler_retry).to receive(:attempts).and_yield
+ expect(fetcher).to receive(:download).with(spec, uri, path)
+
+ Bundler.rubygems.download_gem(spec, uri, path)
+ end
+ end
+
+ describe "#fetch_all_remote_specs", :rubygems => ">= 2.0" do
+ let(:uri) { URI("https://example.com") }
+ let(:fetcher) { double("gem_remote_fetcher") }
+ let(:specs_response) { Marshal.dump(["specs"]) }
+ let(:prerelease_specs_response) { Marshal.dump(["prerelease_specs"]) }
+
+ context "when a rubygems source mirror is set" do
+ let(:orig_uri) { URI("http://zombo.com") }
+ let(:remote_with_mirror) { double("remote", :uri => uri, :original_uri => orig_uri) }
+
+ it "sets the 'X-Gemfile-Source' header containing the original source" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
+ expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "http://zombo.com").twice
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
+ expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror)
+ expect(result).to eq(%w[specs prerelease_specs])
+ end
+ end
+
+ context "when there is no rubygems source mirror set" do
+ let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+
+ it "does not set the 'X-Gemfile-Source' header" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
+ expect(fetcher).to_not receive(:headers=)
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
+ expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror)
+ expect(result).to eq(%w[specs prerelease_specs])
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/settings/validator_spec.rb b/spec/bundler/bundler/settings/validator_spec.rb
new file mode 100644
index 0000000000..e4ffd89435
--- /dev/null
+++ b/spec/bundler/bundler/settings/validator_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Settings::Validator do
+ describe ".validate!" do
+ def validate!(key, value, settings)
+ transformed_key = Bundler.settings.key_for(key)
+ if value.nil?
+ settings.delete(transformed_key)
+ else
+ settings[transformed_key] = value
+ end
+ described_class.validate!(key, value, settings)
+ settings
+ end
+
+ it "path and path.system are mutually exclusive" do
+ expect(validate!("path", "bundle", {})).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path", nil, {})).to eq({})
+
+ expect(validate!("path.system", true, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path.system", false, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle", "BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path.system", nil, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path.system", true, {})).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path.system", false, {})).to eq("BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path.system", nil, {})).to eq({})
+ end
+
+ it "a group cannot be in both `with` & `without` simultaneously" do
+ expect do
+ validate!("with", "", {})
+ validate!("with", nil, {})
+ validate!("with", "", "BUNDLE_WITHOUT" => "a")
+ validate!("with", nil, "BUNDLE_WITHOUT" => "a")
+ validate!("with", "b:c", "BUNDLE_WITHOUT" => "a")
+
+ validate!("without", "", {})
+ validate!("without", nil, {})
+ validate!("without", "", "BUNDLE_WITH" => "a")
+ validate!("without", nil, "BUNDLE_WITH" => "a")
+ validate!("without", "b:c", "BUNDLE_WITH" => "a")
+ end.not_to raise_error
+
+ expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `with` to "b:c" failed:
+ - a group cannot be in both `with` & `without` simultaneously
+ - `without` is current set to [:c, :d]
+ - the `c` groups conflict
+ EOS
+
+ expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `without` to "b:c" failed:
+ - a group cannot be in both `with` & `without` simultaneously
+ - `with` is current set to [:c, :d]
+ - the `c` groups conflict
+ EOS
+ end
+ end
+
+ describe described_class::Rule do
+ let(:keys) { %w[key] }
+ let(:description) { "rule description" }
+ let(:validate) { proc { raise "validate called!" } }
+ subject(:rule) { described_class.new(keys, description, &validate) }
+
+ describe "#validate!" do
+ it "calls the block" do
+ expect { rule.validate!("key", nil, {}) }.to raise_error(RuntimeError, /validate called!/)
+ end
+ end
+
+ describe "#fail!" do
+ it "raises with a helpful message" do
+ expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `key` to "value" failed:
+ - rule description
+ - reason1
+ - reason2
+ EOS
+ end
+ end
+
+ describe "#set" do
+ it "works when the value has not changed" do
+ allow(Bundler.ui).to receive(:info).never
+
+ subject.set({}, "key", nil)
+ subject.set({ "BUNDLE_KEY" => "value" }, "key", "value")
+ end
+
+ it "prints out when the value is changing" do
+ settings = {}
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value\", since rule description, reason1")
+ subject.set(settings, "key", "value", "reason1")
+ expect(settings).to eq("BUNDLE_KEY" => "value")
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value2\", since rule description, reason2")
+ subject.set(settings, "key", "value2", "reason2")
+ expect(settings).to eq("BUNDLE_KEY" => "value2")
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to nil, since rule description, reason3")
+ subject.set(settings, "key", nil, "reason3")
+ expect(settings).to eq({})
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
new file mode 100644
index 0000000000..1a31493e20
--- /dev/null
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -0,0 +1,326 @@
+# frozen_string_literal: true
+
+require "bundler/settings"
+
+RSpec.describe Bundler::Settings do
+ subject(:settings) { described_class.new(bundled_app) }
+
+ describe "#set_local" do
+ context "when the local config file is not found" do
+ subject(:settings) { described_class.new(nil) }
+
+ it "raises a GemfileNotFound error with explanation" do
+ expect { subject.set_local("foo", "bar") }.
+ to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile")
+ end
+ end
+ end
+
+ describe "load_config" do
+ let(:hash) do
+ {
+ "build.thrift" => "--with-cppflags=-D_FORTIFY_SOURCE=0",
+ "build.libv8" => "--with-system-v8",
+ "build.therubyracer" => "--with-v8-dir",
+ "build.pg" => "--with-pg-config=/usr/local/Cellar/postgresql92/9.2.8_1/bin/pg_config",
+ "gem.coc" => "false",
+ "gem.mit" => "false",
+ "gem.test" => "minitest",
+ "thingy" => <<-EOS.tr("\n", " "),
+--asdf --fdsa --ty=oh man i hope this doesnt break bundler because
+that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
+--very-important-option=DontDeleteRoo
+--very-important-option=DontDeleteRoo
+--very-important-option=DontDeleteRoo
+--very-important-option=DontDeleteRoo
+ EOS
+ "xyz" => "zyx",
+ }
+ end
+
+ before do
+ hash.each do |key, value|
+ settings.set_local key, value
+ end
+ end
+
+ it "can load the config" do
+ loaded = settings.send(:load_config, bundled_app("config"))
+ expected = Hash[hash.map do |k, v|
+ [settings.send(:key_for, k), v.to_s]
+ end]
+ expect(loaded).to eq(expected)
+ end
+
+ context "when BUNDLE_IGNORE_CONFIG is set" do
+ before { ENV["BUNDLE_IGNORE_CONFIG"] = "TRUE" }
+
+ it "ignores the config" do
+ loaded = settings.send(:load_config, bundled_app("config"))
+ expect(loaded).to eq({})
+ end
+ end
+ end
+
+ describe "#global_config_file" do
+ context "when $HOME is not accessible" do
+ context "when $TMPDIR is not writable" do
+ it "does not raise" do
+ expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil)
+ expect(FileUtils).to receive(:mkpath).twice.with(File.join(Dir.tmpdir, "bundler", "home")).and_raise(Errno::EROFS, "Read-only file system @ dir_s_mkdir - /tmp/bundler")
+
+ expect(subject.send(:global_config_file)).to be_nil
+ end
+ end
+ end
+ end
+
+ describe "#[]" do
+ context "when the local config file is not found" do
+ subject(:settings) { described_class.new }
+
+ it "does not raise" do
+ expect do
+ subject["foo"]
+ end.not_to raise_error
+ end
+ end
+
+ context "when not set" do
+ context "when default value present" do
+ it "retrieves value" do
+ expect(settings[:retry]).to be 3
+ end
+ end
+
+ it "returns nil" do
+ expect(settings[:buttermilk]).to be nil
+ end
+ end
+
+ context "when is boolean" do
+ it "returns a boolean" do
+ settings.set_local :frozen, "true"
+ expect(settings[:frozen]).to be true
+ end
+ context "when specific gem is configured" do
+ it "returns a boolean" do
+ settings.set_local "ignore_messages.foobar", "true"
+ expect(settings["ignore_messages.foobar"]).to be true
+ end
+ end
+ end
+
+ context "when is number" do
+ it "returns a number" do
+ settings.set_local :ssl_verify_mode, "1"
+ expect(settings[:ssl_verify_mode]).to be 1
+ end
+ end
+
+ context "when it's not possible to write to the file" do
+ it "raises an PermissionError with explanation" do
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:local_config_file).dirname).
+ and_raise(Errno::EACCES)
+ expect { settings.set_local :frozen, "1" }.
+ to raise_error(Bundler::PermissionError, /config/)
+ end
+ end
+ end
+
+ describe "#temporary" do
+ it "reset after used" do
+ Bundler.settings.set_local :no_install, true
+
+ Bundler.settings.temporary(:no_install => false) do
+ expect(Bundler.settings[:no_install]).to eq false
+ end
+
+ expect(Bundler.settings[:no_install]).to eq true
+ end
+
+ it "returns the return value of the block" do
+ ret = Bundler.settings.temporary({}) { :ret }
+ expect(ret).to eq :ret
+ end
+
+ context "when called without a block" do
+ it "leaves the setting changed" do
+ Bundler.settings.temporary(:foo => :random)
+ expect(Bundler.settings[:foo]).to eq "random"
+ end
+
+ it "returns nil" do
+ expect(Bundler.settings.temporary(:foo => :bar)).to be_nil
+ end
+ end
+ end
+
+ describe "#set_global" do
+ context "when it's not possible to write to the file" do
+ it "raises an PermissionError with explanation" do
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:global_config_file).dirname).
+ and_raise(Errno::EACCES)
+ expect { settings.set_global(:frozen, "1") }.
+ to raise_error(Bundler::PermissionError, %r{\.bundle/config})
+ end
+ end
+ end
+
+ describe "#pretty_values_for" do
+ it "prints the converted value rather than the raw string" do
+ bool_key = described_class::BOOL_KEYS.first
+ settings.set_local(bool_key, "false")
+ expect(subject.pretty_values_for(bool_key)).to eq [
+ "Set for your local app (#{bundled_app("config")}): false",
+ ]
+ end
+ end
+
+ describe "#mirror_for" do
+ let(:uri) { URI("https://rubygems.org/") }
+
+ context "with no configured mirror" do
+ it "returns the original URI" do
+ expect(settings.mirror_for(uri)).to eq(uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(uri)
+ end
+ end
+
+ context "with a configured mirror" do
+ let(:mirror_uri) { URI("https://rubygems-mirror.org/") }
+
+ before { settings.set_local "mirror.https://rubygems.org/", mirror_uri.to_s }
+
+ it "returns the mirror URI" do
+ expect(settings.mirror_for(uri)).to eq(mirror_uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(mirror_uri)
+ end
+
+ it "normalizes the URI" do
+ expect(settings.mirror_for("https://rubygems.org")).to eq(mirror_uri)
+ end
+
+ it "is case insensitive" do
+ expect(settings.mirror_for("HTTPS://RUBYGEMS.ORG/")).to eq(mirror_uri)
+ end
+
+ context "with a file URI" do
+ let(:mirror_uri) { URI("file:/foo/BAR/baz/qUx/") }
+
+ it "returns the mirror URI" do
+ expect(settings.mirror_for(uri)).to eq(mirror_uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("file:/foo/BAR/baz/qUx/")).to eq(mirror_uri)
+ end
+
+ it "normalizes the URI" do
+ expect(settings.mirror_for("file:/foo/BAR/baz/qUx")).to eq(mirror_uri)
+ end
+ end
+ end
+ end
+
+ describe "#credentials_for" do
+ let(:uri) { URI("https://gemserver.example.org/") }
+ let(:credentials) { "username:password" }
+
+ context "with no configured credentials" do
+ it "returns nil" do
+ expect(settings.credentials_for(uri)).to be_nil
+ end
+ end
+
+ context "with credentials configured by URL" do
+ before { settings.set_local "https://gemserver.example.org/", credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+
+ context "with credentials configured by hostname" do
+ before { settings.set_local "gemserver.example.org", credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+ end
+
+ describe "URI normalization" do
+ it "normalizes HTTP URIs in credentials configuration" do
+ settings.set_local "http://gemserver.example.org", "username:password"
+ expect(settings.all).to include("http://gemserver.example.org/")
+ end
+
+ it "normalizes HTTPS URIs in credentials configuration" do
+ settings.set_local "https://gemserver.example.org", "username:password"
+ expect(settings.all).to include("https://gemserver.example.org/")
+ end
+
+ it "normalizes HTTP URIs in mirror configuration" do
+ settings.set_local "mirror.http://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.all).to include("mirror.http://rubygems.org/")
+ end
+
+ it "normalizes HTTPS URIs in mirror configuration" do
+ settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.all).to include("mirror.https://rubygems.org/")
+ end
+
+ it "does not normalize other config keys that happen to contain 'http'" do
+ settings.set_local "local.httparty", home("httparty")
+ expect(settings.all).to include("local.httparty")
+ end
+
+ it "does not normalize other config keys that happen to contain 'https'" do
+ settings.set_local "local.httpsmarty", home("httpsmarty")
+ expect(settings.all).to include("local.httpsmarty")
+ end
+
+ it "reads older keys without trailing slashes" do
+ settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(
+ URI("http://rubygems-mirror.org/")
+ )
+ end
+
+ it "normalizes URIs with a fallback_timeout option" do
+ settings.set_local "mirror.https://rubygems.org/.fallback_timeout", "true"
+ expect(settings.all).to include("mirror.https://rubygems.org/.fallback_timeout")
+ end
+
+ it "normalizes URIs with a fallback_timeout option without a trailing slash" do
+ settings.set_local "mirror.https://rubygems.org.fallback_timeout", "true"
+ expect(settings.all).to include("mirror.https://rubygems.org/.fallback_timeout")
+ end
+ end
+
+ describe "BUNDLE_ keys format" do
+ let(:settings) { described_class.new(bundled_app(".bundle")) }
+
+ it "converts older keys without double dashes" do
+ config("BUNDLE_MY__PERSONAL.RACK" => "~/Work/git/rack")
+ expect(settings["my.personal.rack"]).to eq("~/Work/git/rack")
+ end
+
+ it "converts older keys without trailing slashes and double dashes" do
+ config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://rubygems-mirror.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ end
+
+ it "reads newer keys format properly" do
+ config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
new file mode 100644
index 0000000000..b66c43fd92
--- /dev/null
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -0,0 +1,504 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SharedHelpers do
+ let(:ext_lock_double) { double(:ext_lock) }
+
+ before do
+ allow(Bundler.rubygems).to receive(:ext_lock).and_return(ext_lock_double)
+ allow(ext_lock_double).to receive(:synchronize) {|&block| block.call }
+ end
+
+ subject { Bundler::SharedHelpers }
+
+ describe "#default_gemfile" do
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
+ context "Gemfile is present" do
+ let(:expected_gemfile_path) { Pathname.new("/path/Gemfile") }
+
+ it "returns the Gemfile path" do
+ expect(subject.default_gemfile).to eq(expected_gemfile_path)
+ end
+ end
+
+ context "Gemfile is not present" do
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
+ it "raises a GemfileNotFound error" do
+ expect { subject.default_gemfile }.to raise_error(
+ Bundler::GemfileNotFound, "Could not locate Gemfile"
+ )
+ end
+ end
+
+ context "Gemfile is not an absolute path" do
+ before { ENV["BUNDLE_GEMFILE"] = "Gemfile" }
+
+ let(:expected_gemfile_path) { Pathname.new("Gemfile").expand_path }
+
+ it "returns the Gemfile path" do
+ expect(subject.default_gemfile).to eq(expected_gemfile_path)
+ end
+ end
+ end
+
+ describe "#default_lockfile" do
+ context "gemfile is gems.rb" do
+ let(:gemfile_path) { Pathname.new("/path/gems.rb") }
+ let(:expected_lockfile_path) { Pathname.new("/path/gems.locked") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
+ it "returns the gems.locked path" do
+ expect(subject.default_lockfile).to eq(expected_lockfile_path)
+ end
+ end
+
+ context "is a regular Gemfile" do
+ let(:gemfile_path) { Pathname.new("/path/Gemfile") }
+ let(:expected_lockfile_path) { Pathname.new("/path/Gemfile.lock") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
+ it "returns the lock file path" do
+ expect(subject.default_lockfile).to eq(expected_lockfile_path)
+ end
+ end
+ end
+
+ describe "#default_bundle_dir" do
+ context ".bundle does not exist" do
+ it "returns nil" do
+ expect(subject.default_bundle_dir).to be_nil
+ end
+ end
+
+ context ".bundle is global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("#{bundled_app}") }
+
+ before do
+ Dir.mkdir ".bundle"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
+ end
+
+ it "returns nil" do
+ expect(subject.default_bundle_dir).to be_nil
+ end
+ end
+
+ context ".bundle is not global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("/path/rubygems") }
+ let(:expected_bundle_dir_path) { Pathname.new("#{bundled_app}/.bundle") }
+
+ before do
+ Dir.mkdir ".bundle"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
+ end
+
+ it "returns the .bundle path" do
+ expect(subject.default_bundle_dir).to eq(expected_bundle_dir_path)
+ end
+ end
+ end
+
+ describe "#in_bundle?" do
+ it "calls the find_gemfile method" do
+ expect(subject).to receive(:find_gemfile)
+ subject.in_bundle?
+ end
+
+ shared_examples_for "correctly determines whether to return a Gemfile path" do
+ context "currently in directory with a Gemfile" do
+ before { File.new("Gemfile", "w") }
+
+ it "returns path of the bundle Gemfile" do
+ expect(subject.in_bundle?).to eq("#{bundled_app}/Gemfile")
+ end
+ end
+
+ context "currently in directory without a Gemfile" do
+ it "returns nil" do
+ expect(subject.in_bundle?).to be_nil
+ end
+ end
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] set" do
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
+ it "returns ENV['BUNDLE_GEMFILE']" do
+ expect(subject.in_bundle?).to eq("/path/Gemfile")
+ end
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] not set" do
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
+ it_behaves_like "correctly determines whether to return a Gemfile path"
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] is blank" do
+ before { ENV["BUNDLE_GEMFILE"] = "" }
+
+ it_behaves_like "correctly determines whether to return a Gemfile path"
+ end
+ end
+
+ describe "#chdir" do
+ let(:op_block) { proc { Dir.mkdir "nested_dir" } }
+
+ before { Dir.mkdir "chdir_test_dir" }
+
+ it "executes the passed block while in the specified directory" do
+ subject.chdir("chdir_test_dir", &op_block)
+ expect(Pathname.new("chdir_test_dir/nested_dir")).to exist
+ end
+ end
+
+ describe "#pwd" do
+ it "returns the current absolute path" do
+ expect(subject.pwd).to eq(bundled_app)
+ end
+ end
+
+ describe "#with_clean_git_env" do
+ let(:with_clean_git_env_block) { proc { Dir.mkdir "with_clean_git_env_test_dir" } }
+
+ before do
+ ENV["GIT_DIR"] = "ORIGINAL_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "ORIGINAL_ENV_GIT_WORK_TREE"
+ end
+
+ it "executes the passed block" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(Pathname.new("with_clean_git_env_test_dir")).to exist
+ end
+
+ context "when a block is passed" do
+ let(:with_clean_git_env_block) do
+ proc do
+ Dir.mkdir "git_dir_test_dir" unless ENV["GIT_DIR"].nil?
+ Dir.mkdir "git_work_tree_test_dir" unless ENV["GIT_WORK_TREE"].nil?
+ end end
+
+ it "uses a fresh git env for execution" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(Pathname.new("git_dir_test_dir")).to_not exist
+ expect(Pathname.new("git_work_tree_test_dir")).to_not exist
+ end
+ end
+
+ context "passed block does not throw errors" do
+ let(:with_clean_git_env_block) do
+ proc do
+ ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
+ end end
+
+ it "restores the git env after" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
+ expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
+ end
+ end
+
+ context "passed block throws errors" do
+ let(:with_clean_git_env_block) do
+ proc do
+ ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
+ raise RuntimeError.new
+ end end
+
+ it "restores the git env after" do
+ expect { subject.with_clean_git_env(&with_clean_git_env_block) }.to raise_error(RuntimeError)
+ expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
+ expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
+ end
+ end
+ end
+
+ describe "#set_bundle_environment" do
+ before do
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ end
+
+ shared_examples_for "ENV['PATH'] gets set correctly" do
+ before { Dir.mkdir ".bundle" }
+
+ it "ensures bundle bin path is in ENV['PATH']" do
+ subject.set_bundle_environment
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
+ expect(paths).to include("#{Bundler.bundle_path}/bin")
+ end
+ end
+
+ shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
+ it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
+ subject.set_bundle_environment
+ expect(ENV["RUBYOPT"].split(" ")).to start_with("-rbundler/setup")
+ end
+ end
+
+ shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
+ before do
+ allow(Bundler::SharedHelpers).to receive(:bundler_ruby_lib).and_return(ruby_lib_path)
+ end
+
+ it "ensures bundler's ruby version lib path is in ENV['RUBYLIB']" do
+ subject.set_bundle_environment
+ paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
+ expect(paths).to include(ruby_lib_path)
+ end
+ end
+
+ it "calls the appropriate set methods" do
+ expect(subject).to receive(:set_path)
+ expect(subject).to receive(:set_rubyopt)
+ expect(subject).to receive(:set_rubylib)
+ subject.set_bundle_environment
+ end
+
+ it "exits if bundle path contains the unix-like path separator" do
+ if Gem.respond_to?(:path_separator)
+ allow(Gem).to receive(:path_separator).and_return(":")
+ else
+ stub_const("File::PATH_SEPARATOR", ":".freeze)
+ end
+ allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") }
+ expect { subject.send(:validate_bundle_path) }.to raise_error(
+ Bundler::PathError,
+ "Your bundle path contains text matching \":\", which is the " \
+ "path separator for your system. Bundler cannot " \
+ "function correctly when the Bundle path contains the " \
+ "system's PATH separator. Please change your " \
+ "bundle path to not match \":\".\nYour current bundle " \
+ "path is '#{Bundler.bundle_path}'."
+ )
+ end
+
+ context "with a jruby path_separator regex", :ruby => "1.9" do
+ # In versions of jruby that supported ruby 1.8, the path separator was the standard File::PATH_SEPARATOR
+ let(:regex) { Regexp.new("(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):") }
+ it "does not exit if bundle path is the standard uri path" do
+ allow(Bundler.rubygems).to receive(:path_separator).and_return(regex)
+ allow(Bundler).to receive(:bundle_path) { Pathname.new("uri:classloader:/WEB-INF/gems") }
+ expect { subject.send(:validate_bundle_path) }.not_to raise_error
+ end
+
+ it "exits if bundle path contains another directory" do
+ allow(Bundler.rubygems).to receive(:path_separator).and_return(regex)
+ allow(Bundler).to receive(:bundle_path) {
+ Pathname.new("uri:classloader:/WEB-INF/gems:other/dir")
+ }
+
+ expect { subject.send(:validate_bundle_path) }.to raise_error(
+ Bundler::PathError,
+ "Your bundle path contains text matching " \
+ "/(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):/, which is the " \
+ "path separator for your system. Bundler cannot " \
+ "function correctly when the Bundle path contains the " \
+ "system's PATH separator. Please change your " \
+ "bundle path to not match " \
+ "/(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):/." \
+ "\nYour current bundle path is '#{Bundler.bundle_path}'."
+ )
+ end
+ end
+
+ context "ENV['PATH'] does not exist" do
+ before { ENV.delete("PATH") }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] is empty" do
+ before { ENV["PATH"] = "" }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] exists" do
+ before { ENV["PATH"] = "/some_path/bin" }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] already contains the bundle bin path" do
+ let(:bundle_path) { "#{Bundler.bundle_path}/bin" }
+
+ before do
+ ENV["PATH"] = bundle_path
+ end
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+
+ it "ENV['PATH'] should only contain one instance of bundle bin path" do
+ subject.set_bundle_environment
+ paths = (ENV["PATH"]).split(File::PATH_SEPARATOR)
+ expect(paths.count(bundle_path)).to eq(1)
+ end
+ end
+
+ context "ENV['RUBYOPT'] does not exist" do
+ before { ENV.delete("RUBYOPT") }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists without -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-rbundler/setup" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] does not exist" do
+ before { ENV.delete("RUBYLIB") }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] is empty" do
+ before { ENV["PATH"] = "" }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] exists" do
+ before { ENV["PATH"] = "/some_path/bin" }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "bundle executable in ENV['BUNDLE_BIN_PATH'] does not exist" do
+ before { ENV["BUNDLE_BIN_PATH"] = "/does/not/exist" }
+ before { Bundler.rubygems.replace_bin_path [], [] }
+
+ it "sets BUNDLE_BIN_PATH to the bundle executable file" do
+ subject.set_bundle_environment
+ bundle_exe = ruby_core? ? "../../../../exe/bundle" : "../../../exe/bundle"
+ expect(ENV["BUNDLE_BIN_PATH"]).to eq(File.expand_path(bundle_exe, __FILE__))
+ end
+ end
+
+ context "ENV['RUBYLIB'] already contains the bundler's ruby version lib path" do
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
+ before do
+ ENV["RUBYLIB"] = ruby_lib_path
+ end
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+
+ it "ENV['RUBYLIB'] should only contain one instance of bundler's ruby version lib path" do
+ subject.set_bundle_environment
+ paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
+ expect(paths.count(ruby_lib_path)).to eq(1)
+ end
+ end
+ end
+
+ describe "#filesystem_access" do
+ context "system has proper permission access" do
+ let(:file_op_block) { proc {|path| FileUtils.mkdir_p(path) } }
+
+ it "performs the operation in the passed block" do
+ subject.filesystem_access("./test_dir", &file_op_block)
+ expect(Pathname.new("test_dir")).to exist
+ end
+ end
+
+ context "system throws Errno::EACESS" do
+ let(:file_op_block) { proc {|_path| raise Errno::EACCES } }
+
+ it "raises a PermissionError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::PermissionError
+ )
+ end
+ end
+
+ context "system throws Errno::EAGAIN" do
+ let(:file_op_block) { proc {|_path| raise Errno::EAGAIN } }
+
+ it "raises a TemporaryResourceError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::TemporaryResourceError
+ )
+ end
+ end
+
+ context "system throws Errno::EPROTO" do
+ let(:file_op_block) { proc {|_path| raise Errno::EPROTO } }
+
+ it "raises a VirtualProtocolError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::VirtualProtocolError
+ )
+ end
+ end
+
+ context "system throws Errno::ENOTSUP", :ruby => "1.9" do
+ let(:file_op_block) { proc {|_path| raise Errno::ENOTSUP } }
+
+ it "raises a OperationNotSupportedError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::OperationNotSupportedError
+ )
+ end
+ end
+
+ context "system throws Errno::ENOSPC" do
+ let(:file_op_block) { proc {|_path| raise Errno::ENOSPC } }
+
+ it "raises a NoSpaceOnDeviceError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::NoSpaceOnDeviceError
+ )
+ end
+ end
+
+ context "system throws an unhandled SystemCallError" do
+ let(:error) { SystemCallError.new("Shields down", 1337) }
+ let(:file_op_block) { proc {|_path| raise error } }
+
+ it "raises a GenericSystemCallError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::GenericSystemCallError, /error accessing.+underlying.+Shields down/m
+ )
+ end
+ end
+ end
+
+ describe "#const_get_safely" do
+ module TargetNamespace
+ VALID_CONSTANT = 1
+ end
+
+ context "when the namespace does have the requested constant" do
+ it "returns the value of the requested constant" do
+ expect(subject.const_get_safely(:VALID_CONSTANT, TargetNamespace)).to eq(1)
+ end
+ end
+
+ context "when the requested constant is passed as a string" do
+ it "returns the value of the requested constant" do
+ expect(subject.const_get_safely("VALID_CONSTANT", TargetNamespace)).to eq(1)
+ end
+ end
+
+ context "when the namespace does not have the requested constant" do
+ it "returns nil" do
+ expect(subject.const_get_safely("INVALID_CONSTANT", TargetNamespace)).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
new file mode 100644
index 0000000000..3a29c97461
--- /dev/null
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Git::GitProxy do
+ let(:path) { Pathname("path") }
+ let(:uri) { "https://github.com/bundler/bundler.git" }
+ let(:ref) { "HEAD" }
+ let(:revision) { nil }
+ let(:git_source) { nil }
+ subject { described_class.new(path, uri, ref, revision, git_source) }
+
+ context "with configured credentials" do
+ it "adds username and password to URI" do
+ Bundler.settings.temporary(uri => "u:p")
+ expect(subject).to receive(:git_retry).with(match("https://u:p@github.com/bundler/bundler.git"))
+ subject.checkout
+ end
+
+ it "adds username and password to URI for host" do
+ Bundler.settings.temporary("github.com" => "u:p")
+ expect(subject).to receive(:git_retry).with(match("https://u:p@github.com/bundler/bundler.git"))
+ subject.checkout
+ end
+
+ it "does not add username and password to mismatched URI" do
+ Bundler.settings.temporary("https://u:p@github.com/bundler/bundler-mismatch.git" => "u:p")
+ expect(subject).to receive(:git_retry).with(match(uri))
+ subject.checkout
+ end
+
+ it "keeps original userinfo" do
+ Bundler.settings.temporary("github.com" => "u:p")
+ original = "https://orig:info@github.com/bundler/bundler.git"
+ subject = described_class.new(Pathname("path"), original, "HEAD")
+ expect(subject).to receive(:git_retry).with(match(original))
+ subject.checkout
+ end
+ end
+
+ describe "#version" do
+ context "with a normal version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3")
+ end
+
+ it "returns the git version number" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+
+ context "with a OSX version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ end
+
+ it "strips out OSX specific additions in the version string" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+
+ context "with a msysgit version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3.msysgit.0")
+ end
+
+ it "strips out msysgit specific additions in the version string" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+ end
+
+ describe "#full_version" do
+ context "with a normal version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3")
+ end
+
+ it "returns the git version number" do
+ expect(subject.full_version).to eq("1.2.3")
+ end
+ end
+
+ context "with a OSX version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ end
+
+ it "does not strip out OSX specific additions in the version string" do
+ expect(subject.full_version).to eq("1.2.3 (Apple Git-BS)")
+ end
+ end
+
+ context "with a msysgit version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3.msysgit.0")
+ end
+
+ it "does not strip out msysgit specific additions in the version string" do
+ expect(subject.full_version).to eq("1.2.3.msysgit.0")
+ end
+ end
+ end
+
+ describe "#copy_to" do
+ let(:destination) { tmpdir("copy_to_path") }
+ let(:submodules) { false }
+
+ context "when given a SHA as a revision" do
+ let(:revision) { "abcd" * 10 }
+
+ it "fails gracefully when resetting to the revision fails" do
+ expect(subject).to receive(:git_retry).with(start_with("clone ")) { destination.mkpath }
+ expect(subject).to receive(:git_retry).with(start_with("fetch "))
+ expect(subject).to receive(:git).with("reset --hard #{revision}").and_raise(Bundler::Source::Git::GitCommandError, "command")
+ expect(subject).not_to receive(:git)
+
+ expect { subject.copy_to(destination, submodules) }.
+ to raise_error(Bundler::Source::Git::MissingGitRevisionError,
+ "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
new file mode 100644
index 0000000000..f7475a35aa
--- /dev/null
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Git do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ let(:uri) { "https://github.com/foo/bar.git" }
+ let(:options) do
+ { "uri" => uri }
+ end
+
+ subject { described_class.new(options) }
+
+ describe "#to_s" do
+ it "returns a description" do
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git (at master)"
+ end
+
+ context "when the URI contains credentials" do
+ let(:uri) { "https://my-secret-token:x-oauth-basic@github.com/foo/bar.git" }
+
+ it "filters credentials" do
+ expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git (at master)"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/path_spec.rb b/spec/bundler/bundler/source/path_spec.rb
new file mode 100644
index 0000000000..1d13e03ec1
--- /dev/null
+++ b/spec/bundler/bundler/source/path_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Path do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "#eql?" do
+ subject { described_class.new("path" => "gems/a") }
+
+ context "with two equivalent relative paths from different roots" do
+ let(:a_gem_opts) { { "path" => "../gems/a", "root_path" => Bundler.root.join("nested") } }
+ let(:a_gem) { described_class.new a_gem_opts }
+
+ it "returns true" do
+ expect(subject).to eq a_gem
+ end
+ end
+
+ context "with the same (but not equivalent) relative path from different roots" do
+ subject { described_class.new("path" => "gems/a") }
+
+ let(:a_gem_opts) { { "path" => "gems/a", "root_path" => Bundler.root.join("nested") } }
+ let(:a_gem) { described_class.new a_gem_opts }
+
+ it "returns false" do
+ expect(subject).to_not eq a_gem
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb
new file mode 100644
index 0000000000..9a7ab42128
--- /dev/null
+++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+require "bundler/source/rubygems/remote"
+
+RSpec.describe Bundler::Source::Rubygems::Remote do
+ def remote(uri)
+ Bundler::Source::Rubygems::Remote.new(uri)
+ end
+
+ before do
+ allow(Digest(:MD5)).to receive(:hexdigest).with(duck_type(:to_s)) {|string| "MD5HEX(#{string})" }
+ end
+
+ let(:uri_no_auth) { URI("https://gems.example.com") }
+ let(:uri_with_auth) { URI("https://#{credentials}@gems.example.com") }
+ let(:credentials) { "username:password" }
+
+ context "when the original URI has no credentials" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).uri).to eq(uri_no_auth)
+ end
+
+ it "applies configured credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri_no_auth).cache_slug).to eq("gems.example.com.443.MD5HEX(gems.example.com.443./)")
+ end
+
+ it "only applies the given user" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+ end
+ end
+
+ context "when the original URI has a username and password" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+
+ it "does not apply configured credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => "other:stuff")
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => "other:stuff")
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri_with_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_with_auth.to_s => credentials)
+ expect(remote(uri_with_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+ end
+ end
+
+ context "when the original URI has only a username" do
+ let(:uri) { URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri).anonymized_uri).to eq(URI("https://gem.fury.io/me/"))
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri).cache_slug).to eq("gem.fury.io.SeCrEt-ToKeN.443.MD5HEX(gem.fury.io.SeCrEt-ToKeN.443./me/)")
+ end
+ end
+ end
+
+ context "when a mirror with inline credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://username:password@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before { Bundler.settings.set_local("mirror.https://rubygems.org/", mirror_uri_with_auth.to_s) }
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+
+ specify "#original_uri returns the original source" do
+ expect(remote(uri).original_uri).to eq(uri)
+ end
+
+ specify "#cache_slug returns the correct slug" do
+ expect(remote(uri).cache_slug).to eq("rubygems.org.443.MD5HEX(rubygems.org.443./)")
+ end
+ end
+
+ context "when a mirror with configured credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://#{credentials}@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before do
+ Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s)
+ Bundler.settings.temporary(mirror_uri_no_auth.to_s => credentials)
+ end
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+
+ specify "#original_uri returns the original source" do
+ expect(remote(uri).original_uri).to eq(uri)
+ end
+
+ specify "#cache_slug returns the original source" do
+ expect(remote(uri).cache_slug).to eq("rubygems.org.443.MD5HEX(rubygems.org.443./)")
+ end
+ end
+
+ context "when there is no mirror set" do
+ describe "#original_uri" do
+ it "is not set" do
+ expect(remote(uri_no_auth).original_uri).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb
new file mode 100644
index 0000000000..7c457a7265
--- /dev/null
+++ b/spec/bundler/bundler/source/rubygems_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Rubygems do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "caches" do
+ it "includes Bundler.app_cache" do
+ expect(subject.caches).to include(Bundler.app_cache)
+ end
+
+ it "includes GEM_PATH entries" do
+ Gem.path.each do |path|
+ expect(subject.caches).to include(File.expand_path("#{path}/cache"))
+ end
+ end
+
+ it "is an array of strings or pathnames" do
+ subject.caches.each do |cache|
+ expect([String, Pathname]).to include(cache.class)
+ end
+ end
+ end
+
+ describe "#add_remote" do
+ context "when the source is an HTTP(s) URI with no host" do
+ it "raises error" do
+ expect { subject.add_remote("https:rubygems.org") }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
new file mode 100644
index 0000000000..ce3353012c
--- /dev/null
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -0,0 +1,463 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SourceList do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "./tmp/bundled_app" }
+
+ stub_const "ASourcePlugin", Class.new(Bundler::Plugin::API)
+ ASourcePlugin.source "new_source"
+ allow(Bundler::Plugin).to receive(:source?).with("new_source").and_return(true)
+ end
+
+ subject(:source_list) { Bundler::SourceList.new }
+
+ let(:rubygems_aggregate) { Bundler::Source::Rubygems.new }
+ let(:metadata_source) { Bundler::Source::Metadata.new }
+
+ describe "adding sources" do
+ before do
+ source_list.add_path_source("path" => "/existing/path/to/gem")
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://existing-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+ end
+
+ describe "#add_path_source" do
+ before do
+ @duplicate = source_list.add_path_source("path" => "/path/to/gem")
+ @new_source = source_list.add_path_source("path" => "/path/to/gem")
+ end
+
+ it "returns the new path source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Path)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("path" => "/path/to/gem")
+ end
+
+ it "adds the source to the beginning of path_sources" do
+ expect(source_list.path_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.path_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_git_source" do
+ before do
+ @duplicate = source_list.add_git_source("uri" => "git://host/path.git")
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ end
+
+ it "returns the new git source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Git)
+ end
+
+ it "passes the provided options to the new source" do
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(@new_source.options).to eq("uri" => "git://host/path.git")
+ end
+
+ it "adds the source to the beginning of git_sources" do
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.git_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ @duplicate = source_list.add_git_source("uri" => "git://host/path.git")
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.git_sources).not_to include equal(@duplicate)
+ end
+
+ context "with the git: protocol" do
+ let(:msg) do
+ "The git source `git://existing-git.org/path.git` " \
+ "uses the `git` protocol, which transmits data without encryption. " \
+ "Disable this warning with `bundle config git.allow_insecure true`, " \
+ "or switch to the `https` protocol to keep your data secure."
+ end
+
+ it "warns about git protocols" do
+ expect(Bundler.ui).to receive(:warn).with(msg)
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ end
+
+ it "ignores git protocols on request" do
+ Bundler.settings.temporary(:"git.allow_insecure" => true)
+ expect(Bundler.ui).to_not receive(:warn).with(msg)
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ end
+ end
+ end
+
+ describe "#add_rubygems_source" do
+ before do
+ @duplicate = source_list.add_rubygems_source("remotes" => ["https://rubygems.org/"])
+ @new_source = source_list.add_rubygems_source("remotes" => ["https://rubygems.org/"])
+ end
+
+ it "returns the new rubygems source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Rubygems)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("remotes" => ["https://rubygems.org/"])
+ end
+
+ it "adds the source to the beginning of rubygems_sources" do
+ expect(source_list.rubygems_sources.first).to equal(@new_source)
+ end
+
+ it "removes duplicates" do
+ expect(source_list.rubygems_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_rubygems_remote", :bundler => "< 2" do
+ let!(:returned_source) { source_list.add_rubygems_remote("https://rubygems.org/") }
+
+ it "returns the aggregate rubygems source" do
+ expect(returned_source).to be_instance_of(Bundler::Source::Rubygems)
+ end
+
+ it "adds the provided remote to the beginning of the aggregate source" do
+ source_list.add_rubygems_remote("https://othersource.org")
+ expect(returned_source.remotes).to eq [
+ URI("https://othersource.org/"),
+ URI("https://rubygems.org/"),
+ ]
+ end
+ end
+
+ describe "#add_plugin_source" do
+ before do
+ @duplicate = source_list.add_plugin_source("new_source", "uri" => "http://host/path.")
+ @new_source = source_list.add_plugin_source("new_source", "uri" => "http://host/path.")
+ end
+
+ it "returns the new plugin source" do
+ expect(@new_source).to be_a(Bundler::Plugin::API::Source)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("uri" => "http://host/path.")
+ end
+
+ it "adds the source to the beginning of git_sources" do
+ expect(source_list.plugin_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.plugin_sources).not_to include equal(@duplicate)
+ end
+ end
+ end
+
+ describe "#all_sources" do
+ it "includes the aggregate rubygems source when rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
+ source_list.add_path_source("path" => "/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+
+ expect(source_list.all_sources).to include rubygems_aggregate
+ end
+
+ it "includes the aggregate rubygems source when no rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_path_source("path" => "/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+
+ expect(source_list.all_sources).to include rubygems_aggregate
+ end
+
+ it "returns sources of the same type in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://fifth-rubygems.org"])
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/b")
+ source_list.add_rubygems_source("remotes" => ["https://fourth-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://some.o.url/")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/c")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.all_sources).to eq [
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://some.url/c"),
+ ASourcePlugin.new("uri" => "https://some.o.url/"),
+ ASourcePlugin.new("uri" => "https://some.url/b"),
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
+ rubygems_aggregate,
+ metadata_source,
+ ]
+ end
+ end
+
+ describe "#path_sources" do
+ it "returns an empty array when no path sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.path_sources).to be_empty
+ end
+
+ it "returns path sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.path_sources).to eq [
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ ]
+ end
+ end
+
+ describe "#git_sources" do
+ it "returns an empty array when no git sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.git_sources).to be_empty
+ end
+
+ it "returns git sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.git_sources).to eq [
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ]
+ end
+ end
+
+ describe "#plugin_sources" do
+ it "returns an empty array when no plugin sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.plugin_sources).to be_empty
+ end
+
+ it "returns plugin sources in the reverse order that they were added" do
+ source_list.add_plugin_source("new_source", "uri" => "https://third-git.org/path.git")
+ source_list.add_git_source("https://new-git.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_plugin_source("new_source", "uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_plugin_source("new_source", "uri" => "git://first-git.org/path.git")
+
+ expect(source_list.plugin_sources).to eq [
+ ASourcePlugin.new("uri" => "git://first-git.org/path.git"),
+ ASourcePlugin.new("uri" => "git://second-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://third-git.org/path.git"),
+ ]
+ end
+ end
+
+ describe "#rubygems_sources" do
+ it "includes the aggregate rubygems source when rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.rubygems_sources).to include rubygems_aggregate
+ end
+
+ it "returns only the aggregate rubygems source when no rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.rubygems_sources).to eq [rubygems_aggregate]
+ end
+
+ it "returns rubygems sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://fifth-rubygems.org"])
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://fourth-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.rubygems_sources).to eq [
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
+ rubygems_aggregate,
+ ]
+ end
+ end
+
+ describe "#get" do
+ context "when it includes an equal source" do
+ let(:rubygems_source) { Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"]) }
+ before { @equal_source = source_list.add_rubygems_remote("https://rubygems.org") }
+
+ it "returns the equal source" do
+ expect(source_list.get(rubygems_source)).to be @equal_source
+ end
+ end
+
+ context "when it does not include an equal source" do
+ let(:path_source) { Bundler::Source::Path.new("path" => "/path/to/gem") }
+
+ it "returns nil" do
+ expect(source_list.get(path_source)).to be_nil
+ end
+ end
+ end
+
+ describe "#lock_sources" do
+ before do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://duplicate-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://third-bar.org/foo")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://second-plugin.org/random")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://duplicate-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+ end
+
+ it "combines the rubygems sources into a single instance, removing duplicate remotes from the end", :bundler => "< 2" do
+ expect(source_list.lock_sources).to eq [
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://second-plugin.org/random"),
+ ASourcePlugin.new("uri" => "https://third-bar.org/foo"),
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ Bundler::Source::Rubygems.new("remotes" => [
+ "https://duplicate-rubygems.org",
+ "https://first-rubygems.org",
+ "https://second-rubygems.org",
+ "https://third-rubygems.org",
+ ]),
+ ]
+ end
+
+ it "returns all sources, without combining rubygems sources", :bundler => "2" do
+ expect(source_list.lock_sources).to eq [
+ Bundler::Source::Rubygems.new,
+ Bundler::Source::Rubygems.new("remotes" => ["https://duplicate-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ ASourcePlugin.new("uri" => "https://second-plugin.org/random"),
+ ASourcePlugin.new("uri" => "https://third-bar.org/foo"),
+ ]
+ end
+ end
+
+ describe "replace_sources!" do
+ let(:existing_locked_source) { Bundler::Source::Path.new("path" => "/existing/path") }
+ let(:removed_locked_source) { Bundler::Source::Path.new("path" => "/removed/path") }
+
+ let(:locked_sources) { [existing_locked_source, removed_locked_source] }
+
+ before do
+ @existing_source = source_list.add_path_source("path" => "/existing/path")
+ @new_source = source_list.add_path_source("path" => "/new/path")
+ source_list.replace_sources!(locked_sources)
+ end
+
+ it "maintains the order and number of sources" do
+ expect(source_list.path_sources).to eq [@new_source, @existing_source]
+ end
+
+ it "retains the same instance of the new source" do
+ expect(source_list.path_sources[0]).to be @new_source
+ end
+
+ it "replaces the instance of the existing source" do
+ expect(source_list.path_sources[1]).to be existing_locked_source
+ end
+ end
+
+ describe "#cached!" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+ let(:git_source) { source_list.add_git_source("uri" => "git://host/path.git") }
+ let(:path_source) { source_list.add_path_source("path" => "/path/to/gem") }
+
+ it "calls #cached! on all the sources" do
+ expect(rubygems_source).to receive(:cached!)
+ expect(git_source).to receive(:cached!)
+ expect(path_source).to receive(:cached!)
+ source_list.cached!
+ end
+ end
+
+ describe "#remote!" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+ let(:git_source) { source_list.add_git_source("uri" => "git://host/path.git") }
+ let(:path_source) { source_list.add_path_source("path" => "/path/to/gem") }
+
+ it "calls #remote! on all the sources" do
+ expect(rubygems_source).to receive(:remote!)
+ expect(git_source).to receive(:remote!)
+ expect(path_source).to receive(:remote!)
+ source_list.remote!
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source_spec.rb b/spec/bundler/bundler/source_spec.rb
new file mode 100644
index 0000000000..9ef8e7e50f
--- /dev/null
+++ b/spec/bundler/bundler/source_spec.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source do
+ class ExampleSource < Bundler::Source
+ end
+
+ subject { ExampleSource.new }
+
+ describe "#unmet_deps" do
+ let(:specs) { double(:specs) }
+ let(:unmet_dependency_names) { double(:unmet_dependency_names) }
+
+ before do
+ allow(subject).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:unmet_dependency_names).and_return(unmet_dependency_names)
+ end
+
+ it "should return the names of unmet dependencies" do
+ expect(subject.unmet_deps).to eq(unmet_dependency_names)
+ end
+ end
+
+ describe "#version_message" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6", :platform => rb) }
+
+ shared_examples_for "the lockfile specs are not relevant" do
+ it "should return a string with the spec name and version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6")
+ end
+ end
+
+ context "when there are locked gems" do
+ let(:locked_gems) { double(:locked_gems) }
+
+ before { allow(Bundler).to receive(:locked_gems).and_return(locked_gems) }
+
+ context "that contain the relevant gem spec" do
+ before do
+ specs = double(:specs)
+ allow(locked_gems).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:find).and_return(locked_gem)
+ end
+
+ context "without a version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => nil) }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+
+ context "with the same version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => ">= 1.6") }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+
+ context "with a different version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "< 1.5") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the spec name and version and locked spec version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6\e[32m (was < 1.5)\e[0m")
+ end
+ end
+
+ context "without color" do
+ it "should return a string with the spec name and version and locked spec version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6 (was < 1.5)")
+ end
+ end
+ end
+
+ context "with a more recent version" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => "1.6.1", :platform => rb) }
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the locked spec version in yellow" do
+ expect(subject.version_message(spec)).to eq("nokogiri 1.6.1\e[33m (was 1.7.0)\e[0m")
+ end
+ end
+ end
+
+ context "with an older version" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => "1.7.1", :platform => rb) }
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the locked spec version in green" do
+ expect(subject.version_message(spec)).to eq("nokogiri 1.7.1\e[32m (was 1.7.0)\e[0m")
+ end
+ end
+ end
+ end
+
+ context "that do not contain the relevant gem spec" do
+ before do
+ specs = double(:specs)
+ allow(locked_gems).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:find).and_return(nil)
+ end
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+ end
+
+ context "when there are no locked gems" do
+ before { allow(Bundler).to receive(:locked_gems).and_return(nil) }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+ end
+
+ describe "#can_lock?" do
+ context "when the passed spec's source is equivalent" do
+ let(:spec) { double(:spec, :source => subject) }
+
+ it "should return true" do
+ expect(subject.can_lock?(spec)).to be_truthy
+ end
+ end
+
+ context "when the passed spec's source is not equivalent" do
+ let(:spec) { double(:spec, :source => double(:other_source)) }
+
+ it "should return false" do
+ expect(subject.can_lock?(spec)).to be_falsey
+ end
+ end
+ end
+
+ describe "#include?" do
+ context "when the passed source is equivalent" do
+ let(:source) { subject }
+
+ it "should return true" do
+ expect(subject).to include(source)
+ end
+ end
+
+ context "when the passed source is not equivalent" do
+ let(:source) { double(:source) }
+
+ it "should return false" do
+ expect(subject).to_not include(source)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
new file mode 100644
index 0000000000..6fedd38b50
--- /dev/null
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SpecSet do
+ let(:specs) do
+ [
+ build_spec("a", "1.0"),
+ build_spec("b", "1.0"),
+ build_spec("c", "1.1") do |s|
+ s.dep "a", "< 2.0"
+ s.dep "e", "> 0"
+ end,
+ build_spec("d", "2.0") do |s|
+ s.dep "a", "1.0"
+ s.dep "c", "~> 1.0"
+ end,
+ build_spec("e", "1.0.0.pre.1"),
+ ].flatten
+ end
+
+ subject { described_class.new(specs) }
+
+ context "enumerable methods" do
+ it "has a length" do
+ expect(subject.length).to eq(5)
+ end
+
+ it "has a size" do
+ expect(subject.size).to eq(5)
+ end
+ end
+
+ describe "#find_by_name_and_platform" do
+ let(:platform) { Gem::Platform.new("universal-darwin-64") }
+ let(:platform_spec) { build_spec("b", "2.0", platform).first }
+ let(:specs) do
+ [
+ build_spec("a", "1.0"),
+ platform_spec,
+ ].flatten
+ end
+
+ it "finds spec with given name and platform" do
+ spec = described_class.new(specs).find_by_name_and_platform("b", platform)
+ expect(spec).to eq platform_spec
+ end
+ end
+
+ describe "#merge" do
+ let(:other_specs) do
+ [
+ build_spec("f", "1.0"),
+ build_spec("g", "2.0"),
+ ].flatten
+ end
+
+ let(:other_spec_set) { described_class.new(other_specs) }
+
+ it "merges the items in each gemspec" do
+ new_spec_set = subject.merge(other_spec_set)
+ specs = new_spec_set.to_a.map(&:full_name)
+ expect(specs).to include("a-1.0")
+ expect(specs).to include("f-1.0")
+ end
+ end
+
+ describe "#to_a" do
+ it "returns the specs in order" do
+ expect(subject.to_a.map(&:full_name)).to eq %w[
+ a-1.0
+ b-1.0
+ e-1.0.0.pre.1
+ c-1.1
+ d-2.0
+ ]
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb b/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb
new file mode 100644
index 0000000000..4250bfc497
--- /dev/null
+++ b/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require "bundler/ssl_certs/certificate_manager"
+
+RSpec.describe Bundler::SSLCerts::CertificateManager do
+ let(:rubygems_path) { root }
+ let(:stub_cert) { File.join(root.to_s, "lib", "rubygems", "ssl_certs", "rubygems.org", "ssl-cert.pem") }
+ let(:rubygems_certs_dir) { File.join(root.to_s, "lib", "rubygems", "ssl_certs", "rubygems.org") }
+
+ subject { described_class.new(rubygems_path) }
+
+ # Pretend bundler root is rubygems root
+ before do
+ # Backing up rubygems ceriticates
+ FileUtils.mv(rubygems_certs_dir, rubygems_certs_dir + ".back") if ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]
+
+ FileUtils.mkdir_p(rubygems_certs_dir)
+ FileUtils.touch(stub_cert)
+ end
+
+ after do
+ FileUtils.rm_rf(rubygems_certs_dir)
+
+ # Restore rubygems certificates
+ FileUtils.mv(rubygems_certs_dir + ".back", rubygems_certs_dir) if ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]
+ end
+
+ describe "#update_from" do
+ let(:cert_manager) { double(:cert_manager) }
+
+ before { allow(described_class).to receive(:new).with(rubygems_path).and_return(cert_manager) }
+
+ it "should update the certs through a new certificate manager" do
+ allow(cert_manager).to receive(:update!)
+ expect(described_class.update_from!(rubygems_path)).to be_nil
+ end
+ end
+
+ describe "#initialize" do
+ it "should set bundler_cert_path as path of the subdir with bundler ssl certs" do
+ expect(subject.bundler_cert_path).to eq(File.join(root, "lib/bundler/ssl_certs"))
+ end
+
+ it "should set bundler_certs as the paths of the bundler ssl certs" do
+ expect(subject.bundler_certs).to include(File.join(root, "lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem"))
+ expect(subject.bundler_certs).to include(File.join(root, "lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem"))
+ end
+
+ context "when rubygems_path is not nil" do
+ it "should set rubygems_certs" do
+ expect(subject.rubygems_certs).to include(File.join(root, "lib", "rubygems", "ssl_certs", "rubygems.org", "ssl-cert.pem"))
+ end
+ end
+ end
+
+ describe "#up_to_date?" do
+ context "when bundler certs and rubygems certs are the same" do
+ before do
+ bundler_certs = Dir[File.join(root.to_s, "lib", "bundler", "ssl_certs", "**", "*.pem")]
+ FileUtils.rm(stub_cert)
+ FileUtils.cp(bundler_certs, rubygems_certs_dir)
+ end
+
+ it "should return true" do
+ expect(subject).to be_up_to_date
+ end
+ end
+
+ context "when bundler certs and rubygems certs are not the same" do
+ it "should return false" do
+ expect(subject).to_not be_up_to_date
+ end
+ end
+ end
+
+ describe "#update!" do
+ context "when certificate manager is not up to date" do
+ before do
+ allow(subject).to receive(:up_to_date?).and_return(false)
+ allow(bundler_fileutils).to receive(:rm)
+ allow(bundler_fileutils).to receive(:cp)
+ end
+
+ it "should remove the current bundler certs" do
+ expect(bundler_fileutils).to receive(:rm).with(subject.bundler_certs)
+ subject.update!
+ end
+
+ it "should copy the rubygems certs into bundler certs" do
+ expect(bundler_fileutils).to receive(:cp).with(subject.rubygems_certs, subject.bundler_cert_path)
+ subject.update!
+ end
+
+ it "should return nil" do
+ expect(subject.update!).to be_nil
+ end
+ end
+
+ context "when certificate manager is up to date" do
+ before { allow(subject).to receive(:up_to_date?).and_return(true) }
+
+ it "should return nil" do
+ expect(subject.update!).to be_nil
+ end
+ end
+ end
+
+ describe "#connect_to" do
+ let(:host) { "http://www.host.com" }
+ let(:http) { Net::HTTP.new(host, 443) }
+ let(:cert_store) { OpenSSL::X509::Store.new }
+ let(:http_header_response) { double(:http_header_response) }
+
+ before do
+ allow(Net::HTTP).to receive(:new).with(host, 443).and_return(http)
+ allow(OpenSSL::X509::Store).to receive(:new).and_return(cert_store)
+ allow(http).to receive(:head).with("/").and_return(http_header_response)
+ end
+
+ it "should use ssl for the http request" do
+ expect(http).to receive(:use_ssl=).with(true)
+ subject.connect_to(host)
+ end
+
+ it "use verify peer mode" do
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+ subject.connect_to(host)
+ end
+
+ it "set its cert store as a OpenSSL::X509::Store populated with bundler certs" do
+ expect(cert_store).to receive(:add_file).at_least(:once)
+ expect(http).to receive(:cert_store=).with(cert_store)
+ subject.connect_to(host)
+ end
+
+ it "return the headers of the request response" do
+ expect(subject.connect_to(host)).to eq(http_header_response)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
new file mode 100644
index 0000000000..5521d83769
--- /dev/null
+++ b/spec/bundler/bundler/stub_specification_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::StubSpecification do
+ let(:gemspec) do
+ Gem::Specification.new do |s|
+ s.name = "gemname"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ end
+ end
+
+ let(:with_bundler_stub_spec) do
+ described_class.from_stub(gemspec)
+ end
+
+ if Bundler.rubygems.provides?(">= 2.1")
+ describe "#from_stub" do
+ it "returns the same stub if already a Bundler::StubSpecification" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ expect(stub).to be(with_bundler_stub_spec)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ui/shell_spec.rb b/spec/bundler/bundler/ui/shell_spec.rb
new file mode 100644
index 0000000000..951a446aff
--- /dev/null
+++ b/spec/bundler/bundler/ui/shell_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::UI::Shell do
+ subject { described_class.new }
+
+ before { subject.level = "debug" }
+
+ describe "#info" do
+ before { subject.level = "info" }
+ it "prints to stdout" do
+ expect { subject.info("info") }.to output("info\n").to_stdout
+ end
+ end
+
+ describe "#confirm" do
+ before { subject.level = "confirm" }
+ it "prints to stdout" do
+ expect { subject.confirm("confirm") }.to output("confirm\n").to_stdout
+ end
+ end
+
+ describe "#warn" do
+ before { subject.level = "warn" }
+ it "prints to stdout", :bundler => "< 2" do
+ expect { subject.warn("warning") }.to output("warning\n").to_stdout
+ end
+
+ it "prints to stderr", :bundler => "2" do
+ expect { subject.warn("warning") }.to output("warning\n").to_stderr
+ end
+
+ context "when stderr flag is enabled" do
+ before { Bundler.settings.temporary(:error_on_stderr => true) }
+ it "prints to stderr" do
+ expect { subject.warn("warning!") }.to output("warning!\n").to_stderr
+ end
+ end
+ end
+
+ describe "#debug" do
+ it "prints to stdout" do
+ expect { subject.debug("debug") }.to output("debug\n").to_stdout
+ end
+ end
+
+ describe "#error" do
+ before { subject.level = "error" }
+
+ it "prints to stdout", :bundler => "< 2" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stdout
+ end
+
+ it "prints to stderr", :bundler => "2" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stderr
+ end
+
+ context "when stderr flag is enabled" do
+ before { Bundler.settings.temporary(:error_on_stderr => true) }
+ it "prints to stderr" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stderr
+ end
+
+ context "when stderr is closed" do
+ it "doesn't report anything" do
+ output = capture(:stderr, :closed => true) do
+ subject.error("Something went wrong")
+ end
+ expect(output).to_not eq("Something went wrong\n")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ui_spec.rb b/spec/bundler/bundler/ui_spec.rb
new file mode 100644
index 0000000000..6ef8729277
--- /dev/null
+++ b/spec/bundler/bundler/ui_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::UI do
+ describe Bundler::UI::Silent do
+ it "has the same instance methods as Shell", :ruby => ">= 1.9" do
+ shell = Bundler::UI::Shell
+ methods = proc do |cls|
+ cls.instance_methods.map do |i|
+ m = shell.instance_method(i)
+ [i, m.parameters]
+ end.sort_by(&:first)
+ end
+ expect(methods.call(described_class)).to eq(methods.call(shell))
+ end
+
+ it "has the same instance class as Shell", :ruby => ">= 1.9" do
+ shell = Bundler::UI::Shell
+ methods = proc do |cls|
+ cls.methods.map do |i|
+ m = shell.method(i)
+ [i, m.parameters]
+ end.sort_by(&:first)
+ end
+ expect(methods.call(described_class)).to eq(methods.call(shell))
+ end
+ end
+
+ describe Bundler::UI::Shell do
+ let(:options) { {} }
+ subject { described_class.new(options) }
+ describe "debug?" do
+ it "returns a boolean" do
+ subject.level = :debug
+ expect(subject.debug?).to eq(true)
+
+ subject.level = :error
+ expect(subject.debug?).to eq(false)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/uri_credentials_filter_spec.rb b/spec/bundler/bundler/uri_credentials_filter_spec.rb
new file mode 100644
index 0000000000..fe52d16306
--- /dev/null
+++ b/spec/bundler/bundler/uri_credentials_filter_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::URICredentialsFilter do
+ subject { described_class }
+
+ describe "#credential_filtered_uri" do
+ shared_examples_for "original type of uri is maintained" do
+ it "maintains same type for return value as uri input type" do
+ expect(subject.credential_filtered_uri(uri)).to be_kind_of(uri.class)
+ end
+ end
+
+ shared_examples_for "sensitive credentials in uri are filtered out" do
+ context "authentication using oauth credentials" do
+ context "specified via 'x-oauth-basic'" do
+ let(:credentials) { "oauth_token:x-oauth-basic@" }
+
+ it "returns the uri without the oauth token" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "specified via 'x'" do
+ let(:credentials) { "oauth_token:x@" }
+
+ it "returns the uri without the oauth token" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://x@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ context "authentication using login credentials" do
+ let(:credentials) { "username1:hunter3@" }
+
+ it "returns the uri without the password" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://username1@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "authentication without credentials" do
+ let(:credentials) { "" }
+
+ it "returns the same uri" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(uri.to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ context "uri is a uri object" do
+ let(:uri) { URI("https://#{credentials}github.com/company/private-repo") }
+
+ it_behaves_like "sensitive credentials in uri are filtered out"
+ end
+
+ context "uri is a uri string" do
+ let(:uri) { "https://#{credentials}github.com/company/private-repo" }
+
+ it_behaves_like "sensitive credentials in uri are filtered out"
+ end
+
+ context "uri is a non-uri format string (ex. path)" do
+ let(:uri) { "/path/to/repo" }
+
+ it "returns the same uri" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(uri.to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "uri is nil" do
+ let(:uri) { nil }
+
+ it "returns nil" do
+ expect(subject.credential_filtered_uri(uri)).to be_nil
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ describe "#credential_filtered_string" do
+ let(:str_to_filter) { "This is a git message containing a uri #{uri}!" }
+ let(:credentials) { "" }
+ let(:uri) { URI("https://#{credentials}github.com/company/private-repo") }
+
+ context "with a uri that contains credentials" do
+ let(:credentials) { "oauth_token:x-oauth-basic@" }
+
+ it "returns the string without the sensitive credentials" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(
+ "This is a git message containing a uri https://x-oauth-basic@github.com/company/private-repo!"
+ )
+ end
+ end
+
+ context "that does not contains credentials" do
+ it "returns the same string" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(str_to_filter)
+ end
+ end
+
+ context "string to filter is nil" do
+ let(:str_to_filter) { nil }
+
+ it "returns nil" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to be_nil
+ end
+ end
+
+ context "uri to filter out is nil" do
+ let(:uri) { nil }
+
+ it "returns the same string" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(str_to_filter)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb
new file mode 100644
index 0000000000..338431c4a6
--- /dev/null
+++ b/spec/bundler/bundler/vendored_persistent_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+require "bundler/vendored_persistent"
+
+RSpec.describe Bundler::PersistentHTTP do
+ describe "#warn_old_tls_version_rubygems_connection" do
+ let(:uri) { "https://index.rubygems.org" }
+ let(:connection) { instance_double(subject.http_class) }
+ let(:tls_version) { "TLSv1.2" }
+ let(:socket) { double("Socket") }
+ let(:socket_io) { double("SocketIO") }
+
+ before do
+ allow(connection).to receive(:use_ssl?).and_return(!tls_version.nil?)
+ allow(socket).to receive(:io).and_return(socket_io)
+ connection.instance_variable_set(:@socket, socket)
+
+ if tls_version
+ allow(socket_io).to receive(:ssl_version).and_return(tls_version)
+ end
+ end
+
+ shared_examples_for "does not warn" do
+ it "does not warn" do
+ allow(Bundler.ui).to receive(:warn).never
+ subject.warn_old_tls_version_rubygems_connection(URI(uri), connection)
+ end
+ end
+
+ shared_examples_for "does warn" do |*expected|
+ it "warns" do
+ expect(Bundler.ui).to receive(:warn).with(*expected)
+ subject.warn_old_tls_version_rubygems_connection(URI(uri), connection)
+ end
+ end
+
+ context "an HTTPS uri with TLSv1.2" do
+ include_examples "does not warn"
+ end
+
+ context "without SSL" do
+ let(:tls_version) { nil }
+
+ include_examples "does not warn"
+ end
+
+ context "without a socket" do
+ let(:socket) { nil }
+
+ include_examples "does not warn"
+ end
+
+ context "with a different TLD" do
+ let(:uri) { "https://foo.bar" }
+ include_examples "does not warn"
+
+ context "and an outdated TLS version" do
+ let(:tls_version) { "TLSv1" }
+ include_examples "does not warn"
+ end
+ end
+
+ context "with a nonsense TLS version" do
+ let(:tls_version) { "BlahBlah2.0Blah" }
+ include_examples "does not warn"
+ end
+
+ context "with an outdated TLS version" do
+ let(:tls_version) { "TLSv1" }
+ include_examples "does warn",
+ "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
+ "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \
+ "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
+ :wrap => true
+ end
+ end
+end
diff --git a/spec/bundler/bundler/version_ranges_spec.rb b/spec/bundler/bundler/version_ranges_spec.rb
new file mode 100644
index 0000000000..ccbb9285d5
--- /dev/null
+++ b/spec/bundler/bundler/version_ranges_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "bundler/version_ranges"
+
+RSpec.describe Bundler::VersionRanges do
+ describe ".empty?" do
+ shared_examples_for "empty?" do |exp, *req|
+ it "returns #{exp} for #{req}" do
+ r = Gem::Requirement.new(*req)
+ ranges = described_class.for(r)
+ expect(described_class.empty?(*ranges)).to eq(exp), "expected `#{r}` #{exp ? "" : "not "}to be empty"
+ end
+ end
+
+ include_examples "empty?", false
+ include_examples "empty?", false, "!= 1"
+ include_examples "empty?", false, "!= 1", "= 2"
+ include_examples "empty?", false, "!= 1", "> 1"
+ include_examples "empty?", false, "!= 1", ">= 1"
+ include_examples "empty?", false, "= 1", ">= 0.1", "<= 1.1"
+ include_examples "empty?", false, "= 1", ">= 1", "<= 1"
+ include_examples "empty?", false, "= 1", "~> 1"
+ include_examples "empty?", false, ">= 0.z", "= 0"
+ include_examples "empty?", false, ">= 0"
+ include_examples "empty?", false, ">= 1.0.0", "< 2.0.0"
+ include_examples "empty?", false, "~> 1"
+ include_examples "empty?", false, "~> 2.0", "~> 2.1"
+ include_examples "empty?", true, "!= 1", "< 2", "> 2"
+ include_examples "empty?", true, "!= 1", "<= 1", ">= 1"
+ include_examples "empty?", true, "< 2", "> 2"
+ include_examples "empty?", true, "= 1", "!= 1"
+ include_examples "empty?", true, "= 1", "= 2"
+ include_examples "empty?", true, "= 1", "~> 2"
+ include_examples "empty?", true, ">= 0", "<= 0.a"
+ include_examples "empty?", true, "~> 2.0", "~> 3"
+ end
+end
diff --git a/spec/bundler/bundler/worker_spec.rb b/spec/bundler/bundler/worker_spec.rb
new file mode 100644
index 0000000000..2e5642709d
--- /dev/null
+++ b/spec/bundler/bundler/worker_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "bundler/worker"
+
+RSpec.describe Bundler::Worker do
+ let(:size) { 5 }
+ let(:name) { "Spec Worker" }
+ let(:function) { proc {|object, worker_number| [object, worker_number] } }
+ subject { described_class.new(size, name, function) }
+
+ after { subject.stop }
+
+ describe "#initialize" do
+ context "when Thread.start raises ThreadError" do
+ it "raises when no threads can be created" do
+ allow(Thread).to receive(:start).and_raise(ThreadError, "error creating thread")
+
+ expect { subject.enq "a" }.to raise_error(Bundler::ThreadCreationError, "Failed to create threads for the Spec Worker worker: error creating thread")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/yaml_serializer_spec.rb b/spec/bundler/bundler/yaml_serializer_spec.rb
new file mode 100644
index 0000000000..1241c74bbf
--- /dev/null
+++ b/spec/bundler/bundler/yaml_serializer_spec.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+require "bundler/yaml_serializer"
+
+RSpec.describe Bundler::YAMLSerializer do
+ subject(:serializer) { Bundler::YAMLSerializer }
+
+ describe "#dump" do
+ it "works for simple hash" do
+ hash = { "Q" => "Where does Thursday come before Wednesday? In the dictionary. :P" }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ Q: "Where does Thursday come before Wednesday? In the dictionary. :P"
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+
+ it "handles nested hash" do
+ hash = {
+ "nice-one" => {
+ "read_ahead" => "All generalizations are false, including this one",
+ },
+ }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ nice-one:
+ read_ahead: "All generalizations are false, including this one"
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+
+ it "array inside an hash" do
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Jack and Jill went up the hill",
+ "To fetch a pail of water.",
+ "Jack fell down and broke his crown,",
+ "And Jill came tumbling after.",
+ ],
+ },
+ }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ nested_hash:
+ contains_array:
+ - "Jack and Jill went up the hill"
+ - "To fetch a pail of water."
+ - "Jack fell down and broke his crown,"
+ - "And Jill came tumbling after."
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+ end
+
+ describe "#load" do
+ it "works for simple hash" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ Jon: "Air is free dude!"
+ Jack: "Yes.. until you buy a bag of chips!"
+ YAML
+
+ hash = {
+ "Jon" => "Air is free dude!",
+ "Jack" => "Yes.. until you buy a bag of chips!",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "works for nested hash" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ baa:
+ baa: "black sheep"
+ have: "you any wool?"
+ yes: "merry have I"
+ three: "bags full"
+ YAML
+
+ hash = {
+ "baa" => {
+ "baa" => "black sheep",
+ "have" => "you any wool?",
+ "yes" => "merry have I",
+ },
+ "three" => "bags full",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "handles colon in key/value" do
+ yaml = strip_whitespace <<-YAML
+ BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://rubygems-mirror.org
+ YAML
+
+ expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ end
+
+ it "handles arrays inside hashes" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ nested_hash:
+ contains_array:
+ - "Why shouldn't you write with a broken pencil?"
+ - "Because it's pointless!"
+ YAML
+
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Why shouldn't you write with a broken pencil?",
+ "Because it's pointless!",
+ ],
+ },
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "handles windows-style CRLF line endings" do
+ yaml = strip_whitespace(<<-YAML).gsub("\n", "\r\n")
+ ---
+ nested_hash:
+ contains_array:
+ - "Why shouldn't you write with a broken pencil?"
+ - "Because it's pointless!"
+ - oh so silly
+ YAML
+
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Why shouldn't you write with a broken pencil?",
+ "Because it's pointless!",
+ "oh so silly",
+ ],
+ },
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+ end
+
+ describe "against yaml lib" do
+ let(:hash) do
+ {
+ "a_joke" => {
+ "my-stand" => "I can totally keep secrets",
+ "but" => "The people I tell them to can't :P",
+ "wouldn't it be funny if this string were empty?" => "",
+ },
+ "more" => {
+ "first" => [
+ "Can a kangaroo jump higher than a house?",
+ "Of course, a house doesn't jump at all.",
+ ],
+ "second" => [
+ "What did the sea say to the sand?",
+ "Nothing, it simply waved.",
+ ],
+ "array with empty string" => [""],
+ },
+ "sales" => {
+ "item" => "A Parachute",
+ "description" => "Only used once, never opened.",
+ },
+ "one-more" => "I'd tell you a chemistry joke but I know I wouldn't get a reaction.",
+ }
+ end
+
+ context "#load" do
+ it "retrieves the original hash" do
+ require "yaml"
+ expect(serializer.load(YAML.dump(hash))).to eq(hash)
+ end
+ end
+
+ context "#dump" do
+ it "retrieves the original hash" do
+ require "yaml"
+ expect(YAML.load(serializer.dump(hash))).to eq(hash)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/cache/cache_path_spec.rb b/spec/bundler/cache/cache_path_spec.rb
new file mode 100644
index 0000000000..69d3809964
--- /dev/null
+++ b/spec/bundler/cache/cache_path_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle package" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "with --cache-path" do
+ it "caches gems at given path" do
+ bundle :package, "cache-path" => "vendor/cache-foo"
+ expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with config cache_path" do
+ it "caches gems at given path" do
+ bundle "config cache_path vendor/cache-foo"
+ bundle :package
+ expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with absolute --cache-path" do
+ it "caches gems at given path" do
+ bundle :package, "cache-path" => "/tmp/cache-foo"
+ expect(bundled_app("/tmp/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
new file mode 100644
index 0000000000..4a0b953830
--- /dev/null
+++ b/spec/bundler/cache/gems_spec.rb
@@ -0,0 +1,304 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle cache" do
+ shared_examples_for "when there are only gemsources" do
+ before :each do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+ bundle! :cache
+ end
+
+ it "copies the .gem file to vendor/cache" do
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "uses the cache as a source when installing gems" do
+ build_gem "omg", :path => bundled_app("vendor/cache")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "omg"
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0.0"
+ end
+
+ it "uses the cache as a source when installing gems with --local" do
+ system_gems [], :path => :bundle_path
+ bundle "install --local"
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "does not reinstall gems from the cache if they exist on the system" do
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "does not reinstall gems from the cache if they exist in the bundle" do
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ gemfile <<-G
+ gem "rack"
+ G
+
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ bundle! :install, :local => true
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "creates a lockfile" do
+ cache_gems "rack-1.0.0"
+
+ gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "cache"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ context "using system gems" do
+ before { bundle! "config path.system true" }
+ it_behaves_like "when there are only gemsources"
+ end
+
+ context "installing into a local path" do
+ before { bundle! "config path ./.bundle" }
+ it_behaves_like "when there are only gemsources"
+ end
+
+ describe "when there is a built-in gem", :ruby => "2.0" do
+ before :each do
+ build_repo2 do
+ build_gem "builtin_gem", "1.0.2"
+ end
+
+ build_gem "builtin_gem", "1.0.2", :to_system => true do |s|
+ s.summary = "This builtin_gem is bundled with Ruby"
+ end
+
+ FileUtils.rm("#{system_gem_path}/cache/builtin_gem-1.0.2.gem")
+ end
+
+ it "uses builtin gems when installing to system gems" do
+ bundle! "config path.system true"
+ install_gemfile %(gem 'builtin_gem', '1.0.2')
+ expect(the_bundle).to include_gems("builtin_gem 1.0.2")
+ end
+
+ it "caches remote and builtin gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'builtin_gem', '1.0.2'
+ gem 'rack', '1.0.0'
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/builtin_gem-1.0.2.gem")).to exist
+ end
+
+ it "doesn't make remote request after caching the gem" do
+ build_gem "builtin_gem_2", "1.0.2", :path => bundled_app("vendor/cache") do |s|
+ s.summary = "This builtin_gem is bundled with Ruby"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'builtin_gem_2', '1.0.2'
+ G
+
+ bundle "install --local"
+ expect(the_bundle).to include_gems("builtin_gem_2 1.0.2")
+ end
+
+ it "errors if the builtin gem isn't available to cache" do
+ bundle! "config path.system true"
+
+ install_gemfile <<-G
+ gem 'builtin_gem', '1.0.2'
+ G
+
+ bundle :cache
+ expect(exitstatus).to_not eq(0) if exitstatus
+ expect(out).to include("builtin_gem-1.0.2 is built in to Ruby, and can't be cached")
+ end
+ end
+
+ describe "when there are also git sources" do
+ before do
+ build_git "foo"
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "still works" do
+ bundle :cache
+
+ system_gems []
+ bundle "install --local"
+
+ expect(the_bundle).to include_gems("rack 1.0.0", "foo 1.0")
+ end
+
+ it "should not explode if the lockfile is not present" do
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ bundle :cache
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "when previously cached" do
+ before :each do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "actionpack"
+ G
+ bundle :cache
+ expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("actionpack-2.3.2")).to exist
+ expect(cached_gem("activesupport-2.3.2")).to exist
+ end
+
+ it "re-caches during install" do
+ cached_gem("rack-1.0.0").rmtree
+ bundle :install
+ expect(out).to include("Updating files in vendor/cache")
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "adds and removes when gems are updated" do
+ update_repo2
+ bundle "update", :all => bundle_update_requires_all?
+ expect(cached_gem("rack-1.2")).to exist
+ expect(cached_gem("rack-1.0.0")).not_to exist
+ end
+
+ it "adds new gems and dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails"
+ G
+ expect(cached_gem("rails-2.3.2")).to exist
+ expect(cached_gem("activerecord-2.3.2")).to exist
+ end
+
+ it "removes .gems for removed gems and dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+ expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("actionpack-2.3.2")).not_to exist
+ expect(cached_gem("activesupport-2.3.2")).not_to exist
+ end
+
+ it "removes .gems when gem changes to git source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ gem "actionpack"
+ G
+ expect(cached_gem("rack-1.0.0")).not_to exist
+ expect(cached_gem("actionpack-2.3.2")).to exist
+ expect(cached_gem("activesupport-2.3.2")).to exist
+ end
+
+ it "doesn't remove gems that are for another platform" do
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ bundle :cache
+ expect(cached_gem("platform_specific-1.0-java")).to exist
+ end
+
+ simulate_new_machine
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ expect(cached_gem("platform_specific-1.0-#{Bundler.local_platform}")).to exist
+ expect(cached_gem("platform_specific-1.0-java")).to exist
+ end
+
+ it "doesn't remove gems with mismatched :rubygems_version or :date" do
+ cached_gem("rack-1.0.0").rmtree
+ build_gem "rack", "1.0.0",
+ :path => bundled_app("vendor/cache"),
+ :rubygems_version => "1.3.2"
+ simulate_new_machine
+
+ bundle :install
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "handles directories and non .gem files in the cache" do
+ bundled_app("vendor/cache/foo").mkdir
+ File.open(bundled_app("vendor/cache/bar"), "w") {|f| f.write("not a gem") }
+ bundle :cache
+ end
+
+ it "does not say that it is removing gems when it isn't actually doing so" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "cache"
+ bundle "install"
+ expect(out).not_to match(/removing/i)
+ end
+
+ it "does not warn about all if it doesn't have any git/path dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "cache"
+ expect(out).not_to match(/\-\-all/)
+ end
+
+ it "should install gems with the name bundler in them (that aren't bundler)" do
+ build_gem "foo-bundler", "1.0",
+ :path => bundled_app("vendor/cache")
+
+ install_gemfile <<-G
+ gem "foo-bundler"
+ G
+
+ expect(the_bundle).to include_gems "foo-bundler 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
new file mode 100644
index 0000000000..33387dbbb2
--- /dev/null
+++ b/spec/bundler/cache/git_spec.rb
@@ -0,0 +1,214 @@
+# frozen_string_literal: true
+
+RSpec.describe "git base name" do
+ it "base_name should strip private repo uris" do
+ source = Bundler::Source::Git.new("uri" => "git@github.com:bundler.git")
+ expect(source.send(:base_name)).to eq("bundler")
+ end
+
+ it "base_name should strip network share paths" do
+ source = Bundler::Source::Git.new("uri" => "//MachineName/ShareFolder")
+ expect(source.send(:base_name)).to eq("ShareFolder")
+ end
+end
+
+%w[cache package].each do |cmd|
+ RSpec.describe "bundle #{cmd} with git" do
+ it "copies repository to vendor cache and uses it" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies repository to vendor cache and uses it even when installed with bundle --path" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "install --path vendor/bundle"
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "runs twice without exploding" do
+ build_git "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(last_command.stdout).to include "Updating files in vendor/cache"
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "tracks updates" do
+ git = build_git "foo"
+ old_ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ ref = git.ref_for("master", 11)
+ expect(ref).not_to eq(old_ref)
+
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ run! "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "tracks updates when specifying the gem" do
+ git = build_git "foo"
+ old_ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle! cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ ref = git.ref_for("master", 11)
+ expect(ref).not_to eq(old_ref)
+
+ bundle "update foo"
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ run "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "uses the local repository to generate the cache" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :master
+ G
+
+ bundle %(config local.foo #{lib_path("foo-1.0")})
+ bundle "install"
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-invalid-#{ref}")).to exist
+
+ # Updating the local still uses the local.
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :LOCAL"
+ end
+
+ run "require 'foo'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "copies repository to vendor cache, including submodules" do
+ build_git "submodule", "1.0"
+
+ git = build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ ref = git.ref_for("master", 11)
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}/submodule-1.0")).to exist
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ end
+
+ it "displays warning message when detecting git repo in Gemfile", :bundler => "< 2" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}"
+
+ expect(out).to include("Your Gemfile contains path and git dependencies.")
+ end
+
+ it "does not display warning message if cache_all is set in bundle config" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle cmd
+
+ expect(out).not_to include("Your Gemfile contains path and git dependencies.")
+ end
+
+ it "caches pre-evaluated gemspecs" do
+ git = build_git "foo"
+
+ # Insert a gemspec method that shells out
+ spec_lines = lib_path("foo-1.0/foo.gemspec").read.split("\n")
+ spec_lines.insert(-2, "s.description = `echo bob`")
+ update_git("foo") {|s| s.write "foo.gemspec", spec_lines.join("\n") }
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ ref = git.ref_for("master", 11)
+ gemspec = bundled_app("vendor/cache/foo-1.0-#{ref}/foo.gemspec").read
+ expect(gemspec).to_not match("`echo bob`")
+ end
+ end
+end
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
new file mode 100644
index 0000000000..8c6a843476
--- /dev/null
+++ b/spec/bundler/cache/path_spec.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+%w[cache package].each do |cmd|
+ RSpec.describe "bundle #{cmd} with path" do
+ it "is no-op when the path is within the bundle" do
+ build_lib "foo", :path => bundled_app("lib/foo")
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{bundled_app("lib/foo")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies when the path is outside the bundle " do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies when the path is outside the bundle and the paths intersect" do
+ libname = File.basename(Dir.pwd) + "_gem"
+ libpath = File.join(File.dirname(Dir.pwd), libname)
+
+ build_lib libname, :path => libpath
+
+ install_gemfile <<-G
+ gem "#{libname}", :path => '#{libpath}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/#{libname}")).to exist
+ expect(bundled_app("vendor/cache/#{libname}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf libpath
+ expect(the_bundle).to include_gems "#{libname} 1.0"
+ end
+
+ it "updates the path on each cache" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ build_lib "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0")).to exist
+ FileUtils.rm_rf lib_path("foo-1.0")
+
+ run "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "removes stale entries cache" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ install_gemfile <<-G
+ gem "bar", :path => '#{lib_path("bar-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/bar-1.0")).not_to exist
+ end
+
+ it "raises a warning without --all", :bundler => "< 2" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd
+ expect(out).to match(/please pass the \-\-all flag/)
+ expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
+ end
+
+ it "stores the given flag" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ build_lib "bar"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ gem "bar", :path => '#{lib_path("bar-1.0")}'
+ G
+
+ bundle cmd
+ expect(bundled_app("vendor/cache/bar-1.0")).to exist
+ end
+
+ it "can rewind chosen configuration" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ build_lib "baz"
+
+ gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ gem "baz", :path => '#{lib_path("baz-1.0")}'
+ G
+
+ bundle "#{cmd} --no-all"
+ expect(bundled_app("vendor/cache/baz-1.0")).not_to exist
+ end
+ end
+end
diff --git a/spec/bundler/cache/platform_spec.rb b/spec/bundler/cache/platform_spec.rb
new file mode 100644
index 0000000000..c0622a3c94
--- /dev/null
+++ b/spec/bundler/cache/platform_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle cache with multiple platforms" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :mri, :rbx do
+ gem "rack", "1.0.0"
+ end
+
+ platforms :jruby do
+ gem "activesupport", "2.3.5"
+ end
+ G
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ activesupport (2.3.5)
+
+ PLATFORMS
+ ruby
+ java
+
+ DEPENDENCIES
+ rack (1.0.0)
+ activesupport (2.3.5)
+ G
+
+ cache_gems "rack-1.0.0", "activesupport-2.3.5"
+ end
+
+ it "ensures that a successful bundle install does not delete gems for other platforms" do
+ bundle! "install"
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
+ end
+
+ it "ensures that a successful bundle update does not delete gems for other platforms" do
+ bundle! "update", :all => bundle_update_requires_all?
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
+ end
+end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
new file mode 100644
index 0000000000..9f11adbcf8
--- /dev/null
+++ b/spec/bundler/commands/add_spec.rb
@@ -0,0 +1,217 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle add" do
+ before :each do
+ build_repo2 do
+ build_gem "foo", "1.1"
+ build_gem "foo", "2.0"
+ build_gem "baz", "1.2.3"
+ build_gem "bar", "0.12.3"
+ build_gem "cat", "0.12.3.pre"
+ build_gem "dog", "1.1.3.pre"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "weakling", "~> 0.0.1"
+ G
+ end
+
+ context "when no gems are specified" do
+ it "shows error" do
+ bundle "add"
+
+ expect(last_command.bundler_err).to include("Please specify gems to add")
+ end
+ end
+
+ describe "without version specified" do
+ it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do
+ bundle "add 'bar'"
+ expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(the_bundle).to include_gems "bar 0.12.3"
+ end
+
+ it "version requirement becomes ~> major.minor when resolved version is > 1.0" do
+ bundle "add 'baz'"
+ expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/)
+ expect(the_bundle).to include_gems "baz 1.2.3"
+ end
+
+ it "version requirement becomes ~> major.minor.patch.pre when resolved version is < 1.0" do
+ bundle "add 'cat'"
+ expect(bundled_app("Gemfile").read).to match(/gem "cat", "~> 0.12.3.pre"/)
+ expect(the_bundle).to include_gems "cat 0.12.3.pre"
+ end
+
+ it "version requirement becomes ~> major.minor.pre when resolved version is > 1.0.pre" do
+ bundle "add 'dog'"
+ expect(bundled_app("Gemfile").read).to match(/gem "dog", "~> 1.1.pre"/)
+ expect(the_bundle).to include_gems "dog 1.1.3.pre"
+ end
+ end
+
+ describe "with --version" do
+ it "adds dependency of specified version and runs install" do
+ bundle "add 'foo' --version='~> 1.0'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 1.0"/)
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "adds multiple version constraints when specified" do
+ requirements = ["< 3.0", "> 1.0"]
+ bundle "add 'foo' --version='#{requirements.join(", ")}'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --group" do
+ it "adds dependency for the specified group" do
+ bundle "add 'foo' --group='development'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 2.0", :group => :development/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+
+ it "adds dependency to more than one group" do
+ bundle "add 'foo' --group='development, test'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 2.0", :groups => \[:development, :test\]/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --source" do
+ it "adds dependency with specified source" do
+ bundle "add 'foo' --source='file://#{gem_repo2}'"
+
+ expect(bundled_app("Gemfile").read).to match(%r{gem "foo", "~> 2.0", :source => "file:\/\/#{gem_repo2}"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --skip-install" do
+ it "adds gem to Gemfile but is not installed" do
+ bundle "add foo --skip-install --version=2.0"
+
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "= 2.0"/)
+ expect(the_bundle).to_not include_gems "foo 2.0"
+ end
+ end
+
+ it "using combination of short form options works like long form" do
+ bundle "add 'foo' -s='file://#{gem_repo2}' -g='development' -v='~>1.0'"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 1.0", :group => :development, :source => "file://#{gem_repo2}")
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "shows error message when version is not formatted correctly" do
+ bundle "add 'foo' -v='~>1 . 0'"
+ expect(out).to match("Invalid gem requirement pattern '~>1 . 0'")
+ end
+
+ it "shows error message when gem cannot be found" do
+ bundle "add 'werk_it'"
+ expect(out).to match("Could not find gem 'werk_it' in")
+
+ bundle "add 'werk_it' -s='file://#{gem_repo2}'"
+ expect(out).to match("Could not find gem 'werk_it' in rubygems repository")
+ end
+
+ it "shows error message when source cannot be reached" do
+ bundle "add 'baz' --source='http://badhostasdf'"
+ expect(out).to include("Could not reach host badhostasdf. Check your network connection and try again.")
+
+ bundle "add 'baz' --source='file://does/not/exist'"
+ expect(out).to include("Could not fetch specs from file://does/not/exist/")
+ end
+
+ describe "with --optimistic" do
+ it "adds optimistic version" do
+ bundle! "add 'foo' --optimistic"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", ">= 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --strict option" do
+ it "adds strict version" do
+ bundle! "add 'foo' --strict"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "= 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with no option" do
+ it "adds pessimistic version" do
+ bundle! "add 'foo'"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --optimistic and --strict" do
+ it "throws error" do
+ bundle "add 'foo' --strict --optimistic"
+
+ expect(out).to include("You can not specify `--strict` and `--optimistic` at the same time")
+ end
+ end
+
+ context "multiple gems" do
+ it "adds multiple gems to gemfile" do
+ bundle! "add bar baz"
+
+ expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/)
+ end
+
+ it "throws error if any of the specified gems are present in the gemfile with different version" do
+ bundle "add weakling bar"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).")
+ end
+ end
+
+ describe "when a gem is added which is already specified in Gemfile with version" do
+ it "shows an error when added with different version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ G
+
+ bundle "add 'rack' --version=1.1"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+
+ it "shows error when added without version requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ G
+
+ bundle "add 'rack'"
+
+ expect(out).to include("Gem already added.")
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).not_to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+ end
+
+ describe "when a gem is added which is already specified in Gemfile without version" do
+ it "shows an error when added with different version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle "add 'rack' --version=1.1"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("If you want to update the gem version, run `bundle update rack`.")
+ expect(out).not_to include("You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+ end
+end
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
new file mode 100644
index 0000000000..6a705d3423
--- /dev/null
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -0,0 +1,453 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle binstubs <gem>" do
+ context "when the gem exists in the lockfile" do
+ it "sets up the binstub" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "does not install other binstubs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle "binstubs rails"
+
+ expect(bundled_app("bin/rackup")).not_to exist
+ expect(bundled_app("bin/rails")).to exist
+ end
+
+ it "does install multiple binstubs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle "binstubs rails rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ end
+
+ it "allows installing all binstubs" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle! :binstubs, :all => true
+
+ expect(bundled_app("bin/rails")).to exist
+ expect(bundled_app("bin/rake")).to exist
+ end
+
+ it "displays an error when used without any gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs"
+ expect(exitstatus).to eq(1) if exitstatus
+ expect(out).to include("`bundle binstubs` needs at least one gem to run.")
+ end
+
+ it "displays an error when used with --all and gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack", :all => true
+ expect(last_command).to be_failure
+ expect(last_command.bundler_err).to include("Cannot specify --all with specific gems")
+ end
+
+ context "when generating bundle binstub outside bundler" do
+ it "should abort" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ File.open("bin/bundle", "wb") do |file|
+ file.print "OMG"
+ end
+
+ sys_exec "bin/rackup"
+
+ expect(last_command.stderr).to include("was not generated by Bundler")
+ end
+ end
+
+ context "the bundle binstub" do
+ before do
+ if system_bundler_version == :bundler
+ system_gems :bundler
+ elsif system_bundler_version
+ build_repo4 do
+ build_gem "bundler", system_bundler_version do |s|
+ s.executables = "bundle"
+ s.bindir = "exe"
+ s.write "exe/bundle", "puts %(system bundler #{system_bundler_version}\\n\#{ARGV.inspect})"
+ end
+ end
+ system_gems "bundler-#{system_bundler_version}", :gem_repo => gem_repo4
+ end
+ build_repo2 do
+ build_gem "prints_loaded_gems", "1.0" do |s|
+ s.executables = "print_loaded_gems"
+ s.bindir = "exe"
+ s.write "exe/print_loaded_gems", <<-R
+ specs = Gem.loaded_specs.values.reject {|s| Bundler.rubygems.spec_default_gem?(s) }
+ puts specs.map(&:full_name).sort.inspect
+ R
+ end
+ end
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "prints_loaded_gems"
+ G
+ bundle! "binstubs bundler rack prints_loaded_gems"
+ end
+
+ # When environment has a same version of bundler as default gems.
+ # `system_gems "bundler-x.y.z"` will detect system binstub.
+ # We need to avoid it by virtual version of bundler.
+ let(:system_bundler_version) { Gem::Version.new(Bundler::VERSION).bump.to_s }
+
+ context "when system bundler was used" do
+ # Support master branch of bundler
+ if ENV["BUNDLER_SPEC_SUB_VERSION"]
+ let(:system_bundler_version) { Bundler::VERSION }
+ end
+ it "runs bundler" do
+ sys_exec! "#{bundled_app("bin/bundle")} install"
+ expect(out).to eq %(system bundler #{system_bundler_version}\n["install"])
+ end
+ end
+
+ context "when BUNDLER_VERSION is set" do
+ let(:system_bundler_version) { Bundler::VERSION }
+
+ it "runs the correct version of bundler" do
+ sys_exec "BUNDLER_VERSION='999.999.999' #{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+
+ context "when a lockfile exists with a locked bundler version" do
+ let(:system_bundler_version) { Bundler::VERSION }
+
+ it "runs the correct version of bundler when the version is newer" do
+ lockfile lockfile.gsub(system_bundler_version, "999.999.999")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+
+ it "runs the correct version of bundler when the version is older" do
+ simulate_bundler_version "55"
+ lockfile lockfile.gsub(system_bundler_version, "44.0")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (44.0) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '44.0'`")
+ end
+
+ it "runs the correct version of bundler when the version is a pre-release" do
+ simulate_bundler_version "55"
+ lockfile lockfile.gsub(system_bundler_version, "2.12.0.a")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (2.12.0.a) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '2.12.0.a'`")
+ end
+ end
+
+ context "when update --bundler is called" do
+ before { lockfile.gsub(system_bundler_version, "1.1.1") }
+
+ it "calls through to the latest bundler version" do
+ sys_exec! "#{bundled_app("bin/bundle")} update --bundler"
+ expect(last_command.stdout).to eq %(system bundler #{system_bundler_version}\n["update", "--bundler"])
+ end
+
+ it "calls through to the explicit bundler version" do
+ sys_exec "#{bundled_app("bin/bundle")} update --bundler=999.999.999"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+
+ context "without a lockfile" do
+ it "falls back to the latest installed bundler" do
+ FileUtils.rm bundled_app("Gemfile.lock")
+ sys_exec! bundled_app("bin/bundle").to_s
+ expect(out).to eq "system bundler #{system_bundler_version}\n[]"
+ end
+ end
+
+ context "using another binstub" do
+ let(:system_bundler_version) { :bundler }
+ it "loads all gems" do
+ sys_exec! bundled_app("bin/print_loaded_gems").to_s
+ # RG < 2.0.14 didn't have a `Gem::Specification#default_gem?`
+ # This is dirty detection for old RG versions.
+ if File.dirname(Bundler.load.specs["bundler"][0].loaded_from) =~ %r{specifications/default}
+ expect(out).to eq %(["prints_loaded_gems-1.0", "rack-1.2"])
+ else
+ expect(out).to eq %(["bundler-#{Bundler::VERSION}", "prints_loaded_gems-1.0", "rack-1.2"])
+ end
+ end
+
+ context "when requesting a different bundler version" do
+ before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") }
+
+ it "attempts to load that version", :ruby_repo do
+ sys_exec bundled_app("bin/rackup").to_s
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+ end
+ end
+
+ it "installs binstubs from git gems" do
+ FileUtils.mkdir_p(lib_path("foo/bin"))
+ FileUtils.touch(lib_path("foo/bin/foo"))
+ build_git "foo", "1.0", :path => lib_path("foo") do |s|
+ s.executables = %w[foo]
+ end
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ bundle "binstubs foo"
+
+ expect(bundled_app("bin/foo")).to exist
+ end
+
+ it "installs binstubs from path gems" do
+ FileUtils.mkdir_p(lib_path("foo/bin"))
+ FileUtils.touch(lib_path("foo/bin/foo"))
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.executables = %w[foo]
+ end
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle "binstubs foo"
+
+ expect(bundled_app("bin/foo")).to exist
+ end
+
+ it "sets correct permissions for binstubs" do
+ with_umask(0o002) do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+ binary = bundled_app("bin/rackup")
+ expect(File.stat(binary).mode.to_s(8)).to eq("100775")
+ end
+ end
+
+ context "when using --shebang" do
+ it "sets the specified shebang for the the binstub" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --shebang jruby"
+
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env jruby\n")
+ end
+ end
+ end
+
+ context "when the gem doesn't exist" do
+ it "displays an error with correct status" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "binstubs doesnt_exist"
+
+ expect(exitstatus).to eq(7) if exitstatus
+ expect(out).to include("Could not find gem 'doesnt_exist'.")
+ end
+ end
+
+ context "--path" do
+ it "sets the binstubs dir" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --path exec"
+
+ expect(bundled_app("exec/rackup")).to exist
+ end
+
+ it "setting is saved for bundle install", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle! "binstubs rack", forgotten_command_line_options([:path, :bin] => "exec")
+ bundle! :install
+
+ expect(bundled_app("exec/rails")).to exist
+ end
+ end
+
+ context "with --standalone option" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "generates a standalone binstub" do
+ bundle! "binstubs rack --standalone"
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "generates a binstub that does not depend on rubygems or bundler" do
+ bundle! "binstubs rack --standalone"
+ expect(File.read(bundled_app("bin/rackup"))).to_not include("Gem.bin_path")
+ end
+
+ context "when specified --path option" do
+ it "generates a standalone binstub at the given path" do
+ bundle! "binstubs rack --standalone --path foo"
+ expect(bundled_app("foo/rackup")).to exist
+ end
+ end
+ end
+
+ context "when the bin already exists" do
+ it "doesn't overwrite and warns" do
+ FileUtils.mkdir_p(bundled_app("bin"))
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(File.read(bundled_app("bin/rackup"))).to eq("OMG")
+ expect(out).to include("Skipped rackup")
+ expect(out).to include("overwrite skipped stubs, use --force")
+ end
+
+ context "when using --force" do
+ it "overwrites the binstub" do
+ FileUtils.mkdir_p(bundled_app("bin"))
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --force"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(File.read(bundled_app("bin/rackup"))).not_to eq("OMG")
+ end
+ end
+ end
+
+ context "when the gem has no bins" do
+ it "suggests child gems if they have bins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ G
+
+ bundle "binstubs rack-obama"
+ expect(out).to include("rack-obama has no executables")
+ expect(out).to include("rack has: rackup")
+ end
+
+ it "works if child gems don't have bins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "actionpack"
+ G
+
+ bundle "binstubs actionpack"
+ expect(out).to include("no executables for the gem actionpack")
+ end
+
+ it "works if the gem has development dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_development_dependency"
+ G
+
+ bundle "binstubs with_development_dependency"
+ expect(out).to include("no executables for the gem with_development_dependency")
+ end
+ end
+
+ context "when BUNDLE_INSTALL is specified" do
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack"
+ expect(out).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does nothing when already up to date" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => 1 }
+ expect(out).not_to include("Installing rack 1.0.0")
+ end
+ end
+end
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
new file mode 100644
index 0000000000..f2af446fbf
--- /dev/null
+++ b/spec/bundler/commands/check_spec.rb
@@ -0,0 +1,354 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle check" do
+ it "returns success when the Gemfile is satisfied" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "works with the --gemfile flag when not in the directory" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ Dir.chdir tmp
+ bundle "check --gemfile bundled_app/Gemfile"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "creates a Gemfile.lock by default if one does not exist" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "check"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "does not create a Gemfile.lock if --dry-run was passed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "check --dry-run"
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "prints a generic error if the missing gems are unresolvable" do
+ system_gems ["rails-2.3.2"]
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "prints a generic error if a Gemfile.lock does not exist and a toplevel dependency does not exist" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(exitstatus).to be > 0 if exitstatus
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "prints a generic message if you changed your lockfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rails'
+ G
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rails_fail'
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "rails_fail"
+ G
+
+ bundle :check
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "remembers --without option from install", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ group :foo do
+ gem "rack"
+ end
+ G
+
+ bundle! "install --without foo"
+ bundle! "check"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "uses the without setting" do
+ bundle! "config without foo"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ group :foo do
+ gem "rack"
+ end
+ G
+
+ bundle! "check"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "ensures that gems are actually installed and not just cached" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :foo
+ G
+
+ bundle :install, forgotten_command_line_options(:without => "foo")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "check"
+ expect(out).to include("* rack (1.0.0)")
+ expect(exitstatus).to eq(1) if exitstatus
+ end
+
+ it "ignores missing gems restricted to other platforms" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ platforms :#{not_local_tag} do
+ gem "activesupport"
+ end
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ activesupport
+ G
+
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "works with env conditionals" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ env :NOT_GOING_TO_BE_SET do
+ gem "activesupport"
+ end
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ activesupport
+ G
+
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "outputs an error when the default Gemfile is not found" do
+ bundle :check
+ expect(exitstatus).to eq(10) if exitstatus
+ expect(out).to include("Could not locate Gemfile")
+ end
+
+ it "does not output fatal error message" do
+ bundle :check
+ expect(exitstatus).to eq(10) if exitstatus
+ expect(out).not_to include("Unfortunately, a fatal error has occurred. ")
+ end
+
+ it "should not crash when called multiple times on a new machine" do
+ gemfile <<-G
+ gem 'rails', '3.0.0.beta3'
+ gem 'paperclip', :git => 'git://github.com/thoughtbot/paperclip.git'
+ G
+
+ simulate_new_machine
+ bundle "check"
+ last_out = out
+ 3.times do
+ bundle :check
+ expect(out).to eq(last_out)
+ end
+ end
+
+ it "fails when there's no lock file and frozen is set" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:deployment => true)
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ bundle :check
+ expect(last_command).to be_failure
+ end
+
+ context "--path", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle "install --path vendor/bundle"
+
+ FileUtils.rm_rf(bundled_app(".bundle"))
+ end
+
+ it "returns success" do
+ bundle! "check --path vendor/bundle"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "should write to .bundle/config", :bundler => "< 2" do
+ bundle "check --path vendor/bundle"
+ bundle! "check"
+ end
+ end
+
+ context "--path vendor/bundle after installing gems in the default directory" do
+ it "returns false" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle "check --path vendor/bundle"
+ expect(exitstatus).to eq(1) if exitstatus
+ expect(out).to match(/The following gems are missing/)
+ end
+ end
+
+ describe "when locked" do
+ before :each do
+ system_gems "rack-1.0.0"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+ end
+
+ it "returns success when the Gemfile is satisfied" do
+ bundle :install
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "shows what is missing with the current Gemfile if it is not satisfied" do
+ simulate_new_machine
+ bundle :check
+ expect(out).to match(/The following gems are missing/)
+ expect(out).to include("* rack (1.0")
+ end
+ end
+
+ describe "BUNDLED WITH" do
+ def lock_with(bundler_version = nil)
+ lock = <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if bundler_version
+ lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ end
+
+ lock
+ end
+
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ lockfile lock_with(nil)
+ bundle :check
+ lockfile_should_be lock_with(nil)
+ end
+ end
+
+ context "is newer" do
+ it "does not change the lock but warns" do
+ lockfile lock_with(Bundler::VERSION.succ)
+ bundle! :check
+ expect(last_command.bundler_err).to include("the running version of Bundler (#{Bundler::VERSION}) is older than the version that created the lockfile (#{Bundler::VERSION.succ})")
+ lockfile_should_be lock_with(Bundler::VERSION.succ)
+ end
+ end
+
+ context "is older" do
+ it "does not change the lock" do
+ lockfile lock_with("1.10.1")
+ bundle :check
+ lockfile_should_be lock_with("1.10.1")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
new file mode 100644
index 0000000000..37cbeeb4e7
--- /dev/null
+++ b/spec/bundler/commands/clean_spec.rb
@@ -0,0 +1,771 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle clean" do
+ def should_have_gems(*gems)
+ gems.each do |g|
+ expect(vendored_gems("gems/#{g}")).to exist
+ expect(vendored_gems("specifications/#{g}.gemspec")).to exist
+ expect(vendored_gems("cache/#{g}.gem")).to exist
+ end
+ end
+
+ def should_not_have_gems(*gems)
+ gems.each do |g|
+ expect(vendored_gems("gems/#{g}")).not_to exist
+ expect(vendored_gems("specifications/#{g}.gemspec")).not_to exist
+ expect(vendored_gems("cache/#{g}.gem")).not_to exist
+ end
+ end
+
+ it "removes unused gems that are different" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+ bundle! "install"
+
+ bundle! :clean
+
+ expect(out).to include("Removing foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0"
+ should_not_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes old version of gem if unused" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ gem "foo"
+ G
+ bundle "install"
+
+ bundle :clean
+
+ expect(out).to include("Removing rack (0.9.1)")
+
+ should_have_gems "foo-1.0", "rack-1.0.0"
+ should_not_have_gems "rack-0.9.1"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes new version of gem if unused" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+ bundle! "update rack"
+
+ bundle! :clean
+
+ expect(out).to include("Removing rack (1.0.0)")
+
+ should_have_gems "foo-1.0", "rack-0.9.1"
+ should_not_have_gems "rack-1.0.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes gems in bundle without groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+
+ group :test_group do
+ gem "rack", "1.0.0"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle "install", forgotten_command_line_options(:without => "test_group")
+ bundle :clean
+
+ expect(out).to include("Removing rack (1.0.0)")
+
+ should_have_gems "foo-1.0"
+ should_not_have_gems "rack-1.0.0"
+
+ expect(vendored_gems("bin/rackup")).to_not exist
+ end
+
+ it "does not remove cached git dir if it's being used" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+ git_path = lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ bundle :clean
+
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ cache_path = Bundler::VERSION.start_with?("1.") ? vendored_gems("cache/bundler/git/foo-1.0-#{digest}") : home(".bundle/cache/git/foo-1.0-#{digest}")
+ expect(cache_path).to exist
+ end
+
+ it "removes unused git gems" do
+ build_git "foo", :path => lib_path("foo")
+ git_path = lib_path("foo")
+ revision = revision_for(git_path)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+ bundle "install"
+
+ bundle :clean
+
+ expect(out).to include("Removing foo (#{revision[0..11]})")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).not_to exist
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ expect(vendored_gems("cache/bundler/git/foo-#{digest}")).not_to exist
+
+ expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes old git gems" do
+ build_git "foo-bar", :path => lib_path("foo-bar")
+ revision = revision_for(lib_path("foo-bar"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{lib_path("foo-bar")}" do
+ gem "foo-bar"
+ end
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ update_git "foo", :path => lib_path("foo-bar")
+ revision2 = revision_for(lib_path("foo-bar"))
+
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle! :clean
+
+ expect(out).to include("Removing foo-bar (#{revision[0..11]})")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("bundler/gems/foo-bar-#{revision[0..11]}")).not_to exist
+ expect(vendored_gems("bundler/gems/foo-bar-#{revision2[0..11]}")).to exist
+
+ expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "does not remove nested gems in a git repo" do
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 3.0"
+ end
+ revision = revision_for(lib_path("rails"))
+
+ gemfile <<-G
+ gem "activesupport", :git => "#{lib_path("rails")}", :ref => '#{revision}'
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle :clean
+ expect(out).to include("")
+
+ expect(vendored_gems("bundler/gems/rails-#{revision[0..11]}")).to exist
+ end
+
+ it "does not remove git sources that are in without groups" do
+ build_git "foo", :path => lib_path("foo")
+ git_path = lib_path("foo")
+ revision = revision_for(git_path)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ group :test do
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ end
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :without => "test")
+
+ bundle :clean
+
+ expect(out).to include("")
+ expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).to exist
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ expect(vendored_gems("cache/bundler/git/foo-#{digest}")).to_not exist
+ end
+
+ it "does not blow up when using without groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+
+ group :development do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :without => "development")
+
+ bundle :clean
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "displays an error when used without --path" do
+ bundle! "config path.system true"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ bundle :clean
+
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to include("--force")
+ end
+
+ # handling bundle clean upgrade path from the pre's
+ it "removes .gem/.gemspec file even if there's no corresponding gem dir" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+ bundle "install"
+
+ FileUtils.rm(vendored_gems("bin/rackup"))
+ FileUtils.rm_rf(vendored_gems("gems/thin-1.0"))
+ FileUtils.rm_rf(vendored_gems("gems/rack-1.0.0"))
+
+ bundle :clean
+
+ should_not_have_gems "thin-1.0", "rack-1.0"
+ should_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).not_to exist
+ end
+
+ it "does not call clean automatically when using system gems" do
+ bundle! "config path.system true"
+
+ bundle! :config
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+
+ bundle! "info thin"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ sys_exec! "gem list"
+ expect(out).to include("rack (1.0.0)").and include("thin (1.0)")
+ end
+
+ it "--clean should override the bundle setting on install", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => true)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "install"
+
+ should_have_gems "rack-1.0.0"
+ should_not_have_gems "thin-1.0"
+ end
+
+ it "--clean should override the bundle setting on update", :bundler => "< 2" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => true)
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+
+ should_have_gems "foo-1.0.1"
+ should_not_have_gems "foo-1.0"
+ end
+
+ it "automatically cleans when path has not been set", :bundler => "2" do
+ build_repo2
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! "update", :all => true
+
+ files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*"))
+ files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") }
+ expect(files.sort).to eq %w[
+ /cache/foo-1.0.1.gem
+ /gems/foo-1.0.1
+ /specifications/foo-1.0.1.gemspec
+ ]
+ end
+
+ it "does not clean automatically on --path" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "install"
+
+ should_have_gems "rack-1.0.0", "thin-1.0"
+ end
+
+ it "does not clean on bundle update with --path" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! :update, :all => bundle_update_requires_all?
+ should_have_gems "foo-1.0", "foo-1.0.1"
+ end
+
+ it "does not clean on bundle update when using --system" do
+ bundle! "config path.system true"
+
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install"
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+ bundle! :update, :all => bundle_update_requires_all?
+
+ sys_exec! "gem list"
+ expect(out).to include("foo (1.0.1, 1.0)")
+ end
+
+ it "cleans system gems when --force is used" do
+ bundle! "config path.system true"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "rack"
+ G
+ bundle :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle :install
+ bundle "clean --force"
+
+ expect(out).to include("Removing foo (1.0)")
+ sys_exec "gem list"
+ expect(out).not_to include("foo (1.0)")
+ expect(out).to include("rack (1.0.0)")
+ end
+
+ describe "when missing permissions" do
+ before { ENV["BUNDLE_PATH__SYSTEM"] = "true" }
+ let(:system_cache_path) { system_gem_path("cache") }
+ after do
+ FileUtils.chmod(0o755, system_cache_path)
+ end
+ it "returns a helpful error message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "rack"
+ G
+ bundle :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle :install
+
+ FileUtils.chmod(0o500, system_cache_path)
+
+ bundle :clean, :force => true
+
+ expect(out).to include(system_gem_path.to_s)
+ expect(out).to include("grant write permissions")
+
+ sys_exec "gem list"
+ expect(out).to include("foo (1.0)")
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+
+ it "cleans git gems with a 7 length git revision" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ # mimic 7 length git revisions in Gemfile.lock
+ gemfile_lock = File.read(bundled_app("Gemfile.lock")).split("\n")
+ gemfile_lock.each_with_index do |line, index|
+ gemfile_lock[index] = line[0..(11 + 7)] if line.include?(" revision:")
+ end
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.print gemfile_lock.join("\n")
+ end
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ bundle :clean
+
+ expect(out).not_to include("Removing foo (1.0 #{revision[0..6]})")
+
+ expect(vendored_gems("bundler/gems/foo-1.0-#{revision[0..6]}")).to exist
+ end
+
+ it "when using --force on system gems, it doesn't remove binaries" do
+ bundle! "config path.system true"
+
+ build_repo2
+ update_repo2 do
+ build_gem "bindir" do |s|
+ s.bindir = "exe"
+ s.executables = "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "bindir"
+ G
+ bundle :install
+
+ bundle "clean --force"
+
+ sys_exec "foo"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("1.0")
+ end
+
+ it "doesn't blow up on path gems without a .gempsec" do
+ relative_path = "vendor/private_gems/bar-1.0"
+ absolute_path = bundled_app(relative_path)
+ FileUtils.mkdir_p("#{absolute_path}/lib/bar")
+ File.open("#{absolute_path}/lib/bar/bar.rb", "wb") do |file|
+ file.puts "module Bar; end"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "bar", "1.0", :path => "#{relative_path}"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :clean
+ end
+
+ it "doesn't remove gems in dry-run mode with path set" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "clean --dry-run"
+
+ expect(out).not_to include("Removing foo (1.0)")
+ expect(out).to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "doesn't remove gems in dry-run mode with no path set" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "configuration --delete path"
+
+ bundle "clean --dry-run"
+
+ expect(out).not_to include("Removing foo (1.0)")
+ expect(out).to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "doesn't store dry run as a config setting" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+ bundle "config dry_run false"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "clean"
+
+ expect(out).to include("Removing foo (1.0)")
+ expect(out).not_to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0"
+ should_not_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "weakling"
+ G
+
+ bundle! "config auto_install 1"
+ bundle! :clean
+ expect(out).to include("Installing weakling 0.0.3")
+ should_have_gems "thin-1.0", "rack-1.0.0", "weakling-0.0.3"
+ should_not_have_gems "foo-1.0"
+ end
+
+ it "doesn't remove extensions artifacts from bundled git gems after clean", :ruby_repo, :rubygems => "2.2" do
+ build_git "very_simple_git_binary", &:add_c_extension
+
+ revision = revision_for(lib_path("very_simple_git_binary-1.0"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+ expect(vendored_gems("bundler/gems/extensions")).to exist
+ expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
+
+ bundle! :clean
+ expect(out).to eq("")
+
+ expect(vendored_gems("bundler/gems/extensions")).to exist
+ expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
+ end
+
+ it "removes extension directories", :ruby_repo, :rubygems => "2.2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "very_simple_binary"
+ gem "simple_binary"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ very_simple_binary_extensions_dir =
+ Pathname.glob("#{vendored_gems}/extensions/*/*/very_simple_binary-1.0").first
+
+ simple_binary_extensions_dir =
+ Pathname.glob("#{vendored_gems}/extensions/*/*/simple_binary-1.0").first
+
+ expect(very_simple_binary_extensions_dir).to exist
+ expect(simple_binary_extensions_dir).to exist
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "simple_binary"
+ G
+
+ bundle! "install"
+ bundle! :clean
+ expect(out).to eq("Removing very_simple_binary (1.0)")
+
+ expect(very_simple_binary_extensions_dir).not_to exist
+ expect(simple_binary_extensions_dir).to exist
+ end
+end
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
new file mode 100644
index 0000000000..9e49357465
--- /dev/null
+++ b/spec/bundler/commands/config_spec.rb
@@ -0,0 +1,384 @@
+# frozen_string_literal: true
+
+RSpec.describe ".bundle/config" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ G
+ end
+
+ describe "config" do
+ before { bundle "config foo bar" }
+
+ it "prints a detailed report of local and user configuration" do
+ bundle "config"
+
+ expect(out).to include("Settings are listed in order of priority. The top value will be used")
+ expect(out).to include("foo\nSet for the current user")
+ expect(out).to include(": \"bar\"")
+ end
+
+ context "given --parseable flag" do
+ it "prints a minimal report of local and user configuration" do
+ bundle "config --parseable"
+ expect(out).to include("foo=bar")
+ end
+
+ context "with global config" do
+ it "prints config assigned to local scope" do
+ bundle "config --local foo bar2"
+ bundle "config --parseable"
+ expect(out).to include("foo=bar2")
+ end
+ end
+
+ context "with env overwrite" do
+ it "prints config with env" do
+ bundle "config --parseable", :env => { "BUNDLE_FOO" => "bar3" }
+ expect(out).to include("foo=bar3")
+ end
+ end
+ end
+ end
+
+ describe "BUNDLE_APP_CONFIG" do
+ it "can be moved with an environment variable" do
+ ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(bundled_app(".bundle")).not_to exist
+ expect(tmp("foo/bar/config")).to exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "can provide a relative path with the environment variable" do
+ FileUtils.mkdir_p bundled_app("omg")
+ Dir.chdir bundled_app("omg")
+
+ ENV["BUNDLE_APP_CONFIG"] = "../foo"
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(bundled_app(".bundle")).not_to exist
+ expect(bundled_app("../foo/config")).to exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "global" do
+ before(:each) { bundle :install }
+
+ it "is the default" do
+ bundle "config foo global"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "can also be set explicitly" do
+ bundle! "config --global foo global"
+ run! "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "has lower precedence than local" do
+ bundle "config --local foo local"
+
+ bundle "config --global foo global"
+ expect(out).to match(/Your application has set foo to "local"/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "has lower precedence than env" do
+ begin
+ ENV["BUNDLE_FOO"] = "env"
+
+ bundle "config --global foo global"
+ expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("env")
+ ensure
+ ENV.delete("BUNDLE_FOO")
+ end
+ end
+
+ it "can be deleted" do
+ bundle "config --global foo global"
+ bundle "config --delete foo"
+
+ run "puts Bundler.settings[:foo] == nil"
+ expect(out).to eq("true")
+ end
+
+ it "warns when overriding" do
+ bundle "config --global foo previous"
+ bundle "config --global foo global"
+ expect(out).to match(/You are replacing the current global value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "does not warn when using the same value twice" do
+ bundle "config --global foo value"
+ bundle "config --global foo value"
+ expect(out).not_to match(/You are replacing the current global value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("value")
+ end
+
+ it "expands the path at time of setting" do
+ bundle "config --global local.foo .."
+ run "puts Bundler.settings['local.foo']"
+ expect(out).to eq(File.expand_path(Dir.pwd + "/.."))
+ end
+
+ it "saves with parseable option" do
+ bundle "config --global --parseable foo value"
+ expect(out).to eq("foo=value")
+ run "puts Bundler.settings['foo']"
+ expect(out).to eq("value")
+ end
+
+ context "when replacing a current value with the parseable flag" do
+ before { bundle "config --global foo value" }
+ it "prints the current value in a parseable format" do
+ bundle "config --global --parseable foo value2"
+ expect(out).to eq "foo=value2"
+ run "puts Bundler.settings['foo']"
+ expect(out).to eq("value2")
+ end
+ end
+ end
+
+ describe "local" do
+ before(:each) { bundle :install }
+
+ it "can also be set explicitly" do
+ bundle "config --local foo local"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "has higher precedence than env" do
+ begin
+ ENV["BUNDLE_FOO"] = "env"
+ bundle "config --local foo local"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ ensure
+ ENV.delete("BUNDLE_FOO")
+ end
+ end
+
+ it "can be deleted" do
+ bundle "config --local foo local"
+ bundle "config --delete foo"
+
+ run "puts Bundler.settings[:foo] == nil"
+ expect(out).to eq("true")
+ end
+
+ it "warns when overriding" do
+ bundle "config --local foo previous"
+ bundle "config --local foo local"
+ expect(out).to match(/You are replacing the current local value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "expands the path at time of setting" do
+ bundle "config --local local.foo .."
+ run "puts Bundler.settings['local.foo']"
+ expect(out).to eq(File.expand_path(Dir.pwd + "/.."))
+ end
+
+ it "can be deleted with parseable option" do
+ bundle "config --local foo value"
+ bundle "config --delete --parseable foo"
+ expect(out).to eq ""
+ run "puts Bundler.settings['foo'] == nil"
+ expect(out).to eq("true")
+ end
+ end
+
+ describe "env" do
+ before(:each) { bundle :install }
+
+ it "can set boolean properties via the environment" do
+ ENV["BUNDLE_FROZEN"] = "true"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("true")
+ end
+
+ it "can set negative boolean properties via the environment" do
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = "false"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = "0"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = ""
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+ end
+
+ it "can set properties with periods via the environment" do
+ ENV["BUNDLE_FOO__BAR"] = "baz"
+
+ run "puts Bundler.settings['foo.bar']"
+ expect(out).to eq("baz")
+ end
+ end
+
+ describe "parseable option" do
+ it "prints an empty string" do
+ bundle "config foo --parseable"
+
+ expect(out).to eq ""
+ end
+
+ it "only prints the value of the config" do
+ bundle "config foo local"
+ bundle "config foo --parseable"
+
+ expect(out).to eq "foo=local"
+ end
+
+ it "can print global config" do
+ bundle "config --global bar value"
+ bundle "config bar --parseable"
+
+ expect(out).to eq "bar=value"
+ end
+
+ it "prefers local config over global" do
+ bundle "config --local bar value2"
+ bundle "config --global bar value"
+ bundle "config bar --parseable"
+
+ expect(out).to eq "bar=value2"
+ end
+ end
+
+ describe "gem mirrors" do
+ before(:each) { bundle :install }
+
+ it "configures mirrors using keys with `mirror.`" do
+ bundle "config --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ run(<<-E)
+Bundler.settings.gem_mirrors.each do |k, v|
+ puts "\#{k} => \#{v}"
+end
+E
+ expect(out).to eq("http://gems.example.org/ => http://gem-mirror.example.org/")
+ end
+ end
+
+ describe "quoting" do
+ before(:each) { gemfile "# no gems" }
+ let(:long_string) do
+ "--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
+ "--with-xslt-dir=/usr/pkg"
+ end
+
+ it "saves quotes" do
+ bundle "config foo something\\'"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("something'")
+ end
+
+ it "doesn't return quotes around values", :ruby => "1.9" do
+ bundle "config foo '1'"
+ run "puts Bundler.settings.send(:global_config_file).read"
+ expect(out).to include('"1"')
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("1")
+ end
+
+ it "doesn't duplicate quotes around values", :if => (RUBY_VERSION >= "2.1") do
+ bundled_app(".bundle").mkpath
+ File.open(bundled_app(".bundle/config"), "w") do |f|
+ f.write 'BUNDLE_FOO: "$BUILD_DIR"'
+ end
+
+ bundle "config bar baz"
+ run "puts Bundler.settings.send(:local_config_file).read"
+
+ # Starting in Ruby 2.1, YAML automatically adds double quotes
+ # around some values, including $ and newlines.
+ expect(out).to include('BUNDLE_FOO: "$BUILD_DIR"')
+ end
+
+ it "doesn't duplicate quotes around long wrapped values" do
+ bundle "config foo #{long_string}"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq(long_string)
+
+ bundle "config bar baz"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq(long_string)
+ end
+ end
+
+ describe "very long lines" do
+ before(:each) { bundle :install }
+
+ let(:long_string) do
+ "--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
+ "--with-xslt-dir=/usr/pkg"
+ end
+
+ let(:long_string_without_special_characters) do
+ "here is quite a long string that will wrap to a second line but will not be " \
+ "surrounded by quotes"
+ end
+
+ it "doesn't wrap values" do
+ bundle "config foo #{long_string}"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to match(long_string)
+ end
+
+ it "can read wrapped unquoted values" do
+ bundle "config foo #{long_string_without_special_characters}"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to match(long_string_without_special_characters)
+ end
+ end
+end
+
+RSpec.describe "setting gemfile via config" do
+ context "when only the non-default Gemfile exists" do
+ it "persists the gemfile location to .bundle/config" do
+ File.open(bundled_app("NotGemfile"), "w") do |f|
+ f.write <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ expect(File.exist?(".bundle/config")).to eq(true)
+
+ bundle "config"
+ expect(out).to include("NotGemfile")
+ end
+ end
+end
diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb
new file mode 100644
index 0000000000..9bf66e8f5b
--- /dev/null
+++ b/spec/bundler/commands/console_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle console", :bundler => "< 2" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded" do
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "uses IRB as default console" do
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":irb_binding")
+ end
+
+ it "starts another REPL if configured as such" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "pry"
+ G
+ bundle "config console pry"
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":__pry__")
+ end
+
+ it "falls back to IRB if the other REPL isn't available" do
+ bundle "config console pry"
+ # make sure pry isn't there
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":irb_binding")
+ end
+
+ it "doesn't load any other groups" do
+ bundle "console" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+
+ describe "when given a group" do
+ it "loads the given group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("2.3.5")
+ end
+
+ it "loads the default group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "doesn't load other groups" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts RACK_MIDDLEWARE")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :console do |input, _, _|
+ input.puts("puts 'hello'")
+ input.puts("exit")
+ end
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("hello")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+end
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
new file mode 100644
index 0000000000..5260e6cb36
--- /dev/null
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require "find"
+require "stringio"
+require "bundler/cli"
+require "bundler/cli/doctor"
+
+RSpec.describe "bundle doctor" do
+ before(:each) do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ @stdout = StringIO.new
+
+ [:error, :warn].each do |method|
+ allow(Bundler.ui).to receive(method).and_wrap_original do |m, message|
+ m.call message
+ @stdout.puts message
+ end
+ end
+ end
+
+ context "when all files in home are readable/writable" do
+ before(:each) do
+ stat = double("stat")
+ unwritable_file = double("file")
+ allow(Find).to receive(:find).with(Bundler.home.to_s) { [unwritable_file] }
+ allow(File).to receive(:stat).with(unwritable_file) { stat }
+ allow(stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(unwritable_file) { true }
+ allow(File).to receive(:readable?).with(unwritable_file) { true }
+ end
+
+ it "exits with no message if the installed gem has no C extensions" do
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to be_empty
+ end
+
+ it "exits with no message if the installed gem's C extension dylib breakage is fine" do
+ doctor = Bundler::CLI::Doctor.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true)
+ expect { doctor.run }.not_to(raise_error, @stdout.string)
+ expect(@stdout.string).to be_empty
+ end
+
+ it "exits with a message if one of the linked libraries is missing" do
+ doctor = Bundler::CLI::Doctor.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false)
+ expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
+ The following gems are missing OS dependencies:
+ * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
+ * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
+ E
+ end
+ end
+
+ context "when home contains files that are not readable/writable" do
+ before(:each) do
+ @stat = double("stat")
+ @unwritable_file = double("file")
+ allow(Find).to receive(:find).with(Bundler.home.to_s) { [@unwritable_file] }
+ allow(File).to receive(:stat).with(@unwritable_file) { @stat }
+ end
+
+ it "exits with an error if home contains files that are not readable/writable" do
+ allow(@stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { false }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+
+ context "when home contains files that are not owned by the current process" do
+ before(:each) do
+ allow(@stat).to receive(:uid) { 0o0000 }
+ end
+
+ it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { false }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+
+ it "exits with a warning if home contains files that are read/write but not owned by current user" do
+ allow(File).to receive(:writable?).with(@unwritable_file) { true }
+ allow(File).to receive(:readable?).with(@unwritable_file) { true }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
new file mode 100644
index 0000000000..6835305d55
--- /dev/null
+++ b/spec/bundler/commands/exec_spec.rb
@@ -0,0 +1,858 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle exec" do
+ let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
+ before :each do
+ system_gems(system_gems_to_install, :path => :bundle_path)
+ end
+
+ it "works with --gemfile flag" do
+ create_file "CustomGemfile", <<-G
+ gem "rack", "1.0.0"
+ G
+
+ bundle "exec --gemfile CustomGemfile rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "activates the correct gem" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+
+ it "works when the bins are in ~/.bundle" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "works when running from a random directory", :ruby_repo do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(out).to include("1.0.0")
+ end
+
+ it "works when exec'ing something else" do
+ install_gemfile 'gem "rack"'
+ bundle "exec echo exec"
+ expect(out).to eq("exec")
+ end
+
+ it "works when exec'ing to ruby" do
+ install_gemfile 'gem "rack"'
+ bundle "exec ruby -e 'puts %{hi}'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("hi")
+ end
+
+ it "accepts --verbose" do
+ install_gemfile 'gem "rack"'
+ bundle "exec --verbose echo foobar"
+ expect(out).to eq("foobar")
+ end
+
+ it "passes --verbose to command if it is given after the command" do
+ install_gemfile 'gem "rack"'
+ bundle "exec echo --verbose"
+ expect(out).to eq("--verbose")
+ end
+
+ it "handles --keep-file-descriptors" do
+ require "tempfile"
+
+ command = Tempfile.new("io-test")
+ command.sync = true
+ command.write <<-G
+ if ARGV[0]
+ IO.for_fd(ARGV[0].to_i)
+ else
+ require 'tempfile'
+ io = Tempfile.new("io-test-fd")
+ args = %W[#{Gem.ruby} -I#{lib} #{bindir.join("bundle")} exec --keep-file-descriptors #{Gem.ruby} #{command.path} \#{io.to_i}]
+ args << { io.to_i => io } if RUBY_VERSION >= "2.0"
+ exec(*args)
+ end
+ G
+
+ install_gemfile ""
+ with_env_vars "RUBYOPT" => "-r#{spec_dir.join("support/hax")}" do
+ sys_exec "#{Gem.ruby} #{command.path}"
+ end
+
+ if Bundler.current_ruby.ruby_2?
+ expect(out).to eq("")
+ else
+ expect(out).to eq("Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec.")
+ end
+
+ expect(err).to lack_errors
+ end
+
+ it "accepts --keep-file-descriptors" do
+ install_gemfile ""
+ bundle "exec --keep-file-descriptors echo foobar"
+
+ expect(err).to lack_errors
+ end
+
+ it "can run a command named --verbose" do
+ install_gemfile 'gem "rack"'
+ File.open("--verbose", "w") do |f|
+ f.puts "#!/bin/sh"
+ f.puts "echo foobar"
+ end
+ File.chmod(0o744, "--verbose")
+ with_path_as(".") do
+ bundle "exec -- --verbose"
+ end
+ expect(out).to eq("foobar")
+ end
+
+ it "handles different versions in different bundles" do
+ build_repo2 do
+ build_gem "rack_two", "1.0.0" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ Dir.chdir bundled_app2 do
+ install_gemfile bundled_app2("Gemfile"), <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_two", "1.0.0"
+ G
+ end
+
+ bundle! "exec rackup"
+
+ expect(out).to eq("0.9.1")
+
+ Dir.chdir bundled_app2 do
+ bundle! "exec rackup"
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ it "handles gems installed with --without" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => "middleware")
+ source "file://#{gem_repo1}"
+ gem "rack" # rack 0.9.1 and 1.0 exist
+
+ group :middleware do
+ gem "rack_middleware" # rack_middleware depends on rack 0.9.1
+ end
+ G
+
+ bundle "exec rackup"
+
+ expect(out).to eq("0.9.1")
+ expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ end
+
+ it "does not duplicate already exec'ed RUBYOPT" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ rubyopt = ENV["RUBYOPT"]
+ rubyopt = "-rbundler/setup #{rubyopt}"
+
+ bundle "exec 'echo $RUBYOPT'"
+ expect(out).to have_rubyopts(rubyopt)
+
+ bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt }
+ expect(out).to have_rubyopts(rubyopt)
+ end
+
+ it "does not duplicate already exec'ed RUBYLIB" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ rubylib = ENV["RUBYLIB"]
+ rubylib = "#{rubylib}".split(File::PATH_SEPARATOR).unshift "#{bundler_path}"
+ rubylib = rubylib.uniq.join(File::PATH_SEPARATOR)
+
+ bundle "exec 'echo $RUBYLIB'"
+ expect(out).to include(rubylib)
+
+ bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib }
+ expect(out).to include(rubylib)
+ end
+
+ it "errors nicely when the argument doesn't exist" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec foobarbaz"
+ expect(exitstatus).to eq(127) if exitstatus
+ expect(out).to include("bundler: command not found: foobarbaz")
+ expect(out).to include("Install missing gem executables with `bundle install`")
+ end
+
+ it "errors nicely when the argument is not executable" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec touch foo"
+ bundle "exec ./foo"
+ expect(exitstatus).to eq(126) if exitstatus
+ expect(out).to include("bundler: not executable: ./foo")
+ end
+
+ it "errors nicely when no arguments are passed" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec"
+ expect(exitstatus).to eq(128) if exitstatus
+ expect(out).to include("bundler: exec needs a command to run")
+ end
+
+ it "raises a helpful error when exec'ing to something outside of the bundle", :ruby_repo, :rubygems => ">= 2.5.2" do
+ bundle! "config clean false" # want to keep the rackup binstub
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "with_license"
+ G
+ [true, false].each do |l|
+ bundle! "config disable_exec_load #{l}"
+ bundle "exec rackup"
+ expect(last_command.stderr).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
+ end
+ end
+
+ # Different error message on old RG versions (before activate_bin_path) because they
+ # called `Kernel#gem` directly
+ it "raises a helpful error when exec'ing to something outside of the bundle", :rubygems => "< 2.5.2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "with_license"
+ G
+ [true, false].each do |l|
+ bundle! "config disable_exec_load #{l}"
+ bundle "exec rackup", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(last_command.stderr).to include "rack is not part of the bundle. Add it to your Gemfile."
+ end
+ end
+
+ describe "with help flags" do
+ each_prefix = proc do |string, &blk|
+ 1.upto(string.length) {|l| blk.call(string[0, l]) }
+ end
+ each_prefix.call("exec") do |exec|
+ describe "when #{exec} is used" do
+ before(:each) do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ create_file("print_args", <<-'RUBY')
+ #!/usr/bin/env ruby
+ puts "args: #{ARGV.inspect}"
+ RUBY
+ bundled_app("print_args").chmod(0o755)
+ end
+
+ it "shows executable's man page when --help is after the executable" do
+ bundle "#{exec} print_args --help"
+ expect(out).to eq('args: ["--help"]')
+ end
+
+ it "shows executable's man page when --help is after the executable and an argument" do
+ bundle "#{exec} print_args foo --help"
+ expect(out).to eq('args: ["foo", "--help"]')
+
+ bundle "#{exec} print_args foo bar --help"
+ expect(out).to eq('args: ["foo", "bar", "--help"]')
+
+ bundle "#{exec} print_args foo --help bar"
+ expect(out).to eq('args: ["foo", "--help", "bar"]')
+ end
+
+ it "shows executable's man page when the executable has a -" do
+ FileUtils.mv(bundled_app("print_args"), bundled_app("docker-template"))
+ bundle "#{exec} docker-template build discourse --help"
+ expect(out).to eq('args: ["build", "discourse", "--help"]')
+ end
+
+ it "shows executable's man page when --help is after another flag" do
+ bundle "#{exec} print_args --bar --help"
+ expect(out).to eq('args: ["--bar", "--help"]')
+ end
+
+ it "uses executable's original behavior for -h" do
+ bundle "#{exec} print_args -h"
+ expect(out).to eq('args: ["-h"]')
+ end
+
+ it "shows bundle-exec's man page when --help is between exec and the executable" do
+ with_fake_man do
+ bundle "#{exec} --help cat"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when --help is before exec" do
+ with_fake_man do
+ bundle "--help #{exec}"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when -h is before exec" do
+ with_fake_man do
+ bundle "-h #{exec}"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when --help is after exec" do
+ with_fake_man do
+ bundle "#{exec} --help"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when -h is after exec" do
+ with_fake_man do
+ bundle "#{exec} -h"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+ end
+ end
+ end
+
+ describe "with gem executables" do
+ describe "run from a random directory", :ruby_repo do
+ before(:each) do
+ install_gemfile <<-G
+ gem "rack"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("1.0.0")
+ expect(out).to include("1.0.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to include("1.0.0")
+ end
+ end
+
+ describe "from gems bundled via :path" do
+ before(:each) do
+ build_lib "fizz", :path => home("fizz") do |s|
+ s.executables = "fizz"
+ end
+
+ install_gemfile <<-G
+ gem "fizz", :path => "#{File.expand_path(home("fizz"))}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+
+ bundle "exec fizz"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "from gems bundled via :git" do
+ before(:each) do
+ build_git "fizz_git" do |s|
+ s.executables = "fizz_git"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_git", :git => "#{lib_path("fizz_git-1.0")}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_git"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec fizz_git"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "from gems bundled via :git with no gemspec" do
+ before(:each) do
+ build_git "fizz_no_gemspec", :gemspec => false do |s|
+ s.executables = "fizz_no_gemspec"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_no_gemspec", "1.0", :git => "#{lib_path("fizz_no_gemspec-1.0")}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "exec rackup"
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ describe "with gems bundled via :path with invalid gemspecs", :ruby_repo do
+ it "outputs the gemspec validation errors", :rubygems => ">= 1.7.2" do
+ build_lib "foo"
+
+ gemspec = lib_path("foo-1.0").join("foo.gemspec").to_s
+ File.open(gemspec, "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = 'foo'
+ s.version = '1.0'
+ s.summary = 'TODO: Add summary'
+ s.authors = 'Me'
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "exec irb"
+
+ expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid")
+ expect(err).to match('"TODO" is not a summary')
+ end
+ end
+
+ describe "with gems bundled for deployment" do
+ it "works when calling bundler from another script" do
+ gemfile <<-G
+ module Monkey
+ def bin_path(a,b,c)
+ raise Gem::GemNotFoundException.new('Fail')
+ end
+ end
+ Bundler.rubygems.extend(Monkey)
+ G
+ bundle "install --deployment"
+ bundle "exec ruby -e '`#{bindir.join("bundler")} -v`; puts $?.success?'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to match("true")
+ end
+ end
+
+ context "`load`ing a ruby file instead of `exec`ing" do
+ let(:path) { bundled_app("ruby_executable") }
+ let(:shebang) { "#!/usr/bin/env ruby" }
+ let(:executable) { <<-RUBY.gsub(/^ */, "").strip }
+ #{shebang}
+
+ require "rack"
+ puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}"
+ puts "ARGS: \#{$0} \#{ARGV.join(' ')}"
+ puts "RACK: \#{RACK}"
+ process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ puts "PROCESS: \#{process_title}"
+ RUBY
+
+ before do
+ path.open("w") {|f| f << executable }
+ path.chmod(0o755)
+
+ install_gemfile <<-G
+ gem "rack"
+ G
+ end
+
+ let(:exec) { "EXEC: load" }
+ let(:args) { "ARGS: #{path} arg1 arg2" }
+ let(:rack) { "RACK: 1.0.0" }
+ let(:process) do
+ title = "PROCESS: #{path}"
+ title += " arg1 arg2" if RUBY_VERSION >= "2.1"
+ title
+ end
+ let(:exit_code) { 0 }
+ let(:expected) { [exec, args, rack, process].join("\n") }
+ let(:expected_err) { "" }
+
+ subject { bundle "exec #{path} arg1 arg2", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" } }
+
+ shared_examples_for "it runs" do
+ it "like a normally executed executable" do
+ subject
+ expect(exitstatus).to eq(exit_code) if exitstatus
+ expect(last_command.stderr).to eq(expected_err)
+ expect(last_command.stdout).to eq(expected)
+ end
+ end
+
+ it_behaves_like "it runs"
+
+ context "the executable exits explicitly" do
+ let(:executable) { super() << "\nexit #{exit_code}\nputs 'POST_EXIT'\n" }
+
+ context "with exit 0" do
+ it_behaves_like "it runs"
+ end
+
+ context "with exit 99" do
+ let(:exit_code) { 99 }
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "the executable exits by SignalException" do
+ let(:executable) do
+ ex = super()
+ ex << "\n"
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ # Ruby < 1.9 needs a flush for a exit by signal, later
+ # rubies do not
+ ex << "STDOUT.flush\n"
+ end
+ ex << "raise SignalException, 'SIGTERM'\n"
+ ex
+ end
+ let(:expected_err) { ENV["TRAVIS"] ? "Terminated" : "" }
+ let(:exit_code) do
+ # signal mask 128 + plus signal 15 -> TERM
+ # this is specified by C99
+ 128 + 15
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable is empty", :bundler => "< 2" do
+ let(:executable) { "" }
+
+ let(:exit_code) { 0 }
+ let(:expected) { "#{path} is empty" }
+ let(:expected_err) { "" }
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ # Kernel#exec in ruby < 1.9 will raise Errno::ENOEXEC if the command content is empty,
+ # even if the command is set as an executable.
+ pending "Kernel#exec is different"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "the executable is empty", :bundler => "2" do
+ let(:executable) { "" }
+
+ let(:exit_code) { 0 }
+ let(:expected_err) { "#{path} is empty" }
+ let(:expected) { "" }
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises", :bundler => "< 2" do
+ let(:executable) { super() << "\nraise 'ERROR'" }
+ let(:exit_code) { 1 }
+ let(:expected) { super() << "\nbundler: failed to load command: #{path} (#{path})" }
+ let(:expected_err) do
+ "RuntimeError: ERROR\n #{path}:10" +
+ (Bundler.current_ruby.ruby_18? ? "" : ":in `<top (required)>'")
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises", :bundler => "2" do
+ let(:executable) { super() << "\nraise 'ERROR'" }
+ let(:exit_code) { 1 }
+ let(:expected_err) do
+ "bundler: failed to load command: #{path} (#{path})" \
+ "\nRuntimeError: ERROR\n #{path}:10:in `<top (required)>'"
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises an error without a backtrace", :bundler => "< 2" do
+ let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" }
+ let(:exit_code) { 1 }
+ let(:expected) { super() << "\nbundler: failed to load command: #{path} (#{path})" }
+ let(:expected_err) { "Err: Err" }
+
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises an error without a backtrace", :bundler => "2" do
+ let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" }
+ let(:exit_code) { 1 }
+ let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\nErr: Err" }
+ let(:expected) { super() }
+
+ it_behaves_like "it runs"
+ end
+
+ context "when the file uses the current ruby shebang", :ruby_repo do
+ let(:shebang) { "#!#{Gem.ruby}" }
+ it_behaves_like "it runs"
+ end
+
+ context "when Bundler.setup fails", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { <<-EOS.strip }
+\e[31mCould not find gem 'rack (= 2)' in any of the gem sources listed in your Gemfile.\e[0m
+\e[33mRun `bundle install` to install missing gems.\e[0m
+ EOS
+
+ it_behaves_like "it runs"
+ end
+
+ context "when Bundler.setup fails", :bundler => "2" do
+ before do
+ gemfile <<-G
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { <<-EOS.strip }
+\e[31mCould not find gem 'rack (= 2)' in locally installed gems.
+The source contains 'rack' at: 1.0.0\e[0m
+\e[33mRun `bundle install` to install missing gems.\e[0m
+ EOS
+
+ it_behaves_like "it runs"
+ end
+
+ context "when the executable exits non-zero via at_exit" do
+ let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" }
+ let(:exit_code) { 1 }
+
+ it_behaves_like "it runs"
+ end
+
+ context "when disable_exec_load is set" do
+ let(:exec) { "EXEC: exec" }
+ let(:process) { "PROCESS: ruby #{path} arg1 arg2" }
+
+ before do
+ bundle "config disable_exec_load true"
+ end
+
+ it_behaves_like "it runs"
+ end
+
+ context "regarding $0 and __FILE__" do
+ let(:executable) { super() + <<-'RUBY' }
+
+ puts "$0: #{$0.inspect}"
+ puts "__FILE__: #{__FILE__.inspect}"
+ RUBY
+
+ let(:expected) { super() + <<-EOS.chomp }
+
+$0: #{path.to_s.inspect}
+__FILE__: #{path.to_s.inspect}
+ EOS
+
+ it_behaves_like "it runs"
+
+ context "when the path is relative" do
+ let(:path) { super().relative_path_from(bundled_app) }
+
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ pending "relative paths have ./ __FILE__"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "when the path is relative with a leading ./" do
+ let(:path) { Pathname.new("./#{super().relative_path_from(Pathname.pwd)}") }
+
+ if LessThanProc.with(RUBY_VERSION).call("< 1.9")
+ pending "relative paths with ./ have absolute __FILE__"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+ end
+
+ context "signal handling" do
+ let(:test_signals) do
+ open3_reserved_signals = %w[CHLD CLD PIPE]
+ reserved_signals = %w[SEGV BUS ILL FPE VTALRM KILL STOP EXIT]
+ bundler_signals = %w[INT]
+
+ Signal.list.keys - (bundler_signals + reserved_signals + open3_reserved_signals)
+ end
+
+ context "signals being trapped by bundler" do
+ let(:executable) { strip_whitespace <<-RUBY }
+ #{shebang}
+ begin
+ Thread.new do
+ puts 'Started' # For process sync
+ STDOUT.flush
+ sleep 1 # ignore quality_spec
+ raise "Didn't receive INT at all"
+ end.join
+ rescue Interrupt
+ puts "foo"
+ end
+ RUBY
+
+ it "receives the signal", :ruby => ">= 1.9.3" do
+ bundle!("exec #{path}") do |_, o, thr|
+ o.gets # Consumes 'Started' and ensures that thread has started
+ Process.kill("INT", thr.pid)
+ end
+
+ expect(out).to eq("foo")
+ end
+ end
+
+ context "signals not being trapped by bunder" do
+ let(:executable) { strip_whitespace <<-RUBY }
+ #{shebang}
+
+ signals = #{test_signals.inspect}
+ result = signals.map do |sig|
+ Signal.trap(sig, "IGNORE")
+ end
+ puts result.select { |ret| ret == "IGNORE" }.count
+ RUBY
+
+ it "makes sure no unexpected signals are restored to DEFAULT" do
+ test_signals.each do |n|
+ Signal.trap(n, "IGNORE")
+ end
+
+ bundle!("exec #{path}")
+
+ expect(out).to eq(test_signals.count.to_s)
+ end
+ end
+ end
+ end
+
+ context "nested bundle exec" do
+ let(:system_gems_to_install) { super() << :bundler }
+
+ context "with shared gems disabled" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :install, :system_bundler => true, :path => "vendor/bundler"
+ end
+
+ it "overrides disable_shared_gems so bundler can be found" do
+ skip "bundler 1.16.x is not support with Ruby 2.6 on Travis CI" if RUBY_VERSION >= "2.6"
+
+ file = bundled_app("file_that_bundle_execs.rb")
+ create_file(file, <<-RB)
+ #!#{Gem.ruby}
+ puts `bundle exec echo foo`
+ RB
+ file.chmod(0o777)
+ bundle! "exec #{file}", :system_bundler => true
+ expect(out).to eq("foo")
+ end
+ end
+
+ context "with a system gem that shadows a default gem" do
+ let(:openssl_version) { "99.9.9" }
+ let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", :artifice => nil }
+
+ it "only leaves the default gem in the stdlib available" do
+ skip "openssl isn't a default gem" if expected.empty?
+
+ install_gemfile! "" # must happen before installing the broken system gem
+
+ build_repo4 do
+ build_gem "openssl", openssl_version do |s|
+ s.write("lib/openssl.rb", <<-RB)
+ raise "custom openssl should not be loaded, it's not in the gemfile!"
+ RB
+ end
+ end
+
+ system_gems(:bundler, "openssl-#{openssl_version}", :gem_repo => gem_repo4)
+
+ file = bundled_app("require_openssl.rb")
+ create_file(file, <<-RB)
+ #!/usr/bin/env ruby
+ require "openssl"
+ puts OpenSSL::VERSION
+ warn Gem.loaded_specs.values.map(&:full_name)
+ RB
+ file.chmod(0o777)
+
+ aggregate_failures do
+ expect(bundle!("exec #{file}", :artifice => nil)).to eq(expected)
+ expect(bundle!("exec bundle exec #{file}", :artifice => nil)).to eq(expected)
+ expect(bundle!("exec ruby #{file}", :artifice => nil)).to eq(expected)
+ # Ignore expectaion for default bundler gem conflict.
+ unless ENV["BUNDLER_SPEC_SUB_VERSION"]
+ expect(run!(file.read, :artifice => nil)).to eq(expected)
+ end
+ end
+
+ # sanity check that we get the newer, custom version without bundler
+ sys_exec("#{Gem.ruby} #{file}")
+ expect(last_command.stderr).to include("custom openssl should not be loaded")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
new file mode 100644
index 0000000000..56b1b6f722
--- /dev/null
+++ b/spec/bundler/commands/help_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle help" do
+ # RubyGems 1.4+ no longer load gem plugins so this test is no longer needed
+ it "complains if older versions of bundler are installed", :rubygems => "< 1.4" do
+ system_gems "bundler-0.8.1"
+
+ bundle "help"
+ expect(err).to include("older than 0.9")
+ expect(err).to include("running `gem cleanup bundler`.")
+ end
+
+ it "uses mann when available" do
+ with_fake_man do
+ bundle "help gemfile"
+ end
+ expect(out).to eq(%(["#{root}/man/gemfile.5"]))
+ end
+
+ it "prefixes bundle commands with bundle- when finding the groff files" do
+ with_fake_man do
+ bundle "help install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "simply outputs the txt file when there is no man on the path" do
+ with_path_as("") do
+ bundle "help install"
+ end
+ expect(out).to match(/BUNDLE-INSTALL/)
+ end
+
+ it "still outputs the old help for commands that do not have man pages yet" do
+ bundle "help version"
+ expect(out).to include("Prints the bundler's version information")
+ end
+
+ it "looks for a binary and executes it with --help option if it's named bundler-<task>" do
+ File.open(tmp("bundler-testtasks"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nputs ARGV.join(' ')\n"
+ end
+
+ with_path_added(tmp) do
+ bundle "help testtasks"
+ end
+
+ expect(exitstatus).to be_zero if exitstatus
+ expect(out).to eq("--help")
+ end
+
+ it "is called when the --help flag is used after the command" do
+ with_fake_man do
+ bundle "install --help"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the --help flag is used before the command" do
+ with_fake_man do
+ bundle "--help install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the -h flag is used before the command" do
+ with_fake_man do
+ bundle "-h install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the -h flag is used after the command" do
+ with_fake_man do
+ bundle "install -h"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "has helpful output when using --help flag for a non-existent command" do
+ with_fake_man do
+ bundle "instill -h"
+ end
+ expect(out).to include('Could not find command "instill".')
+ end
+
+ it "is called when only using the --help flag" do
+ with_fake_man do
+ bundle "--help"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
+
+ with_fake_man do
+ bundle "-h"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
+ end
+end
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
new file mode 100644
index 0000000000..a9ab8fc210
--- /dev/null
+++ b/spec/bundler/commands/info_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle info" do
+ context "info from specific gem in gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "prints information about the current gem" do
+ bundle "info rails"
+ expect(out).to include "* rails (2.3.2)
+\tSummary: This is just a fake gem for testing
+\tHomepage: http://example.com"
+ expect(out).to match(%r{Path\: .*\/rails\-2\.3\.2})
+ end
+
+ context "given a gem that is not installed" do
+ it "prints missing gem error" do
+ bundle "info foo"
+ expect(out).to eq "Could not find gem 'foo'."
+ end
+ end
+
+ context "given a default gem shippped in ruby", :ruby_repo do
+ it "prints information about the default gem", :if => (RUBY_VERSION >= "2.0") do
+ bundle "info rdoc"
+ expect(out).to include("* rdoc")
+ expect(out).to include("Default Gem: yes")
+ end
+ end
+
+ context "when gem does not have homepage" do
+ before do
+ build_repo1 do
+ build_gem "rails", "2.3.2" do |s|
+ s.executables = "rails"
+ s.summary = "Just another test gem"
+ end
+ end
+ end
+
+ it "excludes the homepage field from the output" do
+ expect(out).to_not include("Homepage:")
+ end
+ end
+
+ context "given --path option" do
+ it "prints the path to the gem" do
+ bundle "info rails"
+ expect(out).to match(%r{.*\/rails\-2\.3\.2})
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
new file mode 100644
index 0000000000..9b5bd95814
--- /dev/null
+++ b/spec/bundler/commands/init_spec.rb
@@ -0,0 +1,181 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle init" do
+ it "generates a Gemfile", :bundler => "< 2" do
+ bundle! :init
+ expect(out).to include("Writing new Gemfile")
+ expect(bundled_app("Gemfile")).to be_file
+ end
+
+ it "generates a gems.rb", :bundler => "2" do
+ bundle! :init
+ expect(out).to include("Writing new gems.rb")
+ expect(bundled_app("gems.rb")).to be_file
+ end
+
+ context "when a Gemfile already exists", :bundler => "< 2" do
+ before do
+ create_file "Gemfile", <<-G
+ gem "rails"
+ G
+ end
+
+ it "does not change existing Gemfiles" do
+ expect { bundle :init }.not_to change { File.read(bundled_app("Gemfile")) }
+ end
+
+ it "notifies the user that an existing Gemfile already exists" do
+ bundle :init
+ expect(out).to include("Gemfile already exists")
+ end
+ end
+
+ context "when gems.rb already exists", :bundler => ">= 2" do
+ before do
+ create_file("gems.rb", <<-G)
+ gem "rails"
+ G
+ end
+
+ it "does not change existing Gemfiles" do
+ expect { bundle :init }.not_to change { File.read(bundled_app("gems.rb")) }
+ end
+
+ it "notifies the user that an existing gems.rb already exists" do
+ bundle :init
+ expect(out).to include("gems.rb already exists")
+ end
+ end
+
+ context "when a Gemfile exists in a parent directory", :bundler => "< 2" do
+ let(:subdir) { "child_dir" }
+
+ it "lets users generate a Gemfile in a child directory" do
+ bundle! :init
+
+ FileUtils.mkdir bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle! :init
+ end
+
+ expect(out).to include("Writing new Gemfile")
+ expect(bundled_app("#{subdir}/Gemfile")).to be_file
+ end
+ end
+
+ context "when the dir is not writable by the current user" do
+ let(:subdir) { "child_dir" }
+
+ it "notifies the user that it can not write to it" do
+ FileUtils.mkdir bundled_app(subdir)
+ # chmod a-w it
+ mode = File.stat(bundled_app(subdir)).mode ^ 0o222
+ FileUtils.chmod mode, bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle :init
+ end
+
+ expect(out).to include("directory is not writable")
+ expect(Dir[bundled_app("#{subdir}/*")]).to be_empty
+ end
+ end
+
+ context "when a gems.rb file exists in a parent directory", :bundler => ">= 2" do
+ let(:subdir) { "child_dir" }
+
+ it "lets users generate a Gemfile in a child directory" do
+ bundle! :init
+
+ FileUtils.mkdir bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle! :init
+ end
+
+ expect(out).to include("Writing new gems.rb")
+ expect(bundled_app("#{subdir}/gems.rb")).to be_file
+ end
+ end
+
+ context "given --gemspec option", :bundler => "< 2" do
+ let(:spec_file) { tmp.join("test.gemspec") }
+
+ it "should generate from an existing gemspec" do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.add_dependency 'rack', '= 1.0.1'
+ s.add_development_dependency 'rspec', '1.2'
+ end
+ S
+ end
+
+ bundle :init, :gemspec => spec_file
+
+ gemfile = if Bundler::VERSION[0, 2] == "1."
+ bundled_app("Gemfile").read
+ else
+ bundled_app("gems.rb").read
+ end
+ expect(gemfile).to match(%r{source 'https://rubygems.org'})
+ expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
+ expect(gemfile.scan(/group :development/).size).to eq(1)
+ end
+
+ context "when gemspec file is invalid" do
+ it "notifies the user that specification is invalid" do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.invalid_method_name
+ end
+ S
+ end
+
+ bundle :init, :gemspec => spec_file
+ expect(last_command.bundler_err).to include("There was an error while loading `test.gemspec`")
+ end
+ end
+ end
+
+ context "when init_gems_rb setting is enabled" do
+ before { bundle "config init_gems_rb true" }
+
+ context "given --gemspec option", :bundler => "< 2" do
+ let(:spec_file) { tmp.join("test.gemspec") }
+
+ before do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.add_dependency 'rack', '= 1.0.1'
+ s.add_development_dependency 'rspec', '1.2'
+ end
+ S
+ end
+ end
+
+ it "should generate from an existing gemspec" do
+ bundle :init, :gemspec => spec_file
+
+ gemfile = bundled_app("gems.rb").read
+ expect(gemfile).to match(%r{source 'https://rubygems.org'})
+ expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
+ expect(gemfile.scan(/group :development/).size).to eq(1)
+ end
+
+ it "prints message to user" do
+ bundle :init, :gemspec => spec_file
+
+ expect(out).to include("Writing new gems.rb")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
new file mode 100644
index 0000000000..b7ffc89a34
--- /dev/null
+++ b/spec/bundler/commands/inject_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle inject", :bundler => "< 2" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "without a lockfile" do
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+ end
+
+ context "with a lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "adds the injected gems to the Gemfile" do
+ expect(bundled_app("Gemfile").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile").read).to match(/rack-obama/)
+ end
+
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+ end
+
+ context "with injected gems already in the Gemfile" do
+ it "doesn't add existing gems" do
+ bundle "inject 'rack' '> 0'"
+ expect(out).to match(/cannot specify the same gem twice/i)
+ end
+ end
+
+ context "incorrect arguments" do
+ it "fails when more than 2 arguments are passed" do
+ bundle "inject gem_name 1 v"
+ expect(out).to eq(<<-E.strip)
+ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"]
+Usage: "bundle inject GEM VERSION"
+ E
+ end
+ end
+
+ context "with source option" do
+ it "add gem with source option in gemfile" do
+ bundle "inject 'foo' '>0' --source file://#{gem_repo1}"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"foo\", \"> 0\", :source => \"file://#{gem_repo1}\""
+ expect(gemfile).to include str
+ end
+ end
+
+ context "with group option" do
+ it "add gem with group option in gemfile" do
+ bundle "inject 'rack-obama' '>0' --group=development"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"rack-obama\", \"> 0\", :group => :development"
+ expect(gemfile).to include str
+ end
+
+ it "add gem with multiple groups in gemfile" do
+ bundle "inject 'rack-obama' '>0' --group=development,test"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"rack-obama\", \"> 0\", :groups => [:development, :test]"
+ expect(gemfile).to include str
+ end
+ end
+
+ context "when frozen" do
+ before do
+ bundle "install"
+ if Bundler.feature_flag.bundler_2_mode?
+ bundle! "config --local deployment true"
+ else
+ bundle! "config --local frozen true"
+ end
+ end
+
+ it "injects anyway" do
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile").read).to match(/rack-obama/)
+ end
+
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+
+ it "restores frozen afterwards" do
+ bundle "inject 'rack-obama' '> 0'"
+ config = YAML.load(bundled_app(".bundle/config").read)
+ expect(config["BUNDLE_DEPLOYMENT"] || config["BUNDLE_FROZEN"]).to eq("true")
+ end
+
+ it "doesn't allow Gemfile changes" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ G
+ bundle "inject 'rack' '> 0'"
+ expect(out).to match(/trying to install in deployment mode after changing/)
+
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
new file mode 100644
index 0000000000..394f672fef
--- /dev/null
+++ b/spec/bundler/commands/install_spec.rb
@@ -0,0 +1,587 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gem sources" do
+ describe "the simple case" do
+ it "prints output and returns if no dependencies are specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle :install
+ expect(out).to match(/no dependencies/)
+ end
+
+ it "does not make a lockfile if the install fails" do
+ install_gemfile <<-G
+ raise StandardError, "FAIL"
+ G
+
+ expect(last_command.bundler_err).to include('StandardError, "FAIL"')
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "creates a Gemfile.lock" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "does not create ./.bundle by default", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install # can't use install_gemfile since it sets retry
+ expect(bundled_app(".bundle")).not_to exist
+ end
+
+ it "does not create ./.bundle by default when installing to system gems" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, :env => { "BUNDLE_PATH__SYSTEM" => true } # can't use install_gemfile since it sets retry
+ expect(bundled_app(".bundle")).not_to exist
+ end
+
+ it "creates lock files based on the Gemfile name" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile"
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+
+ it "doesn't delete the lockfile if one already exists" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+
+ install_gemfile <<-G
+ raise StandardError, "FAIL"
+ G
+
+ expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile)
+ end
+
+ it "does not touch the lockfile if nothing changed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect { run "1" }.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "fetches gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(default_bundle_path("gems/rack-1.0.0")).to exist
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "fetches gems when multiple versions are specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "> 0.9", "< 1.0"
+ G
+
+ expect(default_bundle_path("gems/rack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+
+ it "fetches gems when multiple versions are specified take 2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "< 1.0", "> 0.9"
+ G
+
+ expect(default_bundle_path("gems/rack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+
+ it "raises an appropriate error when gems are specified using symbols" do
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+ gem :rack
+ G
+ expect(exitstatus).to eq(4) if exitstatus
+ end
+
+ it "pulls in dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "actionpack 2.3.2", "rails 2.3.2"
+ end
+
+ it "does the right version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ end
+
+ it "does not install the development dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_development_dependency"
+ G
+
+ expect(the_bundle).to include_gems("with_development_dependency 1.0.0").
+ and not_include_gems("activesupport 2.3.5")
+ end
+
+ it "resolves correctly" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activemerchant"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "activemerchant 1.0", "activesupport 2.3.2", "actionpack 2.3.2"
+ end
+
+ it "activates gem correctly according to the resolved gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", "2.3.5"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activemerchant"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "activemerchant 1.0", "activesupport 2.3.2", "actionpack 2.3.2"
+ end
+
+ it "does not reinstall any gem that is already available locally" do
+ system_gems "activesupport-2.3.2", :path => :bundle_path
+
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.write "lib/activesupport.rb", "ACTIVESUPPORT = 'fail'"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activerecord", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "works when the gemfile specifies gems that only exist in the system" do
+ build_gem "foo", :to_bundle => true
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "foo 1.0.0"
+ end
+
+ it "prioritizes local gems over remote gems" do
+ build_gem "rack", "1.0.0", :to_bundle => true do |s|
+ s.add_dependency "activesupport", "2.3.5"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ describe "with a gem that installs multiple platforms" do
+ it "installs gems for the local platform as first choice" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 #{Bundler.local_platform}")
+ end
+
+ it "falls back on plain ruby" do
+ simulate_platform "foo-bar-baz"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 RUBY")
+ end
+
+ it "installs gems for java" do
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 JAVA")
+ end
+
+ it "installs gems for windows" do
+ simulate_platform mswin
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 MSWIN")
+ end
+ end
+
+ describe "doing bundle install foo" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "works" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "allows running bundle install --system without deleting foo", :bundler => "< 2" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ bundle "install", forgotten_command_line_options(:system => true)
+ FileUtils.rm_rf(bundled_app("vendor"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "allows running bundle install --system after deleting foo", :bundler => "< 2" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install", forgotten_command_line_options(:system => true)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "finds gems in multiple sources", :bundler => "< 2" do
+ build_repo2
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo2}"
+
+ gem "activesupport", "1.2.3"
+ gem "rack", "1.2"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.2", "activesupport 1.2.3"
+ end
+
+ it "gives a useful error if no sources are set" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to include("Your Gemfile has no gem server sources")
+ end
+
+ it "creates a Gemfile.lock on a blank Gemfile" do
+ install_gemfile <<-G
+ G
+
+ expect(File.exist?(bundled_app("Gemfile.lock"))).to eq(true)
+ end
+
+ context "throws a warning if a gem is added twice in Gemfile" do
+ it "without version requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rack"
+ G
+
+ expect(out).to include("Your Gemfile lists the gem rack (>= 0) more than once.")
+ expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).")
+ expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
+ end
+
+ it "with same versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ expect(out).to include("Your Gemfile lists the gem rack (= 1.0) more than once.")
+ expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).")
+ expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
+ end
+ end
+
+ context "throws an error if a gem is added twice in Gemfile" do
+ it "when version of one dependency is not specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rack", "1.0"
+ G
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: rack (>= 0) and rack (= 1.0).")
+ end
+
+ it "when different versions of both dependencies are specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: rack (= 1.0) and rack (= 1.1).")
+ end
+ end
+
+ it "gracefully handles error when rubygems server is unavailable" do
+ install_gemfile <<-G, :artifice => nil
+ source "file://#{gem_repo1}"
+ source "http://localhost:9384" do
+ gem 'foo'
+ end
+ G
+
+ bundle :install, :artifice => nil
+ expect(out).to include("Could not fetch specs from http://localhost:9384/")
+ expect(out).not_to include("file://")
+ end
+
+ it "fails gracefully when downloading an invalid specification from the full index", :rubygems => "2.5" do
+ build_repo2 do
+ build_gem "ajp-rails", "0.0.0", :gemspec => false, :skip_validation => true do |s|
+ bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
+ s.
+ instance_variable_get(:@spec).
+ instance_variable_set(:@dependencies, bad_deps)
+
+ raise "failed to set bad deps" unless s.dependencies == bad_deps
+ end
+ build_gem "ruby-ajp", "1.0.0"
+ end
+
+ install_gemfile <<-G, :full_index => true
+ source "file:\/\/localhost#{gem_repo2}"
+
+ gem "ajp-rails", "0.0.0"
+ G
+
+ expect(last_command.stdboth).not_to match(/Error Report/i)
+ expect(last_command.bundler_err).to include("An error occurred while installing ajp-rails (0.0.0), and Bundler cannot continue.").
+ and include(normalize_uri_file("Make sure that `gem install ajp-rails -v '0.0.0' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling."))
+ end
+
+ it "doesn't blow up when the local .bundle/config is empty" do
+ FileUtils.mkdir_p(bundled_app(".bundle"))
+ FileUtils.touch(bundled_app(".bundle/config"))
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+
+ gem 'foo'
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "doesn't blow up when the global .bundle/config is empty" do
+ FileUtils.mkdir_p("#{Bundler.rubygems.user_home}/.bundle")
+ FileUtils.touch("#{Bundler.rubygems.user_home}/.bundle/config")
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+
+ gem 'foo'
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ describe "Ruby version in Gemfile.lock" do
+ include Bundler::GemHelpers
+
+ context "and using an unsupported Ruby version" do
+ it "prints an error" do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.0.1'
+ ruby '~> 2.2'
+ G
+ expect(out).to include("Your Ruby version is 2.0.1, but your Gemfile specified ~> 2.2")
+ end
+ end
+
+ context "and using a supported Ruby version" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.1.0'
+ G
+ end
+
+ it "writes current Ruby version to Gemfile.lock" do
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.1.3p100
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "updates Gemfile.lock with updated incompatible ruby version" do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.2.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.2.0'
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.2.3p100
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+
+ describe "when Bundler root contains regex chars" do
+ before do
+ root_dir = tmp("foo[]bar")
+
+ FileUtils.mkdir_p(root_dir)
+ in_app_root_custom(root_dir)
+ end
+
+ it "doesn't blow up" do
+ build_lib "foo"
+ gemfile = <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+ File.open("Gemfile", "w") do |file|
+ file.puts gemfile
+ end
+
+ bundle :install
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ describe "when requesting a quiet install via --quiet" do
+ it "should be quiet" do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ bundle :install, :quiet => true
+ expect(out).to include("Could not find gem 'rack'")
+ expect(out).to_not include("Your Gemfile has no gem server sources")
+ end
+ end
+
+ describe "when bundle path does not have write access" do
+ before do
+ FileUtils.mkdir_p(bundled_app("vendor"))
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod(0o500, bundled_app("vendor"))
+
+ bundle :install, forgotten_command_line_options(:path => "vendor")
+ expect(out).to include(bundled_app("vendor").to_s)
+ expect(out).to include("grant write permissions")
+ end
+ end
+
+ context "after installing with --standalone" do
+ before do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ forgotten_command_line_options(:path => "bundle")
+ bundle! "install", :standalone => true
+ end
+
+ it "includes the standalone path" do
+ bundle! "binstubs rack", :standalone => true
+ standalone_line = File.read(bundled_app("bin/rackup")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(standalone_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ end
+ end
+
+ describe "when bundle install is executed with unencoded authentication" do
+ before do
+ gemfile <<-G
+ source 'https://rubygems.org/'
+ gem "."
+ G
+ end
+
+ it "should display a helpful messag explaining how to fix it" do
+ bundle :install, :env => { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }
+ expect(exitstatus).to eq(17) if exitstatus
+ expect(out).to eq("Please CGI escape your usernames and passwords before " \
+ "setting them for authentication.")
+ end
+ end
+end
diff --git a/spec/bundler/commands/issue_spec.rb b/spec/bundler/commands/issue_spec.rb
new file mode 100644
index 0000000000..04c575130e
--- /dev/null
+++ b/spec/bundler/commands/issue_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle issue" do
+ it "exits with a message" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle "issue"
+ expect(out).to include "Did you find an issue with Bundler?"
+ expect(out).to include "## Environment"
+ expect(out).to include "## Gemfile"
+ expect(out).to include "## Bundle Doctor"
+ end
+end
diff --git a/spec/bundler/commands/licenses_spec.rb b/spec/bundler/commands/licenses_spec.rb
new file mode 100644
index 0000000000..d61d3492f3
--- /dev/null
+++ b/spec/bundler/commands/licenses_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle licenses" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ G
+ end
+
+ it "prints license information for all gems in the bundle" do
+ bundle "licenses"
+
+ loaded_bundler_spec = Bundler.load.specs["bundler"]
+ expected = if !loaded_bundler_spec.empty?
+ loaded_bundler_spec[0].license
+ else
+ "Unknown"
+ end
+
+ expect(out).to include("bundler: #{expected}")
+ expect(out).to include("with_license: MIT")
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :licenses
+ expect(out).to include("Installing foo 1.0")
+ end
+end
diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb
new file mode 100644
index 0000000000..5305176c65
--- /dev/null
+++ b/spec/bundler/commands/list_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle list", :bundler => "2" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ gem "rspec", :group => [:test]
+ G
+ end
+
+ context "with name-only and paths option" do
+ it "raises an error" do
+ bundle "list --name-only --paths"
+
+ expect(out).to eq "The `--name-only` and `--paths` options cannot be used together"
+ end
+ end
+
+ context "with without-group and only-group option" do
+ it "raises an error" do
+ bundle "list --without-group dev --only-group test"
+
+ expect(out).to eq "The `--only-group` and `--without-group` options cannot be used together"
+ end
+ end
+
+ describe "with without-group option" do
+ context "when group is present" do
+ it "prints the gems not in the specified group" do
+ bundle! "list --without-group test"
+
+ expect(out).to include(" * rack (1.0.0)")
+ expect(out).not_to include(" * rspec (1.2.7)")
+ end
+ end
+
+ context "when group is not found" do
+ it "raises an error" do
+ bundle "list --without-group random"
+
+ expect(out).to eq "`random` group could not be found."
+ end
+ end
+ end
+
+ describe "with only-group option" do
+ context "when group is present" do
+ it "prints the gems in the specified group" do
+ bundle! "list --only-group default"
+
+ expect(out).to include(" * rack (1.0.0)")
+ expect(out).not_to include(" * rspec (1.2.7)")
+ end
+ end
+
+ context "when group is not found" do
+ it "raises an error" do
+ bundle "list --only-group random"
+
+ expect(out).to eq "`random` group could not be found."
+ end
+ end
+ end
+
+ context "with name-only option" do
+ it "prints only the name of the gems in the bundle" do
+ bundle "list --name-only"
+
+ expect(out).to include("rack")
+ expect(out).to include("rspec")
+ end
+ end
+
+ context "with paths option" do
+ before do
+ build_repo2 do
+ build_gem "bar"
+ end
+
+ build_git "git_test", "1.0.0", :path => lib_path("git_test")
+
+ build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rails"
+ gem "git_test", :git => "#{lib_path("git_test")}"
+ gemspec :path => "#{tmp.join("gemspec_test")}"
+ G
+
+ bundle! "install"
+ end
+
+ it "prints the path of each gem in the bundle" do
+ bundle "list --paths"
+ expect(out).to match(%r{.*\/rails\-2\.3\.2})
+ expect(out).to match(%r{.*\/rack\-1\.2})
+ expect(out).to match(%r{.*\/git_test\-\w})
+ expect(out).to match(%r{.*\/gemspec_test})
+ end
+ end
+
+ context "when no gems are in the gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+
+ it "prints message saying no gems are in the bundle" do
+ bundle "list"
+ expect(out).to include("No gems in the Gemfile")
+ end
+ end
+
+ it "lists gems installed in the bundle" do
+ bundle "list"
+ expect(out).to include(" * rack (1.0.0)")
+ end
+
+ it "aliases the ls command to list" do
+ bundle "ls"
+ expect(out).to include("Gems included by the bundle")
+ end
+end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
new file mode 100644
index 0000000000..0b77605f01
--- /dev/null
+++ b/spec/bundler/commands/lock_spec.rb
@@ -0,0 +1,322 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle lock" do
+ def strip_lockfile(lockfile)
+ strip_whitespace(lockfile).sub(/\n\Z/, "")
+ end
+
+ def read_lockfile(file = "Gemfile.lock")
+ strip_lockfile bundled_app(file).read
+ end
+
+ let(:repo) { gem_repo1 }
+
+ before :each do
+ gemfile <<-G
+ source "file://localhost#{repo}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ @lockfile = strip_lockfile(normalize_uri_file(<<-L))
+ GEM
+ remote: file://localhost#{repo}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+ with_license (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ with_license
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "prints a lockfile when there is no existing lockfile with --print" do
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "prints a lockfile when there is an existing lockfile with --print" do
+ lockfile @lockfile
+
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is no existing lockfile" do
+ bundle "lock"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is an outdated lockfile using --update" do
+ lockfile @lockfile.gsub("2.3.2", "2.3.1")
+
+ bundle! "lock --update"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "does not fetch remote specs when using the --local option" do
+ bundle "lock --update --local"
+
+ expect(out).to match(/sources listed in your Gemfile|installed locally/)
+ end
+
+ it "writes to a custom location using --lockfile" do
+ bundle "lock --lockfile=lock"
+
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(read_lockfile "lock").to eq(@lockfile)
+ expect { read_lockfile }.to raise_error(Errno::ENOENT)
+ end
+
+ it "writes to custom location using --lockfile when a default lockfile is present" do
+ bundle "install"
+ bundle "lock --lockfile=lock"
+
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(read_lockfile("lock")).to eq(@lockfile)
+ end
+
+ it "update specific gems using --update" do
+ lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("10.0.2", "10.0.1")
+
+ bundle "lock --update rails rake"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "errors when updating a missing specific gems using --update" do
+ lockfile @lockfile
+
+ bundle "lock --update blahblah"
+ expect(out).to eq("Could not find gem 'blahblah'.")
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ # see update_spec for more coverage on same options. logic is shared so it's not necessary
+ # to repeat coverage here.
+ context "conservative updates" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w[1.4.3 1.4.4] do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w[1.4.5 1.5.0] do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "foo", %w[1.5.1] do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
+
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ it "single gem updates dependent gem to minor" do
+ bundle "lock --update foo --patch"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
+ end
+
+ it "minor preferred with strict" do
+ bundle "lock --update --minor --strict"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
+ end
+ end
+
+ it "supports adding new platforms" do
+ bundle! "lock --add-platform java x86-mingw32"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+ end
+
+ it "supports adding the `ruby` platform" do
+ bundle! "lock --add-platform ruby"
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift("ruby").uniq)
+ end
+
+ it "warns when adding an unknown platform" do
+ bundle "lock --add-platform foobarbaz"
+ expect(out).to include("The platform `foobarbaz` is unknown to RubyGems and adding it will likely lead to resolution errors")
+ end
+
+ it "allows removing platforms" do
+ bundle! "lock --add-platform java x86-mingw32"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+
+ bundle! "lock --remove-platform java"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(mingw).uniq)
+ end
+
+ it "errors when removing all platforms" do
+ bundle "lock --remove-platform #{local_platforms.join(" ")}"
+ expect(last_command.bundler_err).to include("Removing all platforms from the bundle is not allowed")
+ end
+
+ # from https://github.com/bundler/bundler/issues/4896
+ it "properly adds platforms when platform requirements come from different dependencies" do
+ build_repo4 do
+ build_gem "ffi", "1.9.14"
+ build_gem "ffi", "1.9.14" do |s|
+ s.platform = mingw
+ end
+
+ build_gem "gssapi", "0.1"
+ build_gem "gssapi", "0.2"
+ build_gem "gssapi", "0.3"
+ build_gem "gssapi", "1.2.0" do |s|
+ s.add_dependency "ffi", ">= 1.0.1"
+ end
+
+ build_gem "mixlib-shellout", "2.2.6"
+ build_gem "mixlib-shellout", "2.2.6" do |s|
+ s.platform = "universal-mingw32"
+ s.add_dependency "win32-process", "~> 0.8.2"
+ end
+
+ # we need all these versions to get the sorting the same as it would be
+ # pulling from rubygems.org
+ %w[0.8.3 0.8.2 0.8.1 0.8.0].each do |v|
+ build_gem "win32-process", v do |s|
+ s.add_dependency "ffi", ">= 1.0.0"
+ end
+ end
+ end
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo4}"
+
+ gem "mixlib-shellout"
+ gem "gssapi"
+ G
+
+ simulate_platform(mingw) { bundle! :lock }
+
+ expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G)))
+ GEM
+ remote: file://localhost#{gem_repo4}/
+ specs:
+ ffi (1.9.14-x86-mingw32)
+ gssapi (1.2.0)
+ ffi (>= 1.0.1)
+ mixlib-shellout (2.2.6-universal-mingw32)
+ win32-process (~> 0.8.2)
+ win32-process (0.8.3)
+ ffi (>= 1.0.0)
+
+ PLATFORMS
+ x86-mingw32
+
+ DEPENDENCIES
+ gssapi
+ mixlib-shellout
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ simulate_platform(rb) { bundle! :lock }
+
+ expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G)))
+ GEM
+ remote: file://localhost#{gem_repo4}/
+ specs:
+ ffi (1.9.14)
+ ffi (1.9.14-x86-mingw32)
+ gssapi (1.2.0)
+ ffi (>= 1.0.1)
+ mixlib-shellout (2.2.6)
+ mixlib-shellout (2.2.6-universal-mingw32)
+ win32-process (~> 0.8.2)
+ win32-process (0.8.3)
+ ffi (>= 1.0.0)
+
+ PLATFORMS
+ ruby
+ x86-mingw32
+
+ DEPENDENCIES
+ gssapi
+ mixlib-shellout
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ context "when an update is available" do
+ let(:repo) { gem_repo2 }
+
+ before do
+ lockfile(@lockfile)
+ build_repo2 do
+ build_gem "foo", "2.0"
+ end
+ end
+
+ it "does not implicitly update" do
+ bundle! "lock"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "accounts for changes in the gemfile" do
+ gemfile gemfile.gsub('"foo"', '"foo", "2.0"')
+ bundle! "lock"
+
+ expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
+ end
+ end
+end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
new file mode 100644
index 0000000000..e6d6e19122
--- /dev/null
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -0,0 +1,912 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle gem" do
+ def reset!
+ super
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ end
+
+ def remove_push_guard(gem_name)
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ path = "#{gem_name}/#{gem_name}.gemspec"
+ content = File.read(path).sub(/raise "RubyGems 2\.0 or newer.*/, "")
+ File.open(path, "w") {|f| f.write(content) }
+ end
+ end
+
+ def execute_bundle_gem(gem_name, flag = "", to_remove_push_guard = true)
+ bundle! "gem #{gem_name} #{flag}"
+ remove_push_guard(gem_name) if to_remove_push_guard
+ # reset gemspec cache for each test because of commit 3d4163a
+ Bundler.clear_gemspec_cache
+ end
+
+ def gem_skeleton_assertions(gem_name)
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
+ expect(bundled_app("#{gem_name}/README.md")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
+ expect(bundled_app("#{gem_name}/Rakefile")).to exist
+ expect(bundled_app("#{gem_name}/lib/test/gem.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/test/gem/version.rb")).to exist
+ end
+
+ before do
+ git_config_content = <<-EOF
+ [user]
+ name = "Bundler User"
+ email = user@example.com
+ [github]
+ user = bundleuser
+ EOF
+ @git_config_location = ENV["GIT_CONFIG"]
+ path = "#{File.expand_path(tmp, File.dirname(__FILE__))}/test_git_config.txt"
+ File.open(path, "w") {|f| f.write(git_config_content) }
+ ENV["GIT_CONFIG"] = path
+ end
+
+ after do
+ FileUtils.rm(ENV["GIT_CONFIG"]) if File.exist?(ENV["GIT_CONFIG"])
+ ENV["GIT_CONFIG"] = @git_config_location
+ end
+
+ shared_examples_for "git config is present" do
+ context "git config user.{name,email} present" do
+ it "sets gemspec author to git user.name if available" do
+ expect(generated_gem.gemspec.authors.first).to eq("Bundler User")
+ end
+
+ it "sets gemspec email to git user.email if available" do
+ expect(generated_gem.gemspec.email.first).to eq("user@example.com")
+ end
+ end
+ end
+
+ shared_examples_for "git config is absent" do
+ it "sets gemspec author to default message if git user.name is not set or empty" do
+ expect(generated_gem.gemspec.authors.first).to eq("TODO: Write your name")
+ end
+
+ it "sets gemspec email to default message if git user.email is not set or empty" do
+ expect(generated_gem.gemspec.email.first).to eq("TODO: Write your email address")
+ end
+ end
+
+ shared_examples_for "--mit flag" do
+ before do
+ execute_bundle_gem(gem_name, "--mit")
+ end
+ it "generates a gem skeleton with MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/LICENSE.txt")).to exist
+ skel = Bundler::GemHelper.new(bundled_app(gem_name).to_s)
+ expect(skel.gemspec.license).to eq("MIT")
+ end
+ end
+
+ shared_examples_for "--no-mit flag" do
+ before do
+ execute_bundle_gem(gem_name, "--no-mit")
+ end
+ it "generates a gem skeleton without MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/LICENSE.txt")).to_not exist
+ end
+ end
+
+ shared_examples_for "--coc flag" do
+ before do
+ execute_bundle_gem(gem_name, "--coc", false)
+ end
+ it "generates a gem skeleton with MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/CODE_OF_CONDUCT.md")).to exist
+ end
+
+ describe "README additions" do
+ it "generates the README with a section for the Code of Conduct" do
+ expect(bundled_app("test-gem/README.md").read).to include("## Code of Conduct")
+ expect(bundled_app("test-gem/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
+ end
+ end
+ end
+
+ shared_examples_for "--no-coc flag" do
+ before do
+ execute_bundle_gem(gem_name, "--no-coc", false)
+ end
+ it "generates a gem skeleton without Code of Conduct" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/CODE_OF_CONDUCT.md")).to_not exist
+ end
+
+ describe "README additions" do
+ it "generates the README without a section for the Code of Conduct" do
+ expect(bundled_app("test-gem/README.md").read).not_to include("## Code of Conduct")
+ expect(bundled_app("test-gem/README.md").read).not_to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
+ end
+ end
+ end
+
+ context "README.md" do
+ let(:gem_name) { "test_gem" }
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ context "git config github.user present" do
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ it "contribute URL set to git username" do
+ expect(bundled_app("test_gem/README.md").read).not_to include("[USERNAME]")
+ expect(bundled_app("test_gem/README.md").read).to include("github.com/bundleuser")
+ end
+ end
+
+ context "git config github.user is absent" do
+ before do
+ sys_exec("git config --unset github.user")
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it "contribute URL set to [USERNAME]" do
+ expect(bundled_app("test_gem/README.md").read).to include("[USERNAME]")
+ expect(bundled_app("test_gem/README.md").read).not_to include("github.com/bundleuser")
+ end
+ end
+ end
+
+ it "creates a new git repository" do
+ in_app_root
+ bundle "gem test_gem"
+ expect(bundled_app("test_gem/.git")).to exist
+ end
+
+ context "when git is not available" do
+ let(:gem_name) { "test_gem" }
+
+ # This spec cannot have `git` available in the test env
+ before do
+ load_paths = [lib, spec]
+ load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
+
+ sys_exec "PATH=\"\" #{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}"
+ end
+
+ it "creates the gem without the need for git" do
+ expect(bundled_app("#{gem_name}/README.md")).to exist
+ end
+
+ it "doesn't create a git repo" do
+ expect(bundled_app("#{gem_name}/.git")).to_not exist
+ end
+
+ it "doesn't create a .gitignore file" do
+ expect(bundled_app("#{gem_name}/.gitignore")).to_not exist
+ end
+ end
+
+ it "generates a valid gemspec" do
+ in_app_root
+ bundle! "gem newgem --bin"
+
+ process_file(bundled_app("newgem", "newgem.gemspec")) do |line|
+ # Simulate replacing TODOs with real values
+ case line
+ when /spec\.metadata\["(?:allowed_push_host|homepage_uri|source_code_uri|changelog_uri)"\]/, /spec\.homepage/
+ line.gsub(/\=.*$/, "= 'http://example.org'")
+ when /spec\.summary/
+ line.gsub(/\=.*$/, "= %q{A short summary of my new gem.}")
+ when /spec\.description/
+ line.gsub(/\=.*$/, "= %q{A longer description of my new gem.}")
+ # Remove exception that prevents public pushes on older RubyGems versions
+ when /raise "RubyGems 2.0 or newer/
+ line.gsub(/.*/, "") if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ else
+ line
+ end
+ end
+
+ Dir.chdir(bundled_app("newgem")) do
+ gems = ["rake-10.0.2", :bundler]
+ # for Ruby core repository, Ruby 2.6+ has bundler as standard library.
+ gems.delete(:bundler) if ruby_core?
+ system_gems gems, :path => :bundle_path
+ bundle! "exec rake build"
+ end
+
+ expect(last_command.stdboth).not_to include("ERROR")
+ end
+
+ context "gem naming with relative paths" do
+ before do
+ reset!
+ in_app_root
+ end
+
+ it "resolves ." do
+ create_temporary_dir("tmp")
+
+ bundle "gem ."
+
+ expect(bundled_app("tmp/lib/tmp.rb")).to exist
+ end
+
+ it "resolves .." do
+ create_temporary_dir("temp/empty_dir")
+
+ bundle "gem .."
+
+ expect(bundled_app("temp/lib/temp.rb")).to exist
+ end
+
+ it "resolves relative directory" do
+ create_temporary_dir("tmp/empty/tmp")
+
+ bundle "gem ../../empty"
+
+ expect(bundled_app("tmp/empty/lib/empty.rb")).to exist
+ end
+
+ def create_temporary_dir(dir)
+ FileUtils.mkdir_p(dir)
+ Dir.chdir(dir)
+ end
+ end
+
+ context "gem naming with underscore" do
+ let(:gem_name) { "test_gem" }
+
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ it "generates a gem skeleton" do
+ expect(bundled_app("test_gem/test_gem.gemspec")).to exist
+ expect(bundled_app("test_gem/Gemfile")).to exist
+ expect(bundled_app("test_gem/Rakefile")).to exist
+ expect(bundled_app("test_gem/lib/test_gem.rb")).to exist
+ expect(bundled_app("test_gem/lib/test_gem/version.rb")).to exist
+ expect(bundled_app("test_gem/.gitignore")).to exist
+
+ expect(bundled_app("test_gem/bin/setup")).to exist
+ expect(bundled_app("test_gem/bin/console")).to exist
+ expect(bundled_app("test_gem/bin/setup")).to be_executable
+ expect(bundled_app("test_gem/bin/console")).to be_executable
+ end
+
+ it "starts with version 0.1.0" do
+ expect(bundled_app("test_gem/lib/test_gem/version.rb").read).to match(/VERSION = "0.1.0"/)
+ end
+
+ it "does not nest constants" do
+ expect(bundled_app("test_gem/lib/test_gem/version.rb").read).to match(/module TestGem/)
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(/module TestGem/)
+ end
+
+ it_should_behave_like "git config is present"
+
+ context "git config user.{name,email} is not set" do
+ before do
+ `git config --unset user.name`
+ `git config --unset user.email`
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it_should_behave_like "git config is absent"
+ end
+
+ it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do
+ expect(generated_gem.gemspec.metadata["allowed_push_host"]).
+ to match(/mygemserver\.com/)
+ end
+
+ it "requires the version file" do
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(%r{require "test_gem/version"})
+ end
+
+ it "creates a base error class" do
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(/class Error < StandardError; end$/)
+ end
+
+ it "runs rake without problems" do
+ system_gems ["rake-10.0.2"]
+
+ rakefile = strip_whitespace <<-RAKEFILE
+ task :default do
+ puts 'SUCCESS'
+ end
+ RAKEFILE
+ File.open(bundled_app("test_gem/Rakefile"), "w") do |file|
+ file.puts rakefile
+ end
+
+ Dir.chdir(bundled_app(gem_name)) do
+ sys_exec(rake)
+ expect(out).to include("SUCCESS")
+ end
+ end
+
+ context "--exe parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --exe"
+ end
+
+ it "builds exe skeleton" do
+ expect(bundled_app("test_gem/exe/test_gem")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/exe/test_gem").read).to match(/require "test_gem"/)
+ end
+ end
+
+ context "--bin parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --bin"
+ end
+
+ it "builds exe skeleton" do
+ expect(bundled_app("test_gem/exe/test_gem")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/exe/test_gem").read).to match(/require "test_gem"/)
+ end
+ end
+
+ context "no --test parameter" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ end
+
+ it "doesn't create any spec/test file" do
+ expect(bundled_app("test_gem/.rspec")).to_not exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to_not exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to_not exist
+ expect(bundled_app("test_gem/test/test_test_gem.rb")).to_not exist
+ expect(bundled_app("test_gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--test parameter set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=rspec"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/.rspec")).to exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ end
+
+ it "depends on a specific version of rspec", :rubygems => ">= 1.8.1" do
+ remove_push_guard(gem_name)
+ rspec_dep = generated_gem.gemspec.development_dependencies.find {|d| d.name == "rspec" }
+ expect(rspec_dep).to be_specific
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/spec/spec_helper.rb").read).to include(%(require "test_gem"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb").read).to include("expect(false).to eq(true)")
+ end
+ end
+
+ context "gem.test setting set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test rspec"
+ bundle "gem #{gem_name}"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/.rspec")).to exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ end
+ end
+
+ context "gem.test setting set to rspec and --test is set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test rspec"
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "depends on a specific version of minitest", :rubygems => ">= 1.8.1" do
+ remove_push_guard(gem_name)
+ rspec_dep = generated_gem.gemspec.development_dependencies.find {|d| d.name == "minitest" }
+ expect(rspec_dep).to be_specific
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/test/test_helper.rb").read).to include(%(require "test_gem"))
+ end
+
+ it "requires 'minitest_helper'" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include(%(require "test_helper"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("assert false")
+ end
+ end
+
+ context "gem.test setting set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test minitest"
+ bundle "gem #{gem_name}"
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/testtask"
+
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+ end
+
+ task :default => :test
+ RAKEFILE
+
+ expect(bundled_app("test_gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test with no arguments" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test"
+ end
+
+ it "defaults to rspec" do
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ expect(bundled_app("test_gem/test/minitest_helper.rb")).to_not exist
+ end
+
+ it "creates a .travis.yml file to test the library against the current Ruby version on Travis CI" do
+ expect(bundled_app("test_gem/.travis.yml").read).to match(/- #{RUBY_VERSION}/)
+ end
+ end
+
+ context "--edit option" do
+ it "opens the generated gemspec in the user's text editor" do
+ reset!
+ in_app_root
+ output = bundle "gem #{gem_name} --edit=echo"
+ gemspec_path = File.join(Dir.pwd, gem_name, "#{gem_name}.gemspec")
+ expect(output).to include("echo \"#{gemspec_path}\"")
+ end
+ end
+ end
+
+ context "testing --mit and --coc options against bundle config settings" do
+ let(:gem_name) { "test-gem" }
+
+ context "with mit option in bundle config settings set to true" do
+ before do
+ global_config "BUNDLE_GEM__MIT" => "true", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ end
+ after { reset! }
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
+
+ context "with mit option in bundle config settings set to false" do
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
+
+ context "with coc option in bundle config settings set to true" do
+ before do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "true"
+ end
+ after { reset! }
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
+
+ context "with coc option in bundle config settings set to false" do
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
+ end
+
+ context "gem naming with dashed" do
+ let(:gem_name) { "test-gem" }
+
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ it "generates a gem skeleton" do
+ expect(bundled_app("test-gem/test-gem.gemspec")).to exist
+ expect(bundled_app("test-gem/Gemfile")).to exist
+ expect(bundled_app("test-gem/Rakefile")).to exist
+ expect(bundled_app("test-gem/lib/test/gem.rb")).to exist
+ expect(bundled_app("test-gem/lib/test/gem/version.rb")).to exist
+ end
+
+ it "starts with version 0.1.0" do
+ expect(bundled_app("test-gem/lib/test/gem/version.rb").read).to match(/VERSION = "0.1.0"/)
+ end
+
+ it "nests constants so they work" do
+ expect(bundled_app("test-gem/lib/test/gem/version.rb").read).to match(/module Test\n module Gem/)
+ expect(bundled_app("test-gem/lib/test/gem.rb").read).to match(/module Test\n module Gem/)
+ end
+
+ it_should_behave_like "git config is present"
+
+ context "git config user.{name,email} is not set" do
+ before do
+ `git config --unset user.name`
+ `git config --unset user.email`
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it_should_behave_like "git config is absent"
+ end
+
+ it "requires the version file" do
+ expect(bundled_app("test-gem/lib/test/gem.rb").read).to match(%r{require "test/gem/version"})
+ end
+
+ it "runs rake without problems" do
+ system_gems ["rake-10.0.2"]
+
+ rakefile = strip_whitespace <<-RAKEFILE
+ task :default do
+ puts 'SUCCESS'
+ end
+ RAKEFILE
+ File.open(bundled_app("test-gem/Rakefile"), "w") do |file|
+ file.puts rakefile
+ end
+
+ Dir.chdir(bundled_app(gem_name)) do
+ sys_exec(rake)
+ expect(out).to include("SUCCESS")
+ end
+ end
+
+ context "--bin parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --bin"
+ end
+
+ it "builds bin skeleton" do
+ expect(bundled_app("test-gem/exe/test-gem")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/exe/test-gem").read).to match(%r{require "test/gem"})
+ end
+ end
+
+ context "no --test parameter" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ end
+
+ it "doesn't create any spec/test file" do
+ expect(bundled_app("test-gem/.rspec")).to_not exist
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb")).to_not exist
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to_not exist
+ expect(bundled_app("test-gem/test/test_test/gem.rb")).to_not exist
+ expect(bundled_app("test-gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--test parameter set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=rspec"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test-gem/.rspec")).to exist
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb")).to exist
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/spec/spec_helper.rb").read).to include(%(require "test/gem"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb").read).to include("expect(false).to eq(true)")
+ end
+
+ it "creates a default rake task to run the specs" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rspec/core/rake_task"
+
+ RSpec::Core::RakeTask.new(:spec)
+
+ task :default => :spec
+ RAKEFILE
+
+ expect(bundled_app("test-gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb")).to exist
+ expect(bundled_app("test-gem/test/test_helper.rb")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/test/test_helper.rb").read).to match(%r{require "test/gem"})
+ end
+
+ it "requires 'test_helper'" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/require "test_helper"/)
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/assert false/)
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/testtask"
+
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+ end
+
+ task :default => :test
+ RAKEFILE
+
+ expect(bundled_app("test-gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test with no arguments" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test"
+ end
+
+ it "defaults to rspec" do
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to exist
+ expect(bundled_app("test-gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--ext parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem test_gem --ext"
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("test_gem/ext/test_gem/extconf.rb")).to exist
+ expect(bundled_app("test_gem/ext/test_gem/test_gem.h")).to exist
+ expect(bundled_app("test_gem/ext/test_gem/test_gem.c")).to exist
+ end
+
+ it "includes rake-compiler" do
+ expect(bundled_app("test_gem/test_gem.gemspec").read).to include('spec.add_development_dependency "rake-compiler"')
+ end
+
+ it "depends on compile task for build" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/extensiontask"
+
+ task :build => :compile
+
+ Rake::ExtensionTask.new("test_gem") do |ext|
+ ext.lib_dir = "lib/test_gem"
+ end
+
+ task :default => [:clobber, :compile, :spec]
+ RAKEFILE
+
+ expect(bundled_app("test_gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+ end
+
+ describe "uncommon gem names" do
+ it "can deal with two dashes" do
+ bundle "gem a--a"
+ Bundler.clear_gemspec_cache
+
+ expect(bundled_app("a--a/a--a.gemspec")).to exist
+ end
+
+ it "fails gracefully with a ." do
+ bundle "gem foo.gemspec"
+ expect(last_command.bundler_err).to end_with("Invalid gem name foo.gemspec -- `Foo.gemspec` is an invalid constant name")
+ end
+
+ it "fails gracefully with a ^" do
+ bundle "gem ^"
+ expect(last_command.bundler_err).to end_with("Invalid gem name ^ -- `^` is an invalid constant name")
+ end
+
+ it "fails gracefully with a space" do
+ bundle "gem 'foo bar'"
+ expect(last_command.bundler_err).to end_with("Invalid gem name foo bar -- `Foo bar` is an invalid constant name")
+ end
+
+ it "fails gracefully when multiple names are passed" do
+ bundle "gem foo bar baz"
+ expect(last_command.bundler_err).to eq(<<-E.strip)
+ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"]
+Usage: "bundle gem NAME [OPTIONS]"
+ E
+ end
+ end
+
+ describe "#ensure_safe_gem_name" do
+ before do
+ bundle "gem #{subject}"
+ end
+ after do
+ Bundler.clear_gemspec_cache
+ end
+
+ context "with an existing const name" do
+ subject { "gem" }
+ it { expect(out).to include("Invalid gem name #{subject}") }
+ end
+
+ context "with an existing hyphenated const name" do
+ subject { "gem-specification" }
+ it { expect(out).to include("Invalid gem name #{subject}") }
+ end
+
+ context "starting with an existing const name" do
+ subject { "gem-somenewconstantname" }
+ it { expect(out).not_to include("Invalid gem name #{subject}") }
+ end
+
+ context "ending with an existing const name" do
+ subject { "somenewconstantname-gem" }
+ it { expect(out).not_to include("Invalid gem name #{subject}") }
+ end
+ end
+
+ context "on first run" do
+ before do
+ in_app_root
+ end
+
+ it "asks about test framework" do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false"
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "rspec"
+ end
+
+ expect(bundled_app("foobar/spec/spec_helper.rb")).to exist
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rspec/core/rake_task"
+
+ RSpec::Core::RakeTask.new(:spec)
+
+ task :default => :spec
+ RAKEFILE
+
+ expect(bundled_app("foobar/Rakefile").read).to eq(rakefile)
+ expect(bundled_app("foobar/foobar.gemspec").read).to include('spec.add_development_dependency "rspec"')
+ end
+
+ it "asks about MIT license" do
+ global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+
+ bundle :config
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "yes"
+ end
+
+ expect(bundled_app("foobar/LICENSE.txt")).to exist
+ end
+
+ it "asks about CoC" do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false"
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "yes"
+ end
+
+ expect(bundled_app("foobar/CODE_OF_CONDUCT.md")).to exist
+ end
+ end
+
+ context "on conflicts with a previously created file" do
+ it "should fail gracefully" do
+ in_app_root do
+ FileUtils.touch("conflict-foobar")
+ end
+ bundle "gem conflict-foobar"
+ expect(last_command.bundler_err).to include("Errno::ENOTDIR")
+ expect(exitstatus).to eql(32) if exitstatus
+ end
+ end
+
+ context "on conflicts with a previously created directory" do
+ it "should succeed" do
+ in_app_root do
+ FileUtils.mkdir_p("conflict-foobar/Gemfile")
+ end
+ bundle! "gem conflict-foobar"
+ expect(last_command.stdout).to include("file_clash conflict-foobar/Gemfile").
+ and include "Initializing git repo in #{bundled_app("conflict-foobar")}"
+ end
+ end
+end
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
new file mode 100644
index 0000000000..5cab846fb5
--- /dev/null
+++ b/spec/bundler/commands/open_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle open" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "opens the gem with BUNDLER_EDITOR as highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "opens the gem with VISUAL as 2nd highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("visual #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "opens the gem with EDITOR as 3rd highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "complains if no EDITOR is set" do
+ bundle "open rails", :env => { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to eq("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR")
+ end
+
+ it "complains if gem not in bundle" do
+ bundle "open missing", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match(/could not find gem 'missing'/i)
+ end
+
+ it "does not blow up if the gem to open does not have a Gemfile" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "open foo", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match("editor #{default_bundle_path.join("bundler/gems/foo-1.0-#{ref}")}")
+ end
+
+ it "suggests alternatives for similar-sounding gems" do
+ bundle "open Rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match(/did you mean rails\?/i)
+ end
+
+ it "opens the gem with short words" do
+ bundle "open rec", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
+ end
+
+ it "select the gem from many match gems" do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
+ end
+
+ it "allows selecting exit from many match gems" do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle! "open active", :env => env do |input, _, _|
+ input.puts "0"
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ it "opens the editor with a clean env" do
+ bundle "open", :env => { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).not_to include("BUNDLE_GEMFILE=")
+ end
+end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
new file mode 100644
index 0000000000..fc1f1772e7
--- /dev/null
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -0,0 +1,782 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle outdated" do
+ before :each do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
+ describe "with no arguments" do
+ it "returns a sorted list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.2"
+ update_git "foo", :path => lib_path("foo")
+ update_git "zebra", :path => lib_path("zebra")
+ end
+
+ bundle "outdated"
+
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)")
+ expect(out).to include("foo (newest 1.0")
+
+ # Gem names are one per-line, between "*" and their parenthesized version.
+ gem_list = out.split("\n").map {|g| g[/\* (.*) \(/, 1] }.compact
+ expect(gem_list).to eq(gem_list.sort)
+ end
+
+ it "returns non zero exit status if outdated gems present" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ bundle "outdated"
+
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "returns success exit status if no outdated gems present" do
+ bundle "outdated"
+
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "adds gem group to dependency output when repo is updated" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ end
+ G
+
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated --verbose"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5) in groups \"development, test\"")
+ end
+ end
+
+ describe "with --group option" do
+ def test_group_option(group = nil, gems_list_size = 1)
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem "duradura", '7.0'
+ gem 'activesupport', '2.3.5'
+ end
+ G
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "terranova", "9"
+ build_gem "duradura", "8.0"
+ end
+
+ bundle "outdated --group #{group}"
+
+ # Gem names are one per-line, between "*" and their parenthesized version.
+ gem_list = out.split("\n").map {|g| g[/\* (.*) \(/, 1] }.compact
+ expect(gem_list).to eq(gem_list.sort)
+ expect(gem_list.size).to eq gems_list_size
+ end
+
+ it "not outdated gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ bundle "outdated --group"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "returns a sorted list of outdated gems from one group => 'default'" do
+ test_group_option("default")
+
+ expect(out).to include("===== Group default =====")
+ expect(out).to include("terranova (")
+
+ expect(out).not_to include("===== Group development, test =====")
+ expect(out).not_to include("activesupport")
+ expect(out).not_to include("duradura")
+ end
+
+ it "returns a sorted list of outdated gems from one group => 'development'" do
+ test_group_option("development", 2)
+
+ expect(out).not_to include("===== Group default =====")
+ expect(out).not_to include("terranova (")
+
+ expect(out).to include("===== Group development, test =====")
+ expect(out).to include("activesupport")
+ expect(out).to include("duradura")
+ end
+ end
+
+ describe "with --groups option" do
+ it "not outdated gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ bundle "outdated --groups"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "returns a sorted list of outdated gems by groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "terranova", "9"
+ build_gem "duradura", "8.0"
+ end
+
+ bundle "outdated --groups"
+ expect(out).to include("===== Group default =====")
+ expect(out).to include("terranova (newest 9, installed 8, requested = 8)")
+ expect(out).to include("===== Group development, test =====")
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("duradura (newest 8.0, installed 7.0, requested = 7.0)")
+
+ expect(out).not_to include("weakling (")
+
+ # TODO: check gems order inside the group
+ end
+ end
+
+ describe "with --local option" do
+ it "uses local cache to return a list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "2.3.4"
+ end
+
+ bundle! "config clean false"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.4"
+ G
+
+ bundle "outdated --local"
+
+ expect(out).to include("activesupport (newest 2.3.5, installed 2.3.4, requested = 2.3.4)")
+ end
+
+ it "doesn't hit repo2" do
+ FileUtils.rm_rf(gem_repo2)
+
+ bundle "outdated --local"
+ expect(out).not_to match(/Fetching (gem|version|dependency) metadata from/)
+ end
+ end
+
+ shared_examples_for "a minimal output is desired" do
+ context "and gems are outdated" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.2"
+ end
+ end
+
+ it "outputs a sorted list of outdated gems with a more minimal format" do
+ minimal_output = "activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)\n" \
+ "weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)"
+ subject
+ expect(out).to eq(minimal_output)
+ end
+ end
+
+ context "and no gems are outdated" do
+ it "has empty output" do
+ subject
+ expect(out).to eq("")
+ end
+ end
+ end
+
+ describe "with --parseable option" do
+ subject { bundle "outdated --parseable" }
+
+ it_behaves_like "a minimal output is desired"
+ end
+
+ describe "with aliased --porcelain option" do
+ subject { bundle "outdated --porcelain" }
+
+ it_behaves_like "a minimal output is desired"
+ end
+
+ describe "with specified gems" do
+ it "returns list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ bundle "outdated foo"
+ expect(out).not_to include("activesupport (newest")
+ expect(out).to include("foo (newest 1.0")
+ end
+ end
+
+ describe "pre-release gems" do
+ context "without the --pre option" do
+ it "ignores pre-release versions" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta"
+ end
+
+ bundle "outdated"
+ expect(out).not_to include("activesupport (3.0.0.beta > 2.3.5)")
+ end
+ end
+
+ context "with the --pre option" do
+ it "includes pre-release versions" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta"
+ end
+
+ bundle "outdated --pre"
+ expect(out).to include("activesupport (newest 3.0.0.beta, installed 2.3.5, requested = 2.3.5)")
+ end
+ end
+
+ context "when current gem is a pre-release" do
+ it "includes the gem" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta.1"
+ build_gem "activesupport", "3.0.0.beta.2"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "3.0.0.beta.1"
+ G
+
+ bundle "outdated"
+ expect(out).to include("(newest 3.0.0.beta.2, installed 3.0.0.beta.1, requested = 3.0.0.beta.1)")
+ end
+ end
+ end
+
+ describe "with --strict option" do
+ it "only reports gems that have a newer version that matches the specified dependency version requirements" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle "outdated --strict"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.0.5, installed 0.0.3, requested ~> 0.0.1)")
+ end
+
+ it "only reports gem dependencies when they can actually be updated" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_middleware", "1.0"
+ G
+
+ bundle "outdated --strict"
+
+ expect(out).to_not include("rack (1.2")
+ end
+
+ describe "and filter options" do
+ it "only reports gems that match requirement and patch filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.4.0 3.0.0]
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle "outdated --strict --filter-patch"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.0.5, installed 0.0.3")
+ end
+
+ it "only reports gems that match requirement and minor filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.3.9]
+ build_gem "weakling", "0.1.5"
+ end
+
+ bundle "outdated --strict --filter-minor"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.1.5, installed 0.0.3")
+ end
+
+ it "only reports gems that match requirement and major filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.4.0 2.5.0]
+ build_gem "weakling", "1.1.5"
+ end
+
+ bundle "outdated --strict --filter-major"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 1.1.5, installed 0.0.3")
+ end
+ end
+ end
+
+ describe "with invalid gem name" do
+ it "returns could not find gem name" do
+ bundle "outdated invalid_gem_name"
+ expect(out).to include("Could not find gem 'invalid_gem_name'.")
+ end
+
+ it "returns non-zero exit code" do
+ bundle "outdated invalid_gem_name"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :outdated
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ context "after bundle install --deployment", :bundler => "< 2" do
+ before do
+ install_gemfile <<-G, forgotten_command_line_options(:deployment => true)
+ source "file://#{gem_repo2}"
+
+ gem "rack"
+ gem "foo"
+ G
+ end
+
+ it "outputs a helpful message about being in deployment mode" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated"
+ expect(last_command).to be_failure
+ expect(out).to include("You are trying to check outdated gems in deployment mode.")
+ expect(out).to include("Run `bundle outdated` elsewhere.")
+ expect(out).to include("If this is a development machine, remove the ")
+ expect(out).to include("Gemfile freeze\nby running `bundle install --no-deployment`.")
+ end
+ end
+
+ context "after bundle config deployment true" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "rack"
+ gem "foo"
+ G
+ bundle! "config deployment true"
+ end
+
+ it "outputs a helpful message about being in deployment mode" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated"
+ expect(last_command).to be_failure
+ expect(out).to include("You are trying to check outdated gems in deployment mode.")
+ expect(out).to include("Run `bundle outdated` elsewhere.")
+ expect(out).to include("If this is a development machine, remove the ")
+ expect(out).to include("Gemfile freeze\nby running `bundle config --delete deployment`.")
+ end
+ end
+
+ context "update available for a gem on a different platform" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2'
+ G
+ end
+
+ it "reports that no updates are available" do
+ bundle "outdated"
+ expect(out).to include("Bundle up to date!")
+ end
+ end
+
+ context "update available for a gem on the same platform while multiple platforms used for gem" do
+ it "reports that updates are available if the Ruby platform is used" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
+ G
+
+ bundle "outdated"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "reports that updates are available if the JRuby platform is used" do
+ simulate_ruby_engine "jruby", "1.6.7" do
+ simulate_platform "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
+ G
+
+ bundle "outdated"
+ expect(out).to include("Outdated gems included in the bundle:")
+ expect(out).to include("laduradura (newest 5.15.3, installed 5.15.2, requested = 5.15.2)")
+ end
+ end
+ end
+ end
+
+ shared_examples_for "version update is detected" do
+ it "reports that a gem has a newer version" do
+ subject
+ expect(out).to include("Outdated gems included in the bundle:")
+ expect(out).to include("activesupport (newest")
+ expect(out).to_not include("ERROR REPORT TEMPLATE")
+ end
+ end
+
+ shared_examples_for "major version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "0.8.0"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ context "when on a new machine" do
+ before do
+ simulate_new_machine
+
+ update_git "foo", :path => lib_path("foo")
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "0.8.0"
+ end
+ end
+
+ subject { bundle "outdated" }
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "minor version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.7.5"
+ build_gem "weakling", "2.0.1"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "patch version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.3.7"
+ build_gem "weakling", "0.3.1"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "no version updates are detected" do
+ it "does not detect any version updates" do
+ subject
+ expect(out).to include("updates to display.")
+ expect(out).to_not include("ERROR REPORT TEMPLATE")
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to_not include("weakling (newest")
+ end
+ end
+
+ shared_examples_for "major version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "1.0.1"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ shared_examples_for "minor version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.4.5"
+ build_gem "weakling", "0.3.1"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ shared_examples_for "patch version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.3.6"
+ build_gem "weakling", "0.0.4"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ describe "with --filter-major option" do
+ subject { bundle "outdated --filter-major" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version is ignored"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-minor option" do
+ subject { bundle "outdated --filter-minor" }
+
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "major version is ignored"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-patch option" do
+ subject { bundle "outdated --filter-patch" }
+
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "major version is ignored"
+ it_behaves_like "minor version is ignored"
+ end
+
+ describe "with --filter-minor --filter-patch options" do
+ subject { bundle "outdated --filter-minor --filter-patch" }
+
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "major version is ignored"
+ end
+
+ describe "with --filter-major --filter-minor options" do
+ subject { bundle "outdated --filter-major --filter-minor" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-major --filter-patch options" do
+ subject { bundle "outdated --filter-major --filter-patch" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "minor version is ignored"
+ end
+
+ describe "with --filter-major --filter-minor --filter-patch options" do
+ subject { bundle "outdated --filter-major --filter-minor --filter-patch" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ end
+
+ context "conservative updates" do
+ context "without update-strict" do
+ before do
+ build_repo4 do
+ build_gem "patch", %w[1.0.0 1.0.1]
+ build_gem "minor", %w[1.0.0 1.0.1 1.1.0]
+ build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.0.0
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'patch', '1.0.0'
+ gem 'minor', '1.0.0'
+ gem 'major', '1.0.0'
+ G
+
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'patch'
+ gem 'minor'
+ gem 'major'
+ G
+ end
+
+ it "shows nothing when patching and filtering to minor" do
+ bundle "outdated --patch --filter-minor"
+
+ expect(out).to include("No minor updates to display.")
+ expect(out).not_to include("patch (newest")
+ expect(out).not_to include("minor (newest")
+ expect(out).not_to include("major (newest")
+ end
+
+ it "shows all gems when patching and filtering to patch" do
+ bundle "outdated --patch --filter-patch"
+
+ expect(out).to include("patch (newest 1.0.1")
+ expect(out).to include("minor (newest 1.0.1")
+ expect(out).to include("major (newest 1.0.1")
+ end
+
+ it "shows minor and major when updating to minor and filtering to patch and minor" do
+ bundle "outdated --minor --filter-minor"
+
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest 1.1.0")
+ expect(out).to include("major (newest 1.1.0")
+ end
+
+ it "shows minor when updating to major and filtering to minor with parseable" do
+ bundle "outdated --major --filter-minor --parseable"
+
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest")
+ expect(out).not_to include("major (newest")
+ end
+ end
+
+ context "with update-strict" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w[1.4.3 1.4.4] do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w[1.4.5 1.5.0] do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "foo", %w[1.5.1] do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "qux", %w[1.0.0 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
+
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ it "shows gems with update-strict updating to patch and filtering to patch" do
+ bundle "outdated --patch --update-strict --filter-patch"
+
+ expect(out).to include("foo (newest 1.4.4")
+ expect(out).to include("bar (newest 2.0.5")
+ expect(out).not_to include("qux (newest")
+ end
+ end
+ end
+
+ describe "with --only-explicit" do
+ it "does not report outdated dependent gems" do
+ build_repo4 do
+ build_gem "weakling", %w[0.2 0.3] do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "bar", %w[2.1 2.2]
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'weakling', '0.2'
+ gem 'bar', '2.1'
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'weakling'
+ G
+
+ bundle "outdated --only-explicit"
+
+ expect(out).to include("weakling (newest 0.3")
+ expect(out).not_to include("bar (newest 2.2")
+ end
+ end
+end
diff --git a/spec/bundler/commands/package_spec.rb b/spec/bundler/commands/package_spec.rb
new file mode 100644
index 0000000000..6351909bc7
--- /dev/null
+++ b/spec/bundler/commands/package_spec.rb
@@ -0,0 +1,306 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle package" do
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "package --gemfile=NotGemfile"
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with --all" do
+ context "without a gemspec" do
+ it "caches all dependencies except bundler itself" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'bundler'
+ D
+
+ bundle :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+
+ context "with a gemspec" do
+ context "that has the same name as the gem" do
+ before do
+ File.open(bundled_app("mygem.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gem" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+
+ context "that has a different name as the gem" do
+ before do
+ File.open(bundled_app("mygem_diffname.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gem" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+ end
+
+ context "with multiple gemspecs" do
+ before do
+ File.open(bundled_app("mygem.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ File.open(bundled_app("mygem_client.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem_test"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "weakling", "=0.0.3"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gems" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec :name => 'mygem'
+ gemspec :name => 'mygem_test'
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/mygem_test-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+ end
+
+ context "with --path", :bundler => "< 2" do
+ it "sets root directory for gems" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! :package, forgotten_command_line_options(:path => bundled_app("test"))
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(bundled_app("test/vendor/cache/")).to exist
+ end
+ end
+
+ context "with --no-install" do
+ it "puts the gems in vendor/cache but does not install them" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! "package --no-install"
+
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "does not prevent installing gems with bundle install" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! "package --no-install"
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with --all-platforms" do
+ it "puts the gems in vendor/cache even for other rubies", :ruby => "2.1" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack', :platforms => :ruby_19
+ D
+
+ bundle "package --all-platforms"
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with --frozen" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "install"
+ end
+
+ subject { bundle :package, forgotten_command_line_options(:frozen => true) }
+
+ it "tries to install with frozen" do
+ bundle! "config deployment true"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+ subject
+ expect(exitstatus).to eq(16) if exitstatus
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ bundle "env"
+ expect(out).to include("frozen").or include("deployment")
+ end
+ end
+end
+
+RSpec.describe "bundle install with gem sources" do
+ describe "when cached and locked" do
+ it "does not hit the remote at all" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle :pack
+ simulate_new_machine
+ FileUtils.rm_rf gem_repo2
+
+ bundle "install --local"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not hit the remote at all" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle! :pack
+ simulate_new_machine
+ FileUtils.rm_rf gem_repo2
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not reinstall already-installed gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :pack
+
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "raise 'omg'"
+ end
+
+ bundle :install
+ expect(err).to lack_errors
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "ignores cached gems for the wrong platform" do
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+ bundle :pack
+ end
+
+ simulate_new_machine
+
+ simulate_platform "ruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 RUBY")
+ end
+ end
+
+ it "does not update the cache if --no-cache is passed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundled_app("vendor/cache").mkpath
+ expect(bundled_app("vendor/cache").children).to be_empty
+
+ bundle "install --no-cache"
+ expect(bundled_app("vendor/cache").children).to be_empty
+ end
+ end
+end
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
new file mode 100644
index 0000000000..0bfc37560a
--- /dev/null
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_fileutils"
+
+RSpec.describe "bundle pristine", :ruby_repo do
+ before :each do
+ build_lib "baz", :path => bundled_app do |s|
+ s.version = "1.0.0"
+ s.add_development_dependency "baz-dev", "=1.0.0"
+ end
+
+ build_repo2 do
+ build_gem "weakling"
+ build_gem "baz-dev", "1.0.0"
+ build_gem "very_simple_binary", &:add_c_extension
+ build_git "foo", :path => lib_path("foo")
+ build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension
+ build_lib "bar", :path => lib_path("bar")
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "weakling"
+ gem "very_simple_binary"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "git_with_ext", :git => "#{lib_path("git_with_ext")}"
+ gem "bar", :path => "#{lib_path("bar")}"
+
+ gemspec
+ G
+ end
+
+ context "when sourced from RubyGems" do
+ it "reverts using cached .gem file" do
+ spec = Bundler.definition.specs["weakling"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+
+ bundle "pristine"
+ expect(changes_txt).to_not be_file
+ end
+
+ it "does not delete the bundler gem" do
+ ENV["BUNDLER_SPEC_KEEP_DEFAULT_BUNDLER_GEM"] = "true"
+ system_gems :bundler
+ bundle! "install"
+ bundle! "pristine", :system_bundler => true
+ bundle! "-v", :system_bundler => true
+
+ expected = if Bundler::VERSION < "2.0"
+ "Bundler version"
+ else
+ Bundler::VERSION
+ end
+
+ expect(out).to start_with(expected)
+ end
+ end
+
+ context "when sourced from git repo" do
+ it "reverts by resetting to current revision`" do
+ spec = Bundler.definition.specs["foo"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/foo.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts diff }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle! "pristine"
+ expect(File.read(changed_file)).to_not include(diff)
+ end
+
+ it "removes added files" do
+ spec = Bundler.definition.specs["foo"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+
+ bundle! "pristine"
+ expect(changes_txt).not_to be_file
+ end
+ end
+
+ context "when sourced from gemspec" do
+ it "displays warning and ignores changes when sourced from gemspec" do
+ spec = Bundler.definition.specs["baz"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/baz.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts diff }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle "pristine"
+ expect(File.read(changed_file)).to include(diff)
+ expect(out).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is sourced from local path.")
+ end
+
+ it "reinstall gemspec dependency" do
+ spec = Bundler.definition.specs["baz-dev"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/baz-dev.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts "#Pristine spec changes" }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle "pristine"
+ expect(File.read(changed_file)).to_not include(diff)
+ end
+ end
+
+ context "when sourced from path" do
+ it "displays warning and ignores changes when sourced from local path" do
+ spec = Bundler.definition.specs["bar"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+ bundle "pristine"
+ expect(out).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is sourced from local path.")
+ expect(changes_txt).to be_file
+ end
+ end
+
+ context "when passing a list of gems to pristine" do
+ it "resets them" do
+ foo = Bundler.definition.specs["foo"].first
+ foo_changes_txt = Pathname.new(foo.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(foo_changes_txt)
+ expect(foo_changes_txt).to be_file
+
+ bar = Bundler.definition.specs["bar"].first
+ bar_changes_txt = Pathname.new(bar.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(bar_changes_txt)
+ expect(bar_changes_txt).to be_file
+
+ weakling = Bundler.definition.specs["weakling"].first
+ weakling_changes_txt = Pathname.new(weakling.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(weakling_changes_txt)
+ expect(weakling_changes_txt).to be_file
+
+ bundle! "pristine foo bar weakling"
+
+ expect(out).to include("Cannot pristine bar (1.0). Gem is sourced from local path.").
+ and include("Installing weakling 1.0")
+
+ expect(weakling_changes_txt).not_to be_file
+ expect(foo_changes_txt).not_to be_file
+ expect(bar_changes_txt).to be_file
+ end
+
+ it "raises when one of them is not in the lockfile" do
+ bundle "pristine abcabcabc"
+ expect(out).to include("Could not find gem 'abcabcabc'.")
+ end
+ end
+
+ context "when a build config exists for one of the gems" do
+ let(:very_simple_binary) { Bundler.definition.specs["very_simple_binary"].first }
+ let(:c_ext_dir) { Pathname.new(very_simple_binary.full_gem_path).join("ext") }
+ let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
+ before { bundle "config build.very_simple_binary -- #{build_opt}" }
+
+ # This just verifies that the generated Makefile from the c_ext gem makes
+ # use of the build_args from the bundle config
+ it "applies the config when installing the gem" do
+ bundle! "pristine"
+
+ makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
+ expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ end
+ end
+
+ context "when a build config exists for a git sourced gem" do
+ let(:git_with_ext) { Bundler.definition.specs["git_with_ext"].first }
+ let(:c_ext_dir) { Pathname.new(git_with_ext.full_gem_path).join("ext") }
+ let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
+ before { bundle "config build.git_with_ext -- #{build_opt}" }
+
+ # This just verifies that the generated Makefile from the c_ext gem makes
+ # use of the build_args from the bundle config
+ it "applies the config when installing the gem" do
+ bundle! "pristine"
+
+ makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
+ expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
new file mode 100644
index 0000000000..faeb654b14
--- /dev/null
+++ b/spec/bundler/commands/remove_spec.rb
@@ -0,0 +1,583 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle remove" do
+ context "when no gems are specified" do
+ it "throws error" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "remove"
+
+ expect(out).to include("Please specify gems to remove.")
+ end
+ end
+
+ context "when --install flag is specified" do
+ it "removes gems from .bundle" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack --install"
+
+ expect(out).to include("rack was removed.")
+ expect(the_bundle).to_not include_gems "rack"
+ end
+ end
+
+ describe "remove single gem from gemfile" do
+ context "when gem is present in gemfile" do
+ it "shows success for removed gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when gem is not present in gemfile" do
+ it "shows warning for gem that could not be removed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ end
+ end
+ end
+
+ describe "remove mutiple gems from gemfile" do
+ context "when all gems are present in gemfile" do
+ it "shows success fir all removed gems" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle! "remove rack rails"
+
+ expect(out).to include("rack was removed.")
+ expect(out).to include("rails was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when some gems are not present in the gemfile" do
+ it "shows warning for those not present and success for those that can be removed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rails"
+ gem "minitest"
+ gem "rspec"
+ G
+
+ bundle "remove rails rack minitest"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rails"
+ gem "minitest"
+ gem "rspec"
+ G
+ end
+ end
+ end
+
+ context "with inline groups" do
+ it "removes the specified gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :group => [:dev]
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ describe "with group blocks" do
+ context "when single group block with gem to be removed is present" do
+ it "removes the group block" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when an empty block is also present" do
+ it "removes all empty blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rspec"
+ end
+
+ group :dev do
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when the gem belongs to mutiple groups" do
+ it "removes the groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test, :serioustest do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when the gem is present in mutiple groups" do
+ it "removes all empty blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :one do
+ gem "rspec"
+ end
+
+ group :two do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+ end
+
+ describe "nested group blocks" do
+ context "when all the groups will be empty after removal" do
+ it "removes the empty nested blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rspec"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when outer group will not be empty after removal" do
+ it "removes only empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rack-test"
+
+ group :serioustest do
+ gem "rspec"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rack-test"
+
+ end
+ G
+ end
+ end
+
+ context "when inner group will not be empty after removal" do
+ it "removes only empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rspec"
+ gem "rack-test"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rack-test"
+ end
+ end
+ G
+ end
+ end
+ end
+
+ describe "arbitrary gemfile" do
+ context "when mutiple gems are present in same line" do
+ it "shows warning for gems not removed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"; gem "rails"
+ G
+
+ bundle "remove rails"
+
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rack (>= 0, runtime) would also have been removed.")
+ end
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"; gem "rails"
+ G
+ end
+ end
+
+ context "when some gems could not be removed" do
+ it "shows warning for gems not removed and success for those removed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem"rack"
+ gem"rspec"
+ gem "rails"
+ gem "minitest"
+ G
+
+ bundle! "remove rails rack rspec minitest"
+
+ expect(out).to include("rails was removed.")
+ expect(out).to include("minitest was removed.")
+ expect(out).to include("rack, rspec could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ gem"rack"
+ gem"rspec"
+ G
+ end
+ end
+ end
+
+ context "with sources" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "rspec"
+ end
+ end
+
+ it "removes gems and empty source blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+
+ source "file://#{gem_repo3}" do
+ gem "rspec"
+ end
+ G
+
+ bundle! "install"
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+ end
+
+ describe "with eval_gemfile" do
+ context "when gems are present in both gemfiles" do
+ it "removes the gems" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ end
+ end
+
+ context "when gems are present in other gemfile" do
+ it "removes the gems" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+
+ bundle! "remove rack"
+
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
+ expect(out).to include("rack was removed.")
+ end
+ end
+
+ context "when gems to be removed are not specified in any of the gemfiles" do
+ it "throws error for the gems not present" do
+ # an empty gemfile
+ # indicating the gem is not present in the gemfile
+ create_file "Gemfile-other", <<-G
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ end
+ end
+
+ context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do
+ it "removes the gem" do
+ create_file "Gemfile-other", <<-G
+ gem "rails"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rack"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("rack was removed.")
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+ end
+ end
+
+ context "when gems can not be removed from other gemfile" do
+ it "shows error" do
+ create_file "Gemfile-other", <<-G
+ gem "rails"; gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rack"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("rack was removed.")
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.")
+ end
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+ end
+ end
+
+ context "when gems could not be removed from parent gemfile" do
+ it "shows error" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rails"; gem "rack"
+ G
+
+ bundle "remove rack"
+
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.")
+ end
+ expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rails"; gem "rack"
+ G
+ end
+ end
+
+ context "when gem present in gemfiles but could not be removed from one from one of them" do
+ it "removes gem which can be removed and shows warning for file from which it can not be removed" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem"rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
+ end
+ end
+ end
+
+ context "with install_if" do
+ it "removes gems inside blocks and empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ install_if(lambda { false }) do
+ gem "rack"
+ end
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "with env" do
+ it "removes gems inside blocks and empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" do
+ gem "rack"
+ end
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "with gemspec" do
+ it "should not remove the gem" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo.gemspec", "")
+ s.add_dependency "rack"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ bundle! "remove foo"
+
+ expect(out).to include("foo could not be removed.")
+ end
+ end
+end
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
new file mode 100644
index 0000000000..efbe4b13fb
--- /dev/null
+++ b/spec/bundler/commands/show_spec.rb
@@ -0,0 +1,244 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle show", :bundler => "< 2", :ruby => ">= 2.0" do
+ context "with a standard Gemfile" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "creates a Gemfile.lock if one did not exist" do
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "show"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "creates a Gemfile.lock when invoked with a gem name" do
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "show rails"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "prints path if gem exists in bundle" do
+ bundle "show rails"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ context "when show command deprecation is enabled" do
+ before { bundle "config major_deprecations yes" }
+
+ it "prints path if gem exists in bundle" do
+ bundle "show rails"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info rails` instead of `bundle show rails`\n" +
+ default_bundle_path("gems", "rails-2.3.2").to_s
+ )
+ end
+
+ it "prints the path to the running bundler" do
+ bundle "show bundler"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info bundler` instead of `bundle show bundler`\n" +
+ root.to_s
+ )
+ end
+
+ it "prints path if gem exists in bundle (with --paths option)" do
+ bundle "show rails --paths"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info rails --path` instead of `bundle show rails --paths`\n" +
+ default_bundle_path("gems", "rails-2.3.2").to_s
+ )
+ end
+
+ it "prints path of all gems in bundle sorted by name" do
+ bundle "show --paths"
+
+ expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s)
+ expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ out_lines = out.split("\n")
+ expect(out_lines[0]).to eq("[DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show --paths`")
+
+ # Gem names are the last component of their path.
+ gem_list = out_lines[1..-1].map {|p| p.split("/").last }
+ expect(gem_list).to eq(gem_list.sort)
+ end
+ end
+
+ it "prints path if gem exists in bundle (with --paths option)" do
+ bundle "show rails --paths"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "warns if path no longer exists on disk" do
+ FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+
+ bundle "show rails"
+
+ expect(out).to match(/has been deleted/i).
+ and include(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "prints the path to the running bundler" do
+ bundle "show bundler"
+ expect(out).to eq(root.to_s)
+ end
+
+ it "complains if gem not in bundle" do
+ bundle "show missing"
+ expect(out).to match(/could not find gem 'missing'/i)
+ end
+
+ it "prints path of all gems in bundle sorted by name" do
+ bundle "show --paths"
+
+ expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s)
+ expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ # Gem names are the last component of their path.
+ gem_list = out.split.map {|p| p.split("/").last }
+ expect(gem_list).to eq(gem_list.sort)
+ end
+
+ it "prints summary of gems" do
+ bundle "show --verbose"
+
+ loaded_bundler_spec = Bundler.load.specs["bundler"]
+ expected = if !loaded_bundler_spec.empty?
+ loaded_bundler_spec[0].homepage
+ else
+ "No website available."
+ end
+
+ expect(out).to include("* actionmailer (2.3.2)")
+ expect(out).to include("\tSummary: This is just a fake gem for testing")
+ expect(out).to include("\tHomepage: #{expected}")
+ expect(out).to include("\tStatus: Up to date")
+ end
+ end
+
+ context "with a git repo in the Gemfile" do
+ before :each do
+ @git = build_git "foo", "1.0"
+ end
+
+ it "prints out git info" do
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ end
+
+ it "prints out branch names other than master" do
+ update_git "foo", :branch => "omg" do |s|
+ s.write "lib/foo.rb", "FOO = '1.0.omg'"
+ end
+ @revision = revision_for(lib_path("foo-1.0"))[0...6]
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+ expect(the_bundle).to include_gems "foo 1.0.omg"
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{@git.ref_for("omg", 6)}")
+ end
+
+ it "doesn't print the branch when tied to a ref" do
+ sha = revision_for(lib_path("foo-1.0"))
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{sha}"
+ G
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{sha[0..6]})")
+ end
+
+ it "handles when a version is a '-' prerelease", :rubygems => "2.1" do
+ @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ install_gemfile <<-G
+ gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
+ G
+ expect(the_bundle).to include_gems "foo 1.0.0.pre.beta.1"
+
+ bundle! :show
+ expect(out).to include("foo (1.0.0.pre.beta.1")
+ end
+ end
+
+ context "in a fresh gem in a blank git repo" do
+ before :each do
+ build_git "foo", :path => lib_path("foo")
+ in_app_root_custom lib_path("foo")
+ File.open("Gemfile", "w") {|f| f.puts "gemspec" }
+ sys_exec "rm -rf .git && git init"
+ end
+
+ it "does not output git errors" do
+ bundle :show
+ expect(err).to lack_errors
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :show
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ context "with an invalid regexp for gem name" do
+ it "does not find the gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ invalid_regexp = "[]"
+
+ bundle "show #{invalid_regexp}"
+ expect(out).to include("Could not find gem '#{invalid_regexp}'.")
+ end
+ end
+
+ context "--outdated option" do
+ # Regression test for https://github.com/bundler/bundler/issues/5375
+ before do
+ build_repo2
+ end
+
+ it "doesn't update gems to newer versions" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gem("rails 2.3.2")
+
+ update_repo2 do
+ build_gem "rails", "3.0.0" do |s|
+ s.executables = "rails"
+ end
+ end
+
+ bundle! "show --outdated"
+
+ bundle! "install"
+ expect(the_bundle).to include_gem("rails 2.3.2")
+ end
+ end
+end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
new file mode 100644
index 0000000000..6eb49d3acd
--- /dev/null
+++ b/spec/bundler/commands/update_spec.rb
@@ -0,0 +1,943 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ before :each do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ G
+ end
+
+ describe "with no arguments", :bundler => "< 2" do
+ it "updates the entire bundle" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update"
+ expect(out).to include("Bundle updated!")
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ exit!
+ G
+ bundle "update"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "with --all", :bundler => "2" do
+ it "updates the entire bundle" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle! "update", :all => true
+ expect(out).to include("Bundle updated!")
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ exit!
+ G
+ bundle "update", :all => true
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "with --gemfile" do
+ it "creates lock files based on the Gemfile name" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+
+ bundle! "update --gemfile OmgFile", :all => bundle_update_requires_all?
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+ end
+
+ context "when update_requires_all_flag is set" do
+ before { bundle! "config update_requires_all_flag true" }
+
+ it "errors when passed nothing" do
+ install_gemfile! ""
+ bundle :update
+ expect(out).to eq("To update everything, pass the `--all` flag.")
+ end
+
+ it "errors when passed --all and another option" do
+ install_gemfile! ""
+ bundle "update --all foo"
+ expect(out).to eq("Cannot specify --all along with specific options.")
+ end
+
+ it "updates everything when passed --all" do
+ install_gemfile! ""
+ bundle "update --all"
+ expect(out).to include("Bundle updated!")
+ end
+ end
+
+ describe "--quiet argument" do
+ it "hides UI messages" do
+ bundle "update --quiet"
+ expect(out).not_to include("Bundle updated!")
+ end
+ end
+
+ describe "with a top level dependency" do
+ it "unlocks all child dependencies that are unrelated to other locked dependencies" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update rack-obama"
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 2.3.5"
+ end
+ end
+
+ describe "with an unknown dependency" do
+ it "should inform the user" do
+ bundle "update halting-problem-solver"
+ expect(out).to include "Could not find gem 'halting-problem-solver'"
+ end
+ it "should suggest alternatives" do
+ bundle "update active-support"
+ expect(out).to include "Did you mean activesupport?"
+ end
+ end
+
+ describe "with a child dependency" do
+ it "should update the child dependency" do
+ update_repo2
+ bundle "update rack"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ describe "when a possible resolve requires an older version of a locked gem" do
+ context "and only_update_to_newer_versions is set" do
+ before do
+ bundle! "config only_update_to_newer_versions true"
+ end
+
+ it "does not go to an older version" do
+ build_repo4 do
+ build_gem "tilt", "2.0.8"
+ build_gem "slim", "3.0.9" do |s|
+ s.add_dependency "tilt", [">= 1.3.3", "< 2.1"]
+ end
+ build_gem "slim_lint", "0.16.1" do |s|
+ s.add_dependency "slim", [">= 3.0", "< 5.0"]
+ end
+ build_gem "slim-rails", "0.2.1" do |s|
+ s.add_dependency "slim", ">= 0.9.2"
+ end
+ build_gem "slim-rails", "3.1.3" do |s|
+ s.add_dependency "slim", "~> 3.0"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "slim-rails"
+ gem "slim_lint"
+ G
+
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+
+ update_repo4 do
+ build_gem "slim", "4.0.0" do |s|
+ s.add_dependency "tilt", [">= 2.0.6", "< 2.1"]
+ end
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ end
+
+ it "should still downgrade if forced by the Gemfile" do
+ build_repo4 do
+ build_gem "a"
+ build_gem "b", "1.0"
+ build_gem "b", "2.0"
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "a"
+ gem "b"
+ G
+
+ expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "a"
+ gem "b", "1.0"
+ G
+
+ bundle! "update b"
+
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0")
+ end
+ end
+ end
+
+ describe "with --local option" do
+ it "doesn't hit repo2" do
+ FileUtils.rm_rf(gem_repo2)
+
+ bundle "update --local --all"
+ expect(out).not_to include("Fetching source index")
+ end
+ end
+
+ describe "with --group option" do
+ it "should update only specified group gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", :group => :development
+ gem "rack"
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 3.0"
+ expect(the_bundle).not_to include_gems "rack 1.2"
+ end
+
+ context "when conservatively updating a group with non-group sub-deps" do
+ it "should update only specified group gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activemerchant", :group => :development
+ gem "activesupport"
+ G
+ update_repo2 do
+ build_gem "activemerchant", "2.0"
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --conservative --group development"
+ expect(the_bundle).to include_gems "activemerchant 2.0"
+ expect(the_bundle).not_to include_gems "activesupport 3.0"
+ end
+ end
+
+ context "when there is a source with the same name as a gem in a group" do
+ before :each do
+ build_git "foo", :path => lib_path("activesupport")
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", :group => :development
+ gem "foo", :git => "#{lib_path("activesupport")}"
+ G
+ end
+
+ it "should not update the gems from that source" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+ update_git "foo", "2.0", :path => lib_path("activesupport")
+
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 3.0"
+ expect(the_bundle).not_to include_gems "foo 2.0"
+ end
+ end
+
+ context "when bundler itself is a transitive dependency" do
+ it "executes without error" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", :group => :development
+ gem "rack"
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
+ expect(the_bundle).not_to include_gems "rack 1.2"
+ end
+ end
+ end
+
+ describe "in a frozen bundle" do
+ it "should fail loudly", :bundler => "< 2" do
+ bundle! "install --deployment"
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(last_command).to be_failure
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
+ expect(out).to match(/freeze \nby running `bundle install --no-deployment`./m)
+ end
+
+ it "should suggest different command when frozen is set globally", :bundler => "< 2" do
+ bundle! "config --global frozen 1"
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
+ and match(/freeze \nby running `bundle config --delete frozen`./m)
+ end
+
+ it "should suggest different command when frozen is set globally", :bundler => "2" do
+ bundle! "config --global deployment true"
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
+ and match(/freeze \nby running `bundle config --delete deployment`./m)
+ end
+ end
+
+ describe "with --source option" do
+ it "should not update gems not included in the source that happen to have the same name", :bundler => "< 2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle! "update --source activesupport"
+ expect(the_bundle).to include_gem "activesupport 3.0"
+ end
+
+ it "should not update gems not included in the source that happen to have the same name", :bundler => "2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle! "update --source activesupport"
+ expect(the_bundle).not_to include_gem "activesupport 3.0"
+ end
+
+ context "with unlock_source_unlocks_spec set to false" do
+ before { bundle! "config unlock_source_unlocks_spec false" }
+
+ it "should not update gems not included in the source that happen to have the same name" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "update --source activesupport"
+ expect(the_bundle).not_to include_gems "activesupport 3.0"
+ end
+ end
+ end
+
+ context "when there is a child dependency that is also in the gemfile" do
+ before do
+ build_repo2 do
+ build_gem "fred", "1.0"
+ build_gem "harry", "1.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "harry"
+ gem "fred"
+ G
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 2" do
+ update_repo2 do
+ build_gem "fred", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 2.0"
+ expect(the_bundle).to include_gems "fred 1.0"
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "2" do
+ update_repo2 do
+ build_gem "fred", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 1.0", "fred 1.0"
+ end
+ end
+
+ context "when there is a child dependency that appears elsewhere in the dependency graph" do
+ before do
+ build_repo2 do
+ build_gem "fred", "1.0" do |s|
+ s.add_dependency "george"
+ end
+ build_gem "george", "1.0"
+ build_gem "harry", "1.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "harry"
+ gem "fred"
+ G
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 2" do
+ update_repo2 do
+ build_gem "george", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 2.0"
+ expect(the_bundle).to include_gems "fred 1.0"
+ expect(the_bundle).to include_gems "george 1.0"
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "2" do
+ update_repo2 do
+ build_gem "george", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 1.0", "fred 1.0", "george 1.0"
+ end
+ end
+end
+
+RSpec.describe "bundle update in more complicated situations" do
+ before :each do
+ build_repo2
+ end
+
+ it "will eagerly unlock dependencies of a specified gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "thin"
+ gem "rack-obama"
+ G
+
+ update_repo2 do
+ build_gem "thin", "2.0" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ bundle "update thin"
+ expect(the_bundle).to include_gems "thin 2.0", "rack 1.2", "rack-obama 1.0"
+ end
+
+ it "will warn when some explicitly updated gems are not updated" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo2}"
+
+ gem "thin"
+ gem "rack-obama"
+ G
+
+ update_repo2 do
+ build_gem("thin", "2.0") {|s| s.add_dependency "rack" }
+ build_gem "rack", "10.0"
+ end
+
+ bundle! "update thin rack-obama"
+ expect(last_command.stdboth).to include "Bundler attempted to update rack-obama but its version stayed the same"
+ expect(the_bundle).to include_gems "thin 2.0", "rack 10.0", "rack-obama 1.0"
+ end
+
+ it "will update only from pinned source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ source "file://#{gem_repo1}" do
+ gem "thin"
+ end
+ G
+
+ update_repo2 do
+ build_gem "thin", "2.0"
+ end
+
+ bundle "update"
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ context "when the lockfile is for a different platform" do
+ before do
+ build_repo4 do
+ build_gem("a", "0.9")
+ build_gem("a", "0.9") {|s| s.platform = "java" }
+ build_gem("a", "1.1")
+ build_gem("a", "1.1") {|s| s.platform = "java" }
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "a"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ a (0.9-java)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ a
+ L
+
+ simulate_platform linux
+ end
+
+ it "allows updating" do
+ bundle! :update, :all => true
+ expect(the_bundle).to include_gem "a 1.1"
+ end
+
+ it "allows updating a specific gem" do
+ bundle! "update a"
+ expect(the_bundle).to include_gem "a 1.1"
+ end
+ end
+end
+
+RSpec.describe "bundle update without a Gemfile.lock" do
+ it "should not explode" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+end
+
+RSpec.describe "bundle update when a gem depends on a newer version of bundler" do
+ before(:each) do
+ build_repo2 do
+ build_gem "rails", "3.0.1" do |s|
+ s.add_dependency "bundler", Bundler::VERSION.succ
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.1"
+ G
+ end
+
+ it "should explain that bundler conflicted", :bundler => "< 2" do
+ bundle "update", :all => bundle_update_requires_all?
+ expect(last_command.stdboth).not_to match(/in snapshot/i)
+ expect(last_command.bundler_err).to match(/current Bundler version/i).
+ and match(/perhaps you need to update bundler/i)
+ end
+
+ it "should warn that the newer version of Bundler would conflict", :bundler => "2" do
+ bundle! "update", :all => true
+ expect(last_command.bundler_err).to include("rails (3.0.1) has dependency bundler").
+ and include("so the dependency is being ignored")
+ expect(the_bundle).to include_gem "rails 3.0.1"
+ end
+end
+
+RSpec.describe "bundle update" do
+ it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 2" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Using activesupport 2.3.5")
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
+ end
+
+ context "with suppress_install_using_messages set" do
+ before { bundle! "config suppress_install_using_messages true" }
+
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo4}"
+ gem "bar"
+ gem "foo"
+ G
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include "Resolving dependencies...\nBundle updated!"
+
+ update_repo4 do
+ build_gem "foo", "2.0"
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.sub!("Removing foo (1.0)\n", "")
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include strip_whitespace(<<-EOS).strip
+ Resolving dependencies...
+ Fetching foo 2.0 (was 1.0)
+ Installing foo 2.0 (was 1.0)
+ Bundle updated
+ EOS
+ end
+ end
+
+ it "shows error message when Gemfile.lock is not preset and gem is specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+
+ bundle "update nonexisting"
+ expect(out).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.")
+ expect(exitstatus).to eq(22) if exitstatus
+ end
+end
+
+RSpec.describe "bundle update --ruby" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.1.0'
+ G
+ bundle "update --ruby"
+ end
+
+ context "when the Gemfile removes the ruby" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.4'
+ ::RUBY_PATCHLEVEL = 222
+ G
+ end
+ it "removes the Ruby from the Gemfile.lock" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when the Gemfile specified an updated Ruby version" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.4'
+ ::RUBY_PATCHLEVEL = 222
+ ruby '~> 2.1.0'
+ G
+ end
+ it "updates the Gemfile.lock with the latest version" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.1.4p222
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when a different Ruby is being used than has been versioned" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.2.2'
+ ::RUBY_PATCHLEVEL = 505
+ ruby '~> 2.1.0'
+ G
+ end
+ it "shows a helpful error message" do
+ bundle "update --ruby"
+
+ expect(out).to include("Your Ruby version is 2.2.2, but your Gemfile specified ~> 2.1.0")
+ end
+ end
+
+ context "when updating Ruby version and Gemfile `ruby`" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '1.8.3'
+ ::RUBY_PATCHLEVEL = 55
+ ruby '~> 1.8.0'
+ G
+ end
+ it "updates the Gemfile.lock with the latest version" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 1.8.3p55
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+end
+
+RSpec.describe "bundle update --bundler" do
+ it "updates the bundler version in the lockfile without re-resolving" do
+ build_repo4 do
+ build_gem "rack", "1.0"
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "rack"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ FileUtils.rm_r gem_repo4
+
+ bundle! :update, :bundler => true, :verbose => true
+ expect(the_bundle).to include_gem "rack 1.0"
+
+ expect(the_bundle.locked_gems.bundler_version).to eq v(Bundler::VERSION)
+ end
+end
+
+# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
+RSpec.describe "bundle update conservative" do
+ context "patch and minor options" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w[1.4.3 1.4.4] do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w[1.4.5 1.5.0] do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "foo", %w[1.5.1] do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
+
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ context "patch preferred" do
+ it "single gem updates dependent gem to minor" do
+ bundle! "update --patch foo"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
+ end
+
+ it "update all" do
+ bundle! "update --patch", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
+ end
+ end
+
+ context "minor preferred" do
+ it "single gem updates dependent gem to major" do
+ bundle! "update --minor foo"
+
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ end
+ end
+
+ context "strict" do
+ it "patch preferred" do
+ bundle! "update --patch foo bar --strict"
+
+ expect(the_bundle).to include_gems "foo 1.4.4", "bar 2.0.5", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle! "update --minor --strict", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ end
+ end
+ end
+
+ context "eager unlocking" do
+ before do
+ build_repo4 do
+ build_gem "isolated_owner", %w[1.0.1 1.0.2] do |s|
+ s.add_dependency "isolated_dep", "~> 2.0"
+ end
+ build_gem "isolated_dep", %w[2.0.1 2.0.2]
+
+ build_gem "shared_owner_a", %w[3.0.1 3.0.2] do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_owner_b", %w[4.0.1 4.0.2] do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_dep", %w[5.0.1 5.0.2]
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
+
+ gem 'shared_owner_a'
+ gem 'shared_owner_b'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.1)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.1)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.1)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ shared_owner_a
+ shared_owner_b
+ isolated_owner
+
+ BUNDLED WITH
+ 1.13.0
+ L
+ end
+
+ it "should eagerly unlock isolated dependency" do
+ bundle "update isolated_owner"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.1", "shared_owner_b 4.0.1"
+ end
+
+ it "should eagerly unlock shared dependency" do
+ bundle "update shared_owner_a"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.1", "isolated_dep 2.0.1", "shared_dep 5.0.2", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should not eagerly unlock with --conservative" do
+ bundle "update --conservative shared_owner_a isolated_owner"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should match bundle install conservative update behavior when not eagerly unlocking" do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner', '1.0.2'
+
+ gem 'shared_owner_a', '3.0.2'
+ gem 'shared_owner_b'
+ G
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+ end
+
+ context "error handling" do
+ before do
+ gemfile ""
+ end
+
+ it "raises if too many flags are provided" do
+ bundle "update --patch --minor", :all => bundle_update_requires_all?
+
+ expect(last_command.bundler_err).to eq "Provide only one of the following options: minor, patch"
+ end
+ end
+end
diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb
new file mode 100644
index 0000000000..b919c25e0f
--- /dev/null
+++ b/spec/bundler/commands/version_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle version" do
+ context "with -v" do
+ it "outputs the version", :bundler => "< 2" do
+ bundle! "-v"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "outputs the version", :bundler => "2" do
+ bundle! "-v"
+ expect(out).to eq(Bundler::VERSION)
+ end
+ end
+
+ context "with --version" do
+ it "outputs the version", :bundler => "< 2" do
+ bundle! "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "outputs the version", :bundler => "2" do
+ bundle! "--version"
+ expect(out).to eq(Bundler::VERSION)
+ end
+ end
+
+ context "with version" do
+ it "outputs the version with build metadata", :bundler => "< 2" do
+ bundle! "version"
+ expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit [a-fA-F0-9]{7,}\)\z/)
+ end
+
+ it "outputs the version with build metadata", :bundler => "2" do
+ bundle! "version"
+ expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit [a-fA-F0-9]{7,}\)\z/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
new file mode 100644
index 0000000000..3804d3561c
--- /dev/null
+++ b/spec/bundler/commands/viz_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle viz", :ruby => "1.9.3", :bundler => "< 2", :if => Bundler.which("dot") do
+ let(:ruby_graphviz) do
+ graphviz_glob = base_system_gems.join("cache/ruby-graphviz*")
+ Pathname.glob(graphviz_glob).first
+ end
+
+ before do
+ system_gems ruby_graphviz
+ end
+
+ it "graphs gems from the Gemfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle! "viz"
+ expect(out).to include("gem_graph.png")
+
+ bundle! "viz", :format => "debug"
+ expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ DOT
+ end
+
+ it "graphs gems that are prereleases" do
+ build_repo2 do
+ build_gem "rack", "1.3.pre"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "= 1.3.pre"
+ gem "rack-obama"
+ G
+
+ bundle! "viz"
+ expect(out).to include("gem_graph.png")
+
+ bundle! "viz", :format => :debug, :version => true
+ expect(out).to eq(strip_whitespace(<<-EOS).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack\\n1.3.pre"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama\\n1.0"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ EOS
+ end
+
+ context "with another gem that has a graphviz file" do
+ before do
+ build_repo4 do
+ build_gem "graphviz", "999" do |s|
+ s.write("lib/graphviz.rb", "abort 'wrong graphviz gem loaded'")
+ end
+ end
+
+ system_gems ruby_graphviz, "graphviz-999", :gem_repo => gem_repo4
+ end
+
+ it "loads the correct ruby-graphviz gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle! "viz", :format => "debug"
+ expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ DOT
+ end
+ end
+
+ context "--without option" do
+ it "one group" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails"
+ end
+ G
+
+ bundle! "viz --without=rails"
+ expect(out).to include("gem_graph.png")
+ end
+
+ it "two groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rack do
+ gem "rack"
+ end
+
+ group :rails do
+ gem "rails"
+ end
+ G
+
+ bundle! "viz --without=rails:rack"
+ expect(out).to include("gem_graph.png")
+ end
+ end
+end
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
new file mode 100644
index 0000000000..d4bb595771
--- /dev/null
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with :allow_offline_install" do
+ before do
+ bundle "config allow_offline_install true"
+ end
+
+ context "with no cached data locally" do
+ it "still installs" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(the_bundle).to include_gem("rack 1.0")
+ end
+
+ it "still fails when the network is down" do
+ install_gemfile <<-G, :artifice => "fail"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(out).to include("Could not reach host testgemserver.local.")
+ expect(the_bundle).to_not be_locked
+ end
+ end
+
+ context "with cached data locally" do
+ it "will install from the compact index" do
+ system_gems ["rack-1.0.0"], :path => :bundle_path
+
+ bundle! "config clean false"
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ gem "rack", "< 1.0"
+ G
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 0.9.1")
+
+ gemfile <<-G
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+
+ bundle! :update, :artifice => "fail", :all => true
+ expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error"
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0")
+ end
+
+ def break_git_remote_ops!
+ FileUtils.mkdir_p(tmp("broken_path"))
+ File.open(tmp("broken_path/git"), "w", 0o755) do |f|
+ f.puts strip_whitespace(<<-RUBY)
+ #!/usr/bin/env ruby
+ if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty?
+ warn "git remote ops have been disabled"
+ exit 1
+ end
+ ENV["PATH"] = ENV["PATH"].sub(/^.*?:/, "")
+ exec("git", *ARGV)
+ RUBY
+ end
+
+ old_path = ENV["PATH"]
+ ENV["PATH"] = "#{tmp("broken_path")}:#{ENV["PATH"]}"
+ yield if block_given?
+ ensure
+ ENV["PATH"] = old_path if block_given?
+ end
+
+ it "will install from a cached git repo" do
+ git = build_git "a", "1.0.0", :path => lib_path("a")
+ update_git("a", :path => git.path, :branch => "new_branch")
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}
+ G
+
+ break_git_remote_ops! { bundle! :update, :all => true }
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+
+ break_git_remote_ops! do
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}, :branch => "new_branch"
+ G
+ end
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+ end
+ end
+end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
new file mode 100644
index 0000000000..23eb691ab8
--- /dev/null
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install", :bundler => "< 2" do
+ describe "when system_bindir is set" do
+ # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if
+ # you want to avoid sudo installs for system gems with OS X's default ruby
+ it "overrides Gem.bindir" do
+ expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0
+ gemfile <<-G
+ require 'rubygems'
+ def Gem.bindir; "/usr/bin"; end
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(system_gem_path("altbin/rackup")).to exist
+ end
+ end
+
+ describe "when multiple gems contain the same exe", :bundler => "< 2" do
+ before do
+ build_repo2 do
+ build_gem "fake", "14" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ install_gemfile <<-G, :binstubs => true
+ source "file://#{gem_repo2}"
+ gem "fake"
+ gem "rack"
+ G
+ end
+
+ it "loads the correct spec's executable" do
+ gembin("rackup")
+ expect(out).to eq("1.2")
+ end
+ end
+end
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
new file mode 100644
index 0000000000..42863ed89b
--- /dev/null
+++ b/spec/bundler/install/bundler_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "with bundler dependencies" do
+ before(:each) do
+ build_repo2 do
+ build_gem "rails", "3.0" do |s|
+ s.add_dependency "bundler", ">= 0.9.0.pre"
+ end
+ build_gem "bundler", "0.9.1"
+ build_gem "bundler", Bundler::VERSION
+ end
+ end
+
+ it "are forced to the current bundler version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "are not added if not already present" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ expect(the_bundle).not_to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "causes a conflict if explicitly requesting a different version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ gem "bundler", "0.9.2"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "bundler":
+ In Gemfile:
+ bundler (= 0.9.2)
+
+ Current Bundler version:
+ bundler (#{Bundler::VERSION})
+ This Gemfile requires a different version of Bundler.
+ Perhaps you need to update Bundler by running `gem install bundler`?
+
+ Could not find gem 'bundler (= 0.9.2)' in any
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "works for gems with multiple versions in its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "multiple_versioned_deps 1.0.0"
+ end
+
+ it "includes bundler in the bundle when it's a child dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError; puts 'FAIL'; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "allows gem 'bundler' when Bundler is not in the Gemfile or its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError => e; puts e.backtrace; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "causes a conflict if child dependencies conflict" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activemerchant"
+ gem "rails_fail"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "activesupport":
+ In Gemfile:
+ activemerchant was resolved to 1.0, which depends on
+ activesupport (>= 2.0.0)
+
+ rails_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "causes a conflict if a child dependency conflicts with the Gemfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails_fail"
+ gem "activesupport", "2.3.5"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "activesupport":
+ In Gemfile:
+ activesupport (= 2.3.5)
+
+ rails_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "can install dependencies with newer bundler version with system gems", :ruby => "> 2" do
+ bundle! "config path.system true"
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ simulate_bundler_version "99999999.99.1"
+
+ bundle! "check", :env => { "BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD" => "1" }
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "can install dependencies with newer bundler version with a local path", :ruby => "> 2" do
+ bundle! "config path .bundle"
+ bundle! "config global_path_appends_ruby_scope true"
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ simulate_bundler_version "99999999.99.1"
+
+ bundle! "check", :env => { "BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD" => "1" }
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ context "with allow_bundler_dependency_conflicts set" do
+ before { bundle! "config allow_bundler_dependency_conflicts true" }
+
+ it "are forced to the current bundler version with warnings when no compatible version is found" do
+ build_repo4 do
+ build_gem "requires_nonexistant_bundler" do |s|
+ s.add_runtime_dependency "bundler", "99.99.99.99"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo4}"
+ gem "requires_nonexistant_bundler"
+ G
+
+ expect(out).to include "requires_nonexistant_bundler (1.0) has dependency bundler (= 99.99.99.99), " \
+ "which is unsatisfied by the current bundler version #{Bundler::VERSION}, so the dependency is being ignored"
+
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}", "requires_nonexistant_bundler 1.0"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
new file mode 100644
index 0000000000..3b9d68982a
--- /dev/null
+++ b/spec/bundler/install/deploy_spec.rb
@@ -0,0 +1,423 @@
+# frozen_string_literal: true
+
+RSpec.describe "install with --deployment or --frozen" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "with CLI flags", :bundler => "< 2" do
+ it "fails without a lockfile and says that --deployment requires a lock" do
+ bundle "install --deployment"
+ expect(out).to include("The --deployment flag requires a Gemfile.lock")
+ end
+
+ it "fails without a lockfile and says that --frozen requires a lock" do
+ bundle "install --frozen"
+ expect(out).to include("The --frozen flag requires a Gemfile.lock")
+ end
+
+ it "disallows --deployment --system" do
+ bundle "install --deployment --system"
+ expect(out).to include("You have specified both --deployment")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "disallows --deployment --path --system" do
+ bundle "install --deployment --path . --system"
+ expect(out).to include("You have specified both --path")
+ expect(out).to include("as well as --system")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "works after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle! :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "still works if you are not in the app directory and specify --gemfile" do
+ bundle "install"
+ Dir.chdir tmp do
+ simulate_new_machine
+ bundle! :install,
+ forgotten_command_line_options(:gemfile => "#{tmp}/bundled_app/Gemfile",
+ :deployment => true,
+ :path => "vendor/bundle")
+ end
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "works if you exclude a group with a git gem" do
+ build_git "foo"
+ gemfile <<-G
+ group :test do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+ bundle :install
+ bundle! :install, forgotten_command_line_options(:deployment => true, :without => "test")
+ end
+
+ it "works when you bundle exec bundle", :ruby_repo do
+ bundle :install
+ bundle "install --deployment"
+ bundle! "exec bundle check"
+ end
+
+ it "works when using path gems from the same path and the version is specified" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("nested")}"
+ gem "bar", :path => "#{lib_path("nested")}"
+ G
+
+ bundle! :install
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+
+ it "works when there are credentials in the source URL" do
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://user:pass@localgemserver.test/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "endpoint_strict_basic_authentication")
+ end
+
+ it "works with sources given by a block" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}" do
+ gem "rack"
+ end
+ G
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ describe "with an existing lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "works with the --deployment flag if you didn't change anything", :bundler => "< 2" do
+ bundle! "install --deployment"
+ end
+
+ it "works with the --frozen flag if you didn't change anything", :bundler => "< 2" do
+ bundle! "install --frozen"
+ end
+
+ it "works with BUNDLE_FROZEN if you didn't change anything" do
+ bundle! :install, :env => { "BUNDLE_FROZEN" => "true" }
+ end
+
+ it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "works if a path gem is missing but is in a without group" do
+ build_lib "path_gem"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
+ G
+ expect(the_bundle).to include_gems "path_gem 1.0"
+ FileUtils.rm_r lib_path("path_gem-1.0")
+
+ bundle! :install, forgotten_command_line_options(:path => ".bundle", :without => "development", :deployment => true).merge(:env => { :DEBUG => "1" })
+ run! "puts :WIN"
+ expect(out).to eq("WIN")
+ end
+
+ it "explodes if a path gem is missing" do
+ build_lib "path_gem"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
+ G
+ expect(the_bundle).to include_gems "path_gem 1.0"
+ FileUtils.rm_r lib_path("path_gem-1.0")
+
+ bundle :install, forgotten_command_line_options(:path => ".bundle", :deployment => true)
+ expect(out).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
+ end
+
+ it "can have --frozen set via an environment variable", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "1"
+ bundle "install"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --deployment set via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_DEPLOYMENT"] = "true"
+ bundle "install"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --frozen set to false via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "false"
+ ENV["BUNDLE_DEPLOYMENT"] = "false"
+ bundle "install"
+ expect(out).not_to include("deployment mode")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("* rack-obama")
+ end
+
+ it "explodes with the --frozen flag if you make a change and don't check in the lockfile", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama", "1.1"
+ G
+
+ bundle :install, forgotten_command_line_options(:frozen => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama (= 1.1)")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you remove a gem and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* activesupport\n\n")
+ expect(out).to include("You have deleted from the Gemfile:\n* rack")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you add a source" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* source: git://hubz.com (at master)")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")} (at master@#{revision_for(lib_path("rack-1.0"))[0..6]}")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source, leaving it pinned somewhere else" do
+ build_lib "foo", :path => lib_path("rack/foo")
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")} (at master@#{revision_for(lib_path("rack"))[0..6]})`")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ end
+
+ context "when replacing a host with the same host with credentials" do
+ let(:success_message) do
+ if Bundler::VERSION.split(".", 2).first == "1"
+ "Could not reach host localgemserver.test"
+ else
+ "Bundle complete!"
+ end
+ end
+
+ before do
+ install_gemfile <<-G
+ source "http://user_name:password@localgemserver.test/"
+ gem "rack"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ rack
+ G
+ end
+
+ it "prevents the replace by default" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+
+ context "when allow_deployment_source_credential_changes is true" do
+ before { bundle! "config allow_deployment_source_credential_changes true" }
+
+ it "allows the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/#{success_message}/)
+ end
+ end
+
+ context "when allow_deployment_source_credential_changes is false" do
+ before { bundle! "config allow_deployment_source_credential_changes false" }
+
+ it "prevents the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+ end
+
+ context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do
+ before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" }
+
+ it "allows the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/#{success_message}/)
+ end
+ end
+
+ context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do
+ before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" }
+
+ it "prevents the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+ end
+ end
+
+ it "remembers that the bundle is frozen at runtime" do
+ bundle! :lock
+
+ bundle! "config deployment true"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(err).to include strip_whitespace(<<-E).strip
+The dependencies in your gemfile changed
+
+You have added to the Gemfile:
+* rack (= 1.0.0)
+* rack-obama
+
+You have deleted from the Gemfile:
+* rack
+ E
+ end
+ end
+
+ context "with path in Gemfile and packed" do
+ it "works fine after bundle package and bundle install --local" do
+ build_lib "foo", :path => lib_path("foo")
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle! :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo")).to be_directory
+
+ bundle! "install --local"
+ expect(out).to include("Updating files in vendor/cache")
+
+ simulate_new_machine
+ bundle! "install --verbose", forgotten_command_line_options(:deployment => true)
+ expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).to include("vendor/cache/foo")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
new file mode 100644
index 0000000000..b4cdf13857
--- /dev/null
+++ b/spec/bundler/install/failure_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "installing a gem fails" do
+ it "prints out why that gem was being installed" do
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo2}"
+ gem "rails"
+ G
+ expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip))
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+
+ context "when installing a git gem" do
+ it "does not tell the user to run 'gem install'" do
+ build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo1}"
+ gem "rails"
+ gem "activesupport", :git => "#{lib_path("activesupport")}"
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+
+ context "when installing a gem using a git block" do
+ it "does not tell the user to run 'gem install'" do
+ build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo1}"
+ gem "rails"
+
+ git "#{lib_path("activesupport")}" do
+ gem "activesupport"
+ end
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+
+ it "prints out the hint for the remote source when available" do
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+ end
+
+ build_repo4 do
+ build_gem "a"
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo4}"
+ source "file:\/\/localhost#{gem_repo2}" do
+ gem "rails"
+ end
+ G
+ expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip))
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
new file mode 100644
index 0000000000..035d3692aa
--- /dev/null
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
+ before do
+ build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s|
+ s.name = "gunks"
+ s.version = "0.0.1"
+ end
+ end
+
+ context "eval-ed Gemfile points to an internal gemspec" do
+ before do
+ create_file "Gemfile-other", <<-G
+ gemspec :path => 'gems/gunks'
+ G
+ end
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'Gemfile-other'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ end
+ end
+
+ context "eval-ed Gemfile has relative-path gems" do
+ before do
+ build_lib("a", :path => "gems/a")
+ create_file "nested/Gemfile-nested", <<-G
+ gem "a", :path => "../gems/a"
+ G
+
+ gemfile <<-G
+ eval_gemfile "nested/Gemfile-nested"
+ G
+ end
+
+ it "installs the path gem" do
+ bundle! :install
+ expect(the_bundle).to include_gem("a 1.0")
+ end
+
+ # Make sure that we are properly comparing path based gems between the
+ # parsed lockfile and the evaluated gemfile.
+ it "bundles with --deployment" do
+ bundle! :install
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+ end
+
+ context "Gemfile uses gemspec paths after eval-ing a Gemfile" do
+ before { create_file "other/Gemfile-other" }
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'other/Gemfile-other'
+ gemspec :path => 'gems/gunks'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ end
+ end
+
+ context "eval-ed Gemfile references other gemfiles" do
+ it "works with relative paths" do
+ create_file "other/Gemfile-other", "gem 'rack'"
+ create_file "other/Gemfile", "eval_gemfile 'Gemfile-other'"
+ create_file "Gemfile-alt", <<-G
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ G
+ install_gemfile! "eval_gemfile File.expand_path('Gemfile-alt')"
+
+ expect(the_bundle).to include_gem "rack 1.0.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
new file mode 100644
index 0000000000..7ce037730e
--- /dev/null
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -0,0 +1,672 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install from an existing gemspec" do
+ before(:each) do
+ build_repo2 do
+ build_gem "bar"
+ build_gem "bar-dev"
+ end
+ end
+
+ it "should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "that is hidden should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ FileUtils.mv tmp.join("foo", "foo.gemspec"), tmp.join("foo", ".gemspec")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should handle a list of requirements" do
+ update_repo2 do
+ build_gem "baz", "1.0"
+ build_gem "baz", "1.1"
+ end
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "baz", ">= 1.0", "< 1.1"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "baz 1.0"
+ end
+
+ it "should raise if there are no gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo"), :gemspec => false)
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.bundler_err).to match(/There are no gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should raise if there are too many gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby)
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.bundler_err).to match(/There are multiple gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should pick a specific gemspec" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should use a specific group for development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo', :development_group => :dev
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev
+ end
+
+ it "should match a lockfile even if the gemspec defines development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source 'file://#{gem_repo1}'\ngemspec")
+ s.add_dependency "actionpack", "=2.3.2"
+ s.add_development_dependency "rake", "=10.0.2"
+ end
+
+ Dir.chdir(tmp.join("foo")) do
+ bundle "install"
+ # This should really be able to rely on $stderr, but, it's not written
+ # right, so we can't. In fact, this is a bug negation test, and so it'll
+ # ghost pass in future, and will only catch a regression if the message
+ # doesn't change. Exit codes should be used correctly (they can be more
+ # than just 0 and 1).
+ output = bundle("install --deployment")
+ expect(output).not_to match(/You have added to the Gemfile/)
+ expect(output).not_to match(/You have deleted from the Gemfile/)
+ expect(output).not_to match(/install in deployment mode after changing/)
+ end
+ end
+
+ it "should match a lockfile without needing to re-resolve" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+
+ message = "Found no changes, using resolution from the lockfile"
+ expect(out.scan(message).size).to eq(1)
+ end
+
+ it "should match a lockfile without needing to re-resolve with development dependencies" do
+ simulate_platform java
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ s.add_development_dependency "thin"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+
+ message = "Found no changes, using resolution from the lockfile"
+ expect(out.scan(message).size).to eq(1)
+ end
+
+ it "should match a lockfile on non-ruby platforms with a transitive platform dependency" do
+ simulate_platform java
+ simulate_ruby_engine "jruby"
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "platform_specific"
+ end
+
+ system_gems "platform_specific-1.0-java", :path => :bundle_path, :keep_path => true
+
+ install_gemfile! <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "update --bundler", :verbose => true
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA"
+ end
+
+ it "should evaluate the gemspec in its directory" do
+ build_lib("foo", :path => tmp.join("foo"))
+ File.open(tmp.join("foo/foo.gemspec"), "w") do |s|
+ s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.stdboth).not_to include("ahh")
+ end
+
+ it "allows the gemspec to activate other gems" do
+ ENV["BUNDLE_PATH__SYSTEM"] = "true"
+ # see https://github.com/bundler/bundler/issues/5409
+ #
+ # issue was caused by rubygems having an unresolved gem during a require,
+ # so emulate that
+ system_gems %w[rack-1.0.0 rack-0.9.1 rack-obama-1.0]
+
+ build_lib("foo", :path => bundled_app)
+ gemspec = bundled_app("foo.gemspec").read
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack-obama' }"
+ end
+
+ install_gemfile! <<-G
+ gemspec
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
+
+ it "allows conflicts" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ build_gem "deps", :to_bundle => true do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1", :to_bundle => true
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+ end
+
+ it "does not break Gem.finish_resolve with conflicts", :rubygems => ">= 2" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ update_repo2 do
+ build_gem "deps" do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+
+ run! "Gem.finish_resolve; puts 'WIN'"
+ expect(out).to eq("WIN")
+ end
+
+ context "in deployment mode" do
+ context "when the lockfile was not updated after a change to the gemspec's dependencies" do
+ it "reports that installation failed" do
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5")
+
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1.0.1"
+ end
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to include("changed")
+ end
+ end
+ end
+
+ context "when child gemspecs conflict with a released gemspec" do
+ before do
+ # build the "parent" gem that depends on another gem in the same repo
+ build_lib "source_conflict", :path => bundled_app do |s|
+ s.add_dependency "rack_middleware"
+ end
+
+ # build the "child" gem that is the same version as a released gem, but
+ # has completely different and conflicting dependency requirements
+ build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s|
+ s.add_dependency "rack", "1.0" # anything other than 0.9.1
+ end
+ end
+
+ it "should install the child gemspec's deps" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ context "with a lockfile and some missing dependencies" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ context "previously bundled for Ruby" do
+ let(:platform) { "ruby" }
+ let(:explicit_platform) { false }
+
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack", "=1.0.0"
+ end
+
+ if explicit_platform
+ create_file(
+ tmp.join("foo", "foo-#{platform}.gemspec"),
+ build_spec("foo", "1.0", platform) do
+ dep "rack", "=1.0.0"
+ @spec.authors = "authors"
+ @spec.summary = "summary"
+ end.first.to_ruby
+ )
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gemspec :path => "../foo"
+ G
+
+ lockfile <<-L
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+ rack (= 1.0.0)
+
+ GEM
+ remote: #{source_uri}
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "using JRuby with explicit platform" do
+ let(:platform) { "java" }
+ let(:explicit_platform) { true }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using JRuby" do
+ let(:platform) { "java" }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using Windows" do
+ it "should install" do
+ simulate_windows do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "bundled for ruby and jruby" do
+ let(:platform_specific_type) { :runtime }
+ let(:dependency) { "platform_specific" }
+ before do
+ build_repo2 do
+ build_gem "indirect_platform_specific" do |s|
+ s.add_runtime_dependency "platform_specific"
+ end
+ end
+
+ build_lib "foo", :path => "." do |s|
+ if platform_specific_type == :runtime
+ s.add_runtime_dependency dependency
+ elsif platform_specific_type == :development
+ s.add_development_dependency dependency
+ else
+ raise "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
+ end
+ end
+
+ %w[ruby jruby].each do |platform|
+ simulate_platform(platform) do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gemspec
+ G
+ end
+ end
+ end
+
+ context "on ruby", :bundler => "< 2" do
+ before do
+ simulate_platform("ruby")
+ bundle :install
+ end
+
+ context "as a runtime dependency" do
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ platform_specific
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "as a development dependency" do
+ let(:platform_specific_type) { :development }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with an indirect platform-specific development dependency" do
+ let(:platform_specific_type) { :development }
+ let(:dependency) { "indirect_platform_specific" }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ indirect_platform_specific (1.0)
+ platform_specific
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ indirect_platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+
+ context "on ruby", :bundler => "2" do
+ before do
+ simulate_platform("ruby")
+ bundle :install
+ end
+
+ context "as a runtime dependency" do
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ platform_specific
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "as a development dependency" do
+ let(:platform_specific_type) { :development }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with an indirect platform-specific development dependency" do
+ let(:platform_specific_type) { :development }
+ let(:dependency) { "indirect_platform_specific" }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ indirect_platform_specific (1.0)
+ platform_specific
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ indirect_platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+ end
+ end
+
+ context "with multiple platforms" do
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_development_dependency "rack"
+ s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby
+ end
+ end
+
+ it "installs the ruby platform gemspec" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0", "rack 1.0.0"
+ end
+
+ it "installs the ruby platform gemspec and skips dev deps with --without development" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G, forgotten_command_line_options(:without => "development")
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0.0"
+ expect(the_bundle).not_to include_gem "rack"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
new file mode 100644
index 0000000000..57d83a5295
--- /dev/null
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -0,0 +1,1351 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with git sources" do
+ describe "when floating on master" do
+ before :each do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ it "fetches gems" do
+ expect(the_bundle).to include_gems("foo 1.0")
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "caches the git repo", :bundler => "< 2" do
+ expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
+ end
+
+ it "caches the git repo globally" do
+ simulate_new_machine
+ bundle! "config global_gem_cache true"
+ bundle! :install
+ expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes :size => 1
+ end
+
+ it "caches the evaluated gemspec" do
+ git = update_git "foo" do |s|
+ s.executables = ["foobar"] # we added this the first time, so keep it now
+ s.files = ["bin/foobar"] # updating git nukes the files list
+ foospec = s.to_ruby.gsub(/s\.files.*/, 's.files = `git ls-files -z`.split("\x0")')
+ s.write "foo.gemspec", foospec
+ end
+
+ bundle "update foo"
+
+ sha = git.ref_for("master", 11)
+ spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s
+ ruby_code = Gem::Specification.load(spec_file).to_ruby
+ file_code = File.read(spec_file)
+ expect(file_code).to eq(ruby_code)
+ end
+
+ it "does not update the git source implicitly" do
+ update_git "foo"
+
+ in_app_root2 do
+ install_gemfile bundled_app2("Gemfile"), <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ in_app_root do
+ run <<-RUBY
+ require 'foo'
+ puts "fail" if defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to be_empty
+ end
+ end
+
+ it "sets up git gem executables on the path" do
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "complains if pinned specs don't exist in the git repo" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to include("The source contains 'foo' at: 1.0")
+ end
+
+ it "complains with version and platform if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java" do |s|
+ s.platform = "java"
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
+ end
+ G
+
+ expect(out).to include("The source contains 'only_java' at: 1.0 java")
+ end
+
+ it "complains with multiple versions and platforms if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java", "1.0" do |s|
+ s.platform = "java"
+ end
+
+ build_git "only_java", "1.1" do |s|
+ s.platform = "java"
+ s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec")
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
+ end
+ G
+
+ expect(out).to include("The source contains 'only_java' at: 1.0 java, 1.1 java")
+ end
+
+ it "still works after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ Dir.chdir tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "can still install after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ update_git "foo", "1.1", :path => lib_path("foo-1.0")
+
+ Dir.chdir tmp("bundled_app.bck")
+ gemfile tmp("bundled_app.bck/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update foo"
+
+ expect(the_bundle).to include_gems "foo 1.1", "rack 1.0"
+ end
+ end
+
+ describe "with an empty git block" do
+ before do
+ build_git "foo"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ git "#{lib_path("foo-1.0")}" do
+ # this page left intentionally blank
+ end
+ G
+ end
+
+ it "does not explode" do
+ bundle "install"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when specifying a revision" do
+ before(:each) do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{@revision}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a symbol" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision.to_sym.inspect} do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a non-head ref" do
+ # want to ensure we don't fallback to master
+ update_git "foo", :path => lib_path("foo-1.0") do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ Dir.chdir(lib_path("foo-1.0")) do
+ `git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1`
+ end
+
+ # want to ensure we don't fallback to HEAD
+ update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run! <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a non-head ref and it was previously downloaded" do
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ # want to ensure we don't fallback to master
+ update_git "foo", :path => lib_path("foo-1.0") do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ Dir.chdir(lib_path("foo-1.0")) do
+ `git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1`
+ end
+
+ # want to ensure we don't fallback to HEAD
+ update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run! <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not download random non-head refs" do
+ Dir.chdir(lib_path("foo-1.0")) do
+ sys_exec!("git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1")
+ end
+
+ bundle! "config global_gem_cache true"
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ # ensure we also git fetch after cloning
+ bundle! :update, :all => bundle_update_requires_all?
+
+ Dir.chdir(Dir[home(".bundle/cache/git/foo-*")].first) do
+ sys_exec("git ls-remote .")
+ end
+
+ expect(out).not_to include("refs/bundler/1")
+ end
+ end
+
+ describe "when specifying a branch" do
+ let(:branch) { "branch" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :branch => branch)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the branch starts with a `#`" do
+ let(:branch) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the branch includes quotes" do
+ let(:branch) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying a tag" do
+ let(:tag) { "tag" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :tag => tag)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the tag starts with a `#`" do
+ let(:tag) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the tag includes quotes" do
+ let(:tag) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying local override" do
+ it "uses the local repository instead of checking a new one out" do
+ # We don't generate it because we actually don't need it
+ # build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "chooses the local repository on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "unlocks the source when the dependencies have changed while switching to the local" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+ run! "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "updates specs on runtime" do
+ system_gems "nokogiri-1.4.2"
+
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.add_dependency "nokogiri", "1.4.2"
+ end
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "updates ref on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack")
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "explodes if given path does not exist on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ end
+
+ it "explodes if branch is not given on install" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/cannot use local override/i)
+ end
+
+ it "does not explode if disable_local_branch_check is given" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle %(config disable_local_branch_check true)
+ bundle :install
+ expect(out).to match(/Bundle complete!/)
+ end
+
+ it "explodes on different branches on install" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/is using branch another but Gemfile specifies master/)
+ end
+
+ it "explodes on invalid revision on install" do
+ build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/The Gemfile lock is pointing to revision \w+/)
+ end
+ end
+
+ describe "specified inline" do
+ # TODO: Figure out how to write this test so that it is not flaky depending
+ # on the current network situation.
+ # it "supports private git URLs" do
+ # gemfile <<-G
+ # gem "thingy", :git => "git@notthere.fallingsnow.net:somebody/thingy.git"
+ # G
+ #
+ # bundle :install
+ #
+ # # p out
+ # # p err
+ # puts err unless err.empty? # This spec fails randomly every so often
+ # err.should include("notthere.fallingsnow.net")
+ # err.should include("ssh")
+ # end
+
+ it "installs from git even if a newer gem is available elsewhere" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.8"
+ end
+
+ it "installs dependencies from git even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_git "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "correctly unlocks when changing to a git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "correctly unlocks when changing to a git source without versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ build_git "rack", "1.2", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a git block" do
+ build_lib "omg", :path => lib_path("hi2u/omg")
+ build_lib "hi2u", :path => lib_path("hi2u")
+
+ install_gemfile <<-G
+ path "#{lib_path("hi2u")}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "uses a ref if specified" do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{@revision}"
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "correctly handles cases with invalid gemspecs" do
+ build_git "foo" do |s|
+ s.summary = nil
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "runs the gemspec in the context of its parent directory" do
+ build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ $:.unshift Dir.pwd # For 1.9
+ require 'lib/version'
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.author = 'no one'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ end
+ G
+ end
+
+ build_git "foo", :path => lib_path("foo") do |s|
+ s.write "bin/foo", ""
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("foo")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs from git even if a rubygems gem is present" do
+ build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'FAIL'"
+ end
+
+ build_git "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fakes the gem out if there is no gemspec" do
+ build_git "foo", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "catches git errors and spits out useful output" do
+ gemfile <<-G
+ gem "foo", "1.0", :git => "omgomg"
+ G
+
+ bundle :install
+
+ expect(out).to include("Git error:")
+ expect(err).to include("fatal")
+ expect(err).to include("omgomg")
+ end
+
+ it "works when the gem path has spaces in it" do
+ build_git "foo", :path => lib_path("foo space-1.0")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo space-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "handles repos that have been force-pushed" do
+ build_git "forced", "1.0"
+
+ install_gemfile <<-G
+ git "#{lib_path("forced-1.0")}" do
+ gem 'forced'
+ end
+ G
+ expect(the_bundle).to include_gems "forced 1.0"
+
+ update_git "forced" do |s|
+ s.write "lib/forced.rb", "FORCED = '1.1'"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "forced 1.1"
+
+ Dir.chdir(lib_path("forced-1.0")) do
+ `git reset --hard HEAD^`
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "forced 1.0"
+ end
+
+ it "ignores submodules if :submodule is not passed" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+ expect(out).to match(/could not find gem 'submodule/i)
+
+ expect(the_bundle).not_to include_gems "has_submodule 1.0"
+ end
+
+ it "handles repos with submodules" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ end
+
+ it "handles implicit updates when modifying the source info" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ update_git "foo"
+ update_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{git.ref_for("HEAD^")}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" if FOO_PREV_REF == '#{git.ref_for("HEAD^^")}'
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not to a remote fetch if the revision is cached locally" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ FileUtils.rm_rf(lib_path("foo-1.0"))
+
+ bundle "install"
+ expect(out).not_to match(/updating/i)
+ end
+
+ it "doesn't blow up if bundle install is run twice in a row" do
+ build_git "foo"
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install"
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "prints a friendly error if a file blocks the git repo" do
+ build_git "foo"
+
+ FileUtils.mkdir_p(default_bundle_path)
+ FileUtils.touch(default_bundle_path("bundler"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(exitstatus).to_not eq(0) if exitstatus
+ expect(out).to include("Bundler could not install a gem because it " \
+ "needs to create a directory, but a file exists " \
+ "- #{default_bundle_path("bundler")}")
+ end
+
+ it "does not duplicate git gem sources" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+
+ build_git "foo", :path => lib_path("nested")
+ build_git "bar", :path => lib_path("nested")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("nested")}"
+ gem "bar", :git => "#{lib_path("nested")}"
+ G
+
+ bundle "install"
+ expect(File.read(bundled_app("Gemfile.lock")).scan("GIT").size).to eq(1)
+ end
+
+ describe "switching sources" do
+ it "doesn't explode when switching Path to Git sources" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "doesn't explode when switching Gem to Git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0"
+ G
+
+ build_git "rack", "1.0" do |s|
+ s.write "lib/new_file.rb", "puts 'USING GIT'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ run "require 'new_file'"
+ expect(out).to eq("USING GIT")
+ end
+ end
+
+ describe "bundle install after the remote has been updated" do
+ it "installs" do
+ build_git "valim"
+
+ install_gemfile <<-G
+ gem "valim", :git => "file://#{lib_path("valim-1.0")}"
+ G
+
+ old_revision = revision_for(lib_path("valim-1.0"))
+ update_git "valim"
+ new_revision = revision_for(lib_path("valim-1.0"))
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.puts lockfile.gsub(/revision: #{old_revision}/, "revision: #{new_revision}")
+ end
+
+ bundle "install"
+
+ run <<-R
+ require "valim"
+ puts VALIM_PREV_REF
+ R
+
+ expect(out).to eq(old_revision)
+ end
+
+ it "gives a helpful error message when the remote ref no longer exists" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "#{revision}"
+ G
+ bundle "install"
+ expect(out).to_not match(/Revision.*does not exist/)
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "deadbeef"
+ G
+ bundle "install"
+ expect(out).to include("Revision deadbeef does not exist in the repository")
+ end
+ end
+
+ describe "bundle install --deployment with git sources" do
+ it "works" do
+ build_git "valim", :path => lib_path("valim")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "valim", "= 1.0", :git => "#{lib_path("valim")}"
+ G
+
+ simulate_new_machine
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+ end
+
+ context "with an extension" do
+ it "installs the extension", :ruby_repo do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq("YES")
+
+ run! <<-R
+ puts $:.grep(/ext/)
+ R
+ expect(out).to eq(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ end
+
+ it "does not use old extension after ref changes", :ruby_repo do
+ git_reader = build_git "foo", :no_default => true do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-RUBY
+ require "mkmf"
+ create_makefile("foo")
+ RUBY
+ s.write "ext/foo.c", "void Init_foo() {}"
+ end
+
+ 2.times do |i|
+ Dir.chdir(git_reader.path) do
+ File.open("ext/foo.c", "w") do |file|
+ file.write <<-C
+ #include "ruby.h"
+ VALUE foo() { return INT2FIX(#{i}); }
+ void Init_foo() { rb_define_global_function("foo", &foo, 0); }
+ C
+ end
+ `git commit -m 'commit for iteration #{i}' ext/foo.c`
+ end
+ git_commit_sha = git_reader.ref_for("HEAD")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{git_commit_sha}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts foo
+ R
+
+ expect(out).to eq(i.to_s)
+ end
+ end
+
+ it "does not prompt to gem install if extension fails" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ raise
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing foo (1.0), and Bundler cannot continue.
+
+In Gemfile:
+ foo
+ M
+ expect(out).not_to include("gem install foo")
+ end
+
+ it "does not reinstall the extension", :ruby_repo, :rubygems => ">= 2.3.0" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ cur_time = Time.now.to_f.to_s
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = \#{cur_time}"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+
+ installed_time = out
+ expect(installed_time).to match(/\A\d+\.\d+\z/)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq(installed_time)
+ end
+ end
+
+ it "ignores git environment variables" do
+ build_git "xxxxxx" do |s|
+ s.executables = "xxxxxxbar"
+ end
+
+ Bundler::SharedHelpers.with_clean_git_env do
+ ENV["GIT_DIR"] = "bar"
+ ENV["GIT_WORK_TREE"] = "bar"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("xxxxxx-1.0")}" do
+ gem 'xxxxxx'
+ end
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(ENV["GIT_DIR"]).to eq("bar")
+ expect(ENV["GIT_WORK_TREE"]).to eq("bar")
+ end
+ end
+
+ describe "without git installed" do
+ it "prints a better error message" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ with_path_as("") do
+ bundle "update", :all => bundle_update_requires_all?
+ end
+ expect(last_command.bundler_err).
+ to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
+ end
+
+ it "installs a packaged git gem successfully" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ bundle :package, forgotten_command_line_options([:all, :cache_all] => true)
+ simulate_new_machine
+
+ bundle! "install", :env => { "PATH" => "" }
+ expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
+ end
+ end
+
+ describe "when the git source is overridden with a local git repo" do
+ before do
+ bundle! "config --global local.foo #{lib_path("foo")}"
+ end
+
+ describe "and git output is colorized" do
+ before do
+ File.open("#{ENV["HOME"]}/.gitconfig", "w") do |f|
+ f.write("[color]\n\tui = always\n")
+ end
+ end
+
+ it "installs successfully" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ context "git sources that include credentials" do
+ context "that are username and password" do
+ let(:credentials) { "user1:password1" }
+
+ it "does not display the password" do
+ install_gemfile <<-G
+ git "https://#{credentials}@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(last_command.stdboth).to_not include("password1")
+ expect(last_command.stdout).to include("Fetching https://user1@github.com/company/private-repo")
+ end
+ end
+
+ context "that is an oauth token" do
+ let(:credentials) { "oauth_token" }
+
+ it "displays the oauth scheme but not the oauth token" do
+ install_gemfile <<-G
+ git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(last_command.stdboth).to_not include("oauth_token")
+ expect(last_command.stdout).to include("Fetching https://x-oauth-basic@github.com/company/private-repo")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
new file mode 100644
index 0000000000..19c379e188
--- /dev/null
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -0,0 +1,384 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with groups" do
+ describe "installing with no options" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :groups => [:emo]
+ G
+ end
+
+ it "installs gems in the default group" do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems in a group block into that group" do
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+
+ load_error_run <<-R, "activesupport", :default
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "installs gems with inline :groups into those groups" do
+ expect(the_bundle).to include_gems "thin 1.0"
+
+ load_error_run <<-R, "thin", :default
+ require 'thin'
+ puts THIN
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up everything if Bundler.setup is used with no groups" do
+ output = run("require 'rack'; puts RACK")
+ expect(output).to eq("1.0.0")
+
+ output = run("require 'activesupport'; puts ACTIVESUPPORT")
+ expect(output).to eq("2.3.5")
+
+ output = run("require 'thin'; puts THIN")
+ expect(output).to eq("1.0")
+ end
+
+ it "removes old groups when new groups are set up" do
+ load_error_run <<-RUBY, "thin", :emo
+ Bundler.setup(:default)
+ require 'thin'
+ puts THIN
+ RUBY
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up old groups when they have previously been removed" do
+ output = run <<-RUBY, :emo
+ Bundler.setup(:default)
+ Bundler.setup(:default, :emo)
+ require 'thin'; puts THIN
+ RUBY
+ expect(output).to eq("1.0")
+ end
+ end
+
+ describe "installing --without" do
+ describe "with gems assigned to a single group" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ group :debugging, :optional => true do
+ gem "thin"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ end
+
+ it "does not install gems from the excluded group" do
+ bundle :install, :without => "emo"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "does not install gems from the previously excluded group" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not say it installed gems from the excluded group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(out).not_to include("activesupport")
+ end
+
+ it "allows Bundler.setup for specific groups" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ run!("require 'rack'; puts RACK", :default)
+ expect(out).to eq("1.0.0")
+ end
+
+ it "does not effect the resolve" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ group :emo do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
+ end
+
+ it "still works on a different machine and excludes gems" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ simulate_new_machine
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "still works when BUNDLE_WITHOUT is set" do
+ ENV["BUNDLE_WITHOUT"] = "emo"
+
+ bundle :install
+ expect(out).not_to include("activesupport")
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+
+ ENV["BUNDLE_WITHOUT"] = nil
+ end
+
+ it "clears without when passed an empty list" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ bundle :install, forgotten_command_line_options(:without => "")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "doesn't clear without when nothing is passed" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install gems from the optional group" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional group when requested" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the previously requested group" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ expect(the_bundle).to include_gems "thin 1.0"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional groups requested with BUNDLE_WITH" do
+ ENV["BUNDLE_WITH"] = "debugging"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ ENV["BUNDLE_WITH"] = nil
+ end
+
+ it "clears with when passed an empty list" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ bundle :install, forgotten_command_line_options(:with => "")
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does remove groups from without when passed at --with", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ bundle :install, forgotten_command_line_options(:with => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does remove groups from with when passed at --without", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ bundle :install, forgotten_command_line_options(:without => "debugging")
+ expect(the_bundle).not_to include_gem "thin 1.0"
+ end
+
+ it "errors out when passing a group to with and without via CLI flags", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:with => "emo debugging", :without => "emo")
+ expect(last_command).to be_failure
+ expect(out).to include("The offending groups are: emo")
+ end
+
+ it "allows the BUNDLE_WITH setting to override BUNDLE_WITHOUT" do
+ ENV["BUNDLE_WITH"] = "debugging"
+
+ bundle! :install
+ expect(the_bundle).to include_gem "thin 1.0"
+
+ ENV["BUNDLE_WITHOUT"] = "debugging"
+ expect(the_bundle).to include_gem "thin 1.0"
+
+ bundle! :install
+ expect(the_bundle).to include_gem "thin 1.0"
+ end
+
+ it "can add and remove a group at the same time" do
+ bundle :install, forgotten_command_line_options(:with => "debugging", :without => "emo")
+ expect(the_bundle).to include_gems "thin 1.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing a not optional group in with" do
+ bundle :install, forgotten_command_line_options(:with => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing an optional group in without" do
+ bundle :install, forgotten_command_line_options(:without => "debugging")
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+ end
+
+ describe "with gems assigned to multiple groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo, :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ describe "with a gem defined multiple times in different groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs the gem w/ option --without emo" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "installs the gem w/ option --without lolercoaster" do
+ bundle :install, forgotten_command_line_options(:without => "lolercoaster")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without emo lolercoaster" do
+ bundle :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without 'emo lolercoaster'" do
+ bundle :install, forgotten_command_line_options(:without => "'emo lolercoaster'")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "nesting groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "when loading only the default group" do
+ it "should not load all groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :groups => :development
+ G
+
+ ruby <<-R
+ require "bundler"
+ Bundler.setup :default
+ Bundler.require :default
+ puts RACK
+ begin
+ require "activesupport"
+ rescue LoadError
+ puts "no activesupport"
+ end
+ R
+
+ expect(out).to include("1.0")
+ expect(out).to include("no activesupport")
+ end
+ end
+
+ describe "when locked and installed with --without" do
+ before(:each) do
+ build_repo2
+ system_gems "rack-0.9.1" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => "rack")
+ source "file://#{gem_repo2}"
+ gem "rack"
+
+ group :rack do
+ gem "rack_middleware"
+ end
+ G
+ end
+ end
+
+ it "uses the correct versions even if --without was used on the original" do
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ simulate_new_machine
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "rack_middleware 1.0"
+ end
+
+ it "does not hit the remote a second time" do
+ FileUtils.rm_rf gem_repo2
+ bundle! :install, forgotten_command_line_options(:without => "rack").merge(:verbose => true)
+ expect(last_command.stdboth).not_to match(/fetching/i)
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/install_if.rb b/spec/bundler/install/gemfile/install_if.rb
new file mode 100644
index 0000000000..1319051fdb
--- /dev/null
+++ b/spec/bundler/install/gemfile/install_if.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+describe "bundle install with install_if conditionals" do
+ it "follows the install_if DSL" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ install_if(lambda { true }) do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :install_if => false
+ install_if(lambda { false }) do
+ gem "foo"
+ end
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems("rack 1.0", "activesupport 2.3.5")
+ expect(the_bundle).not_to include_gems("thin")
+ expect(the_bundle).not_to include_gems("foo")
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ foo (1.0)
+ rack (1.0.0)
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ activesupport (= 2.3.5)
+ foo
+ rack
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+end
diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb
new file mode 100644
index 0000000000..dc1baca6ea
--- /dev/null
+++ b/spec/bundler/install/gemfile/lockfile_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with a lockfile present" do
+ let(:gf) { <<-G }
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ subject do
+ install_gemfile(gf)
+ end
+
+ context "gemfile evaluation" do
+ let(:gf) { super() + "\n\n File.open('evals', 'a') {|f| f << %(1\n) } unless ENV['BUNDLER_SPEC_NO_APPEND']" }
+
+ context "with plugins disabled" do
+ before do
+ bundle! "config plugins false"
+ subject
+ end
+
+ it "does not evaluate the gemfile twice" do
+ bundle! :install
+
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+
+ # The first eval is from the initial install, we're testing that the
+ # second install doesn't double-eval
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
+ end
+
+ context "when the gem is not installed" do
+ before { FileUtils.rm_rf ".bundle" }
+
+ it "does not evaluate the gemfile twice" do
+ bundle! :install
+
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+
+ # The first eval is from the initial install, we're testing that the
+ # second install doesn't double-eval
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
new file mode 100644
index 0000000000..f7789e7ea5
--- /dev/null
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -0,0 +1,630 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with explicit source paths" do
+ it "fetches gems with a global path source", :bundler => "< 2" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "fetches gems" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports pinned paths" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports relative paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new(Dir.pwd))
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("~").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~/#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths raise error with not existing user's home dir" do
+ build_lib "foo"
+ username = "some_unexisting_user"
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~#{username}/#{relative_path}"
+ G
+ expect(out).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
+ expect(out).to match("user #{username} doesn't exist")
+ end
+
+ it "expands paths relative to Bundler.root" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => "./foo-1.0"
+ G
+
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ it "expands paths when comparing locked paths to Gemfile paths" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => File.expand_path("../foo-1.0", __FILE__)
+ G
+
+ bundle! :install, forgotten_command_line_options(:frozen => true)
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "installs dependencies from the path even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_lib "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "works" do
+ build_gem "foo", "1.0.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ build_lib "omg", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "foo"
+ end
+
+ build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
+
+ install_gemfile <<-G
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "prefers gemspecs closer to the path root" do
+ build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s|
+ s.write "gemfiles/ruby187.gemspec", <<-G
+ Gem::Specification.new do |s|
+ s.name = 'premailer'
+ s.version = '1.0.0'
+ s.summary = 'Hi'
+ s.authors = 'Me'
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "premailer", :path => "#{lib_path("premailer")}"
+ G
+
+ # Installation of the 'gemfiles' gemspec would fail since it will be unable
+ # to require 'premailer.rb'
+ expect(the_bundle).to include_gems "premailer 1.0.0"
+ end
+
+ it "warns on invalid specs", :rubygems => "1.7" do
+ build_lib "foo"
+
+ gemspec = lib_path("foo-1.0").join("foo.gemspec").to_s
+ File.open(gemspec, "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to_not include("ERROR REPORT")
+ expect(out).to_not include("Your Gemfile has no gem server sources.")
+ expect(out).to match(/is not valid. Please fix this gemspec./)
+ expect(out).to match(/The validation error was 'missing value for attribute version'/)
+ expect(out).to match(/You have one or more invalid gemspecs that need to be fixed/)
+ end
+
+ it "supports gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ gemfile = <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("foo")) do
+ bundle "install"
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "supports gemspec syntax with an alternative path" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ s.add_development_dependency "activesupport"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "raises if there are multiple gemspecs" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to match(/There are multiple gemspecs/)
+ end
+
+ it "allows :name to be specified to resolve ambiguity" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}", :name => "foo"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "sets up executables" do
+ build_lib "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "handles directories in bin/" do
+ build_lib "foo"
+ lib_path("foo-1.0").join("foo.gemspec").rmtree
+ lib_path("foo-1.0").join("bin/performance").mkpath
+
+ install_gemfile <<-G
+ gem 'foo', '1.0', :path => "#{lib_path("foo-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+
+ it "removes the .gem file after installing" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(lib_path("foo-1.0").join("foo-1.0.gem")).not_to exist
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a path block" do
+ build_lib "omg"
+ build_lib "hi2u"
+
+ install_gemfile <<-G
+ path "#{lib_path}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "keeps source pinning" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "omg", "1.0", :path => lib_path("omg")
+ build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec" do
+ build_lib "foo", :gemspec => false
+
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec but there is a lockfile" do
+ lockfile <<-L
+ PATH
+ remote: vendor/bar
+ specs:
+
+ GEM
+ remote: http://rubygems.org
+ L
+
+ in_app_root { FileUtils.mkdir_p("vendor/bar") }
+
+ install_gemfile <<-G
+ gem "bar", "1.0.0", path: "vendor/bar", require: "bar/nyard"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ context "existing lockfile" do
+ it "rubygems gems don't re-resolve without changes" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', '1.0'
+ gem 'net-ssh', '1.0'
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+
+ it "source path gems w/deps don't re-resolve without changes" do
+ build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', :path => "#{lib_path("omg")}"
+ gem 'net-ssh', :path => "#{lib_path("omg")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+ end
+
+ it "installs executable stubs" do
+ build_lib "foo" do |s|
+ s.executables = ["foo"]
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "exec foo"
+ expect(out).to eq("1.0")
+ end
+
+ describe "when the gem version in the path is updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "unlocks all gems when the top level gem is updated" do
+ build_lib "foo", "2.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 2.0", "bar 1.0"
+ end
+
+ it "unlocks all gems when a child dependency gem is updated" do
+ build_lib "bar", "2.0", :path => lib_path("foo/bar")
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 2.0"
+ end
+ end
+
+ describe "when dependencies in the path are updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "gets dependencies that are updated in the path" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "switching sources" do
+ it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_gem "bar", "1.0", :to_system => true do |s|
+ s.write "lib/bar.rb", "raise 'fail'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ end
+ G
+
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ gem "bar"
+ end
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ end
+ end
+
+ describe "when there are both a gemspec and remote gems" do
+ it "doesn't query rubygems for local gemspec name" do
+ build_lib "private_lib", "2.2", :path => lib_path("private_lib")
+ gemfile = <<-G
+ source "http://localgemserver.test"
+ gemspec
+ gem 'rack'
+ G
+ File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("private_lib")) do
+ bundle :install, :env => { "DEBUG" => 1 }, :artifice => "endpoint"
+ expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
+ expect(out).not_to match(/^HTTP GET.*private_lib/)
+ expect(the_bundle).to include_gems "private_lib 2.2"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+
+ it "loads plugins from the path gem" do
+ foo_file = home("foo_plugin_loaded")
+ bar_file = home("bar_plugin_loaded")
+ expect(foo_file).not_to be_file
+ expect(bar_file).not_to be_file
+
+ build_lib "foo" do |s|
+ s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{foo_file}')")
+ end
+
+ build_git "bar" do |s|
+ s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{bar_file}')")
+ end
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :path => "#{lib_path("bar-1.0")}"
+ G
+
+ expect(foo_file).to be_file
+ expect(bar_file).to be_file
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
new file mode 100644
index 0000000000..bfdf9b68c8
--- /dev/null
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -0,0 +1,426 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install across platforms" do
+ it "maintains the same lockfile if all gems are compatible across platforms" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ end
+
+ it "pulls in the correct platform specific gem" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+ platform_specific (1.0-x86-mswin32)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+ G
+
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 JAVA"
+ end
+
+ it "works with gems that have different dependencies" do
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+
+ simulate_new_machine
+
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ expect(the_bundle).not_to include_gems "weakling"
+ end
+
+ it "does not keep unneeded platforms for gems that are used" do
+ build_repo4 do
+ build_gem "empyrean", "0.1.0"
+ build_gem "coderay", "1.1.2"
+ build_gem "method_source", "0.9.0"
+ build_gem("spoon", "0.0.6") {|s| s.add_runtime_dependency "ffi" }
+ build_gem "pry", "0.11.3" do |s|
+ s.platform = "java"
+ s.add_runtime_dependency "coderay", "~> 1.1.0"
+ s.add_runtime_dependency "method_source", "~> 0.9.0"
+ s.add_runtime_dependency "spoon", "~> 0.0"
+ end
+ build_gem "pry", "0.11.3" do |s|
+ s.add_runtime_dependency "coderay", "~> 1.1.0"
+ s.add_runtime_dependency "method_source", "~> 0.9.0"
+ end
+ build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
+ build_gem("ffi", "1.9.23")
+ end
+
+ simulate_platform java
+
+ install_gemfile! <<-G
+ source "file://localhost/#{gem_repo4}"
+
+ gem "empyrean", "0.1.0"
+ gem "pry"
+ G
+
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "lock --add-platform ruby"
+
+ good_lockfile = strip_whitespace(<<-L)
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ bad_lockfile = strip_whitespace <<-L
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ aggregate_failures do
+ lockfile bad_lockfile
+ bundle! :install
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! :update, :all => true
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! "update ffi"
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! "update empyrean"
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! :lock
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+ end
+ end
+
+ it "works the other way with gems that have different dependencies" do
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ simulate_platform "java"
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+ end
+
+ it "works with gems that have extra platform-specific runtime dependencies", :bundler => "< 2" do
+ simulate_platform x64_mac
+
+ update_repo2 do
+ build_gem "facter", "2.4.6"
+ build_gem "facter", "2.4.6" do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem "CFPropertyList"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+
+ gem "facter"
+ G
+
+ expect(out).to include "Unable to use the platform-specific (universal-darwin) version of facter (2.4.6) " \
+ "because it has different dependencies from the ruby version. " \
+ "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
+
+ expect(the_bundle).to include_gem "facter 2.4.6"
+ expect(the_bundle).not_to include_gem "CFPropertyList"
+ end
+
+ it "fetches gems again after changing the version of Ruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ new_version = Gem::ConfigMap[:ruby_version] == "1.8" ? "1.9.1" : "1.8"
+ FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, new_version))
+
+ bundle! :install
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ end
+end
+
+RSpec.describe "bundle install with platform conditionals" do
+ it "installs gems tagged w/ the current platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ platforms :#{not_local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platforms => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "nokogiri", :platforms => :#{not_local_tag}
+ G
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "doesn't install gems tagged w/ another platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{not_local_tag}
+ G
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not blow up on sources with all platform-excluded specs" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ platform :#{not_local_tag} do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+
+ bundle :list
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "does not attempt to install gems from :rbx when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", :platform => :rbx
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "does not attempt to install gems from other rubies when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+ other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", platform: :#{other_ruby_version_tag}
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "prints a helpful warning when a dependency is unused on any platform" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ G
+
+ bundle! "install"
+
+ expect(out).to include <<-O.strip
+The dependency #{Gem::Dependency.new("rack", ">= 0")} will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
+ O
+ end
+
+ context "when disable_platform_warnings is true" do
+ before { bundle! "config disable_platform_warnings true" }
+
+ it "does not print the warning when a dependency is unused on any platform" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ G
+
+ bundle! "install"
+
+ expect(out).not_to match(/The dependency (.*) will be unused/)
+ end
+ end
+end
+
+RSpec.describe "when a gem has no architecture" do
+ it "still installs correctly" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ # Try to install gem with nil arch
+ source "http://localgemserver.test/"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+end
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
new file mode 100644
index 0000000000..24fe021fa3
--- /dev/null
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+RSpec.describe "ruby requirement" do
+ def locked_ruby_version
+ Bundler::RubyVersion.from_string(Bundler::LockfileParser.new(lockfile).ruby_version)
+ end
+
+ # As discovered by https://github.com/bundler/bundler/issues/4147, there is
+ # no test coverage to ensure that adding a gem is possible with a ruby
+ # requirement. This test verifies the fix, committed in bfbad5c5.
+ it "allows adding gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack-obama 1.0"
+ end
+
+ it "allows removing the ruby version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "~> #{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ expect(lockfile).to include("RUBY VERSION")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(lockfile).not_to include("RUBY VERSION")
+ end
+
+ it "allows changing the ruby version requirement to something compatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.1"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+ end
+
+ it "allows changing the ruby version requirement to something incompatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 5000.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version.versions).to eq(["5100"])
+ end
+
+ it "allows requirements with trailing whitespace" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}\\n \t\\n"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "fails gracefully with malformed requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 0", "-.\\0"
+ gem "rack"
+ G
+
+ expect(out).to include("There was an error parsing") # i.e. DSL error, not error template
+ end
+end
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
new file mode 100644
index 0000000000..c814d0de76
--- /dev/null
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -0,0 +1,619 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gems on multiple sources" do
+ # repo1 is built automatically before all of the specs run
+ # it contains rack-obama 1.0.0 and rack 0.9.1 & 1.0.0 amongst other gems
+
+ context "without source affinity" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", repo3_rack_version do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ context "with multiple toplevel sources" do
+ let(:repo3_rack_version) { "1.0.0" }
+
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo3}"
+ source "file://localhost#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack"
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+
+ it "errors when disable_multisource is set" do
+ bundle "config disable_multisource true"
+ bundle :install
+ expect(out).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4) if exitstatus
+ end
+ end
+
+ context "when different versions of the same gem are in multiple sources" do
+ let(:repo3_rack_version) { "1.2" }
+
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo3}"
+ source "file://localhost#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0" # force it to install the working version in repo1
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+ end
+ end
+
+ context "with source affinity" do
+ context "with sources given by a block" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}" do
+ gem "thin" # comes first to test name sorting
+ gem "rack"
+ end
+ gem "rack-obama" # shoud come from repo3!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle! :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0")
+ expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1")
+ end
+
+ it "can cache and deploy" do
+ bundle! :package
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "with sources set by an option" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack-obama" # should come from repo3!
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "when a pinned gem has an indirect dependency" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+ end
+
+ context "when the indirect dependency is in the pinned source" do
+ before do
+ # we need a working rack gem in repo3
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ context "and not in any other sources" do
+ before do
+ build_repo(gem_repo2) {}
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in another source" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ context "when lockfile_uses_separate_rubygems_sources is set" do
+ before do
+ bundle! "config lockfile_uses_separate_rubygems_sources true"
+ bundle! "config disable_multisource true"
+ end
+
+ it "installs from the same source without any warning" do
+ bundle! :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle! :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "when the indirect dependency is in a different source" do
+ before do
+ # In these tests, we need a working rack gem in repo2 and not repo3
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ context "and not in any other sources" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in yet another source" do
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}"
+ source "file://localhost#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source and warns about ambiguous gems", :bundler => "< 2" do
+ bundle "config major_deprecations true"
+ bundle :install
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo2}"))
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and only the dependency is pinned" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}" # contains depends_on_rack
+ source "file://#{gem_repo2}" # contains broken rack
+
+ gem "depends_on_rack" # installed from gem_repo3
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the dependency from the pinned source without warning", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+
+ # In https://github.com/bundler/bundler/issues/3585 this failed
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "when a top-level gem has an indirect dependency" do
+ context "when lockfile_uses_separate_rubygems_sources is set" do
+ before do
+ bundle! "config lockfile_uses_separate_rubygems_sources true"
+ bundle! "config disable_multisource true"
+ end
+
+ before do
+ build_repo gem_repo2 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ build_repo gem_repo3 do
+ build_gem "unrelated_gem", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "depends_on_rack"
+
+ source "file://#{gem_repo3}" do
+ gem "unrelated_gem"
+ end
+ G
+ end
+
+ context "and the dependency is only in the top-level source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ it "installs all gems without warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ end
+ end
+
+ context "and the dependency is only in a pinned source" do
+ before do
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ it "does not find the dependency" do
+ bundle :install
+ expect(out).to include("Could not find gem 'rack', which is required by gem 'depends_on_rack', in any of the relevant sources")
+ end
+ end
+
+ context "and the dependency is in both the top-level and a pinned source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ it "installs the dependency from the top-level source without warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "with a gem that is only found in the wrong source" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "not_in_repo1", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "not_in_repo1", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "does not install the gem" do
+ bundle :install
+ expect(out).to include("Could not find gem 'not_in_repo1'")
+ end
+ end
+
+ context "with an existing lockfile" do
+ before do
+ system_gems "rack-0.9.1", "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}
+ remote: file:#{gem_repo3}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack!
+ L
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo3}" do
+ gem 'rack'
+ end
+ G
+ end
+
+ # Reproduction of https://github.com/bundler/bundler/issues/3298
+ it "does not unlock the installed gem on exec" do
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+ end
+
+ context "with a path gem in the same Gemfile" do
+ before do
+ build_lib "foo"
+
+ gemfile <<-G
+ gem "rack", :source => "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "does not unlock the non-path gem after install" do
+ bundle! :install
+
+ bundle! %(exec ruby -e 'puts "OK"'), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(out).to include("OK")
+ end
+ end
+ end
+
+ context "when an older version of the same gem also ships with Ruby" do
+ before do
+ system_gems "rack-0.9.1"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack" # shoud come from repo1!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+ end
+
+ context "when a single source contains multiple locked gems" do
+ before do
+ # 1. With these gems,
+ build_repo4 do
+ build_gem "foo", "0.1"
+ build_gem "bar", "0.1"
+ end
+
+ # 2. Installing this gemfile will produce...
+ gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.1', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 3. this lockfile.
+ lockfile <<-L
+ GEM
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote1/
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote4/
+ specs:
+ bar (0.1)
+ foo (0.1)
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ bar (~> 0.1)!
+ foo (~> 0.1)!
+ rack
+ L
+
+ bundle! :install, forgotten_command_line_options(:path => "../gems/system")
+
+ # 4. Then we add some new versions...
+ update_repo4 do
+ build_gem "foo", "0.2"
+ build_gem "bar", "0.3"
+ end
+ end
+
+ it "allows them to be unlocked separately" do
+ # 5. and install this gemfile, updating only foo.
+ install_gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.2', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 6. Which should update foo to 0.2, but not the (locked) bar 0.1
+ expect(the_bundle).to include_gems("foo 0.2", "bar 0.1")
+ end
+ end
+
+ context "re-resolving" do
+ context "when there is a mix of sources in the gemfile" do
+ before do
+ build_repo3
+ build_lib "path1"
+ build_lib "path2"
+ build_git "git1"
+ build_git "git2"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ source "file://#{gem_repo3}" do
+ gem "rack"
+ end
+
+ gem "path1", :path => "#{lib_path("path1-1.0")}"
+ gem "path2", :path => "#{lib_path("path2-1.0")}"
+ gem "git1", :git => "#{lib_path("git1-1.0")}"
+ gem "git2", :git => "#{lib_path("git2-1.0")}"
+ G
+ end
+
+ it "does not re-resolve" do
+ bundle :install, :verbose => true
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies")
+ end
+ end
+ end
+
+ context "when a gem is installed to system gems" do
+ before do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "and the gemfile changes" do
+ it "is still able to find that gem from remote sources" do
+ source_uri = "file://#{gem_repo1}"
+ second_uri = "file://#{gem_repo4}"
+
+ build_repo4 do
+ build_gem "rack", "2.0.1.1.forked"
+ build_gem "thor", "0.19.1.1.forked"
+ end
+
+ # When this gemfile is installed...
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor"
+ end
+ gem "rack-obama"
+ G
+
+ # It creates this lockfile.
+ lockfile <<-L
+ GEM
+ remote: #{source_uri}/
+ remote: #{second_uri}/
+ specs:
+ rack (2.0.1.1.forked)
+ rack-obama (1.0)
+ rack
+ thor (0.19.1.1.forked)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 2.0.1.1.forked)!
+ rack-obama
+ thor!
+ L
+
+ # Then we change the Gemfile by adding a version to thor
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor", "0.19.1.1.forked"
+ end
+ gem "rack-obama"
+ G
+
+ # But we should still be able to find rack 2.0.1.1.forked and install it
+ bundle! :install
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
new file mode 100644
index 0000000000..9c725416d5
--- /dev/null
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with specific_platform enabled" do
+ before do
+ bundle "config specific_platform true"
+
+ build_repo2 do
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.4.0")
+ build_gem("google-protobuf", "3.0.0.alpha.3.1.pre")
+ build_gem("google-protobuf", "3.0.0.alpha.3")
+ build_gem("google-protobuf", "3.0.0.alpha.2.0")
+ build_gem("google-protobuf", "3.0.0.alpha.1.1")
+ build_gem("google-protobuf", "3.0.0.alpha.1.0")
+
+ build_gem("facter", "2.4.6")
+ build_gem("facter", "2.4.6") do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem("CFPropertyList")
+ end
+ end
+
+ let(:google_protobuf) { <<-G }
+ source "file:#{gem_repo2}"
+ gem "google-protobuf"
+ G
+
+ context "when on a darwin machine" do
+ before { simulate_platform "x86_64-darwin-15" }
+
+ it "locks to both the specific darwin platform and ruby" do
+ install_gemfile!(google_protobuf)
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ])
+ end
+
+ it "caches both the universal-darwin and ruby gems when --all-platforms is passed" do
+ gemfile(google_protobuf)
+ bundle! "package --all-platforms"
+ expect([cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1"), cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")]).
+ to all(exist)
+ end
+
+ it "uses the platform-specific gem with extra dependencies" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo2}"
+ gem "facter"
+ G
+
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
+ "facter-2.4.6",
+ "facter-2.4.6-universal-darwin"])
+ end
+
+ context "when adding a platform via lock --add_platform" do
+ it "adds the foreign platform" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{x64_mingw}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([rb, x64_mingw, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
+ ])
+ end
+
+ it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{java}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([java, rb, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ])
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
new file mode 100644
index 0000000000..e74c5ffe59
--- /dev/null
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -0,0 +1,145 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "with duplicated gems" do
+ it "will display a warning" do
+ install_gemfile <<-G
+ gem 'rails', '~> 4.0.0'
+ gem 'rails', '~> 4.0.0'
+ G
+ expect(out).to include("more than once")
+ end
+ end
+
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :gemfile => bundled_app("NotGemfile")
+
+ # Specify BUNDLE_GEMFILE for `the_bundle`
+ # to retrieve the proper Gemfile
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with gemfile set via config" do
+ before do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ end
+ it "uses the gemfile to install" do
+ bundle "install"
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ it "uses the gemfile while in a subdirectory" do
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ bundle "install"
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+ end
+
+ context "with deprecated features" do
+ before :each do
+ in_app_root
+ end
+
+ it "reports that lib is an invalid option" do
+ gemfile <<-G
+ gem "rack", :lib => "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
+ end
+ end
+
+ context "with prefer_gems_rb set" do
+ before { bundle! "config prefer_gems_rb true" }
+
+ it "prefers gems.rb to Gemfile" do
+ create_file("gems.rb", "gem 'bundler'")
+ create_file("Gemfile", "raise 'wrong Gemfile!'")
+
+ bundle! :install
+
+ expect(bundled_app("gems.rb")).to be_file
+ expect(bundled_app("Gemfile.lock")).not_to be_file
+
+ expect(the_bundle).to include_gem "bundler #{Bundler::VERSION}"
+ end
+ end
+
+ context "with engine specified in symbol" do
+ it "does not raise any error parsing Gemfile" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ G
+
+ expect(out).to match(/Bundle complete!/)
+ end
+ end
+ end
+
+ it "installation succeeds" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "with a Gemfile containing non-US-ASCII characters" do
+ it "reads the Gemfile with the UTF-8 encoding by default" do
+ skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9"
+
+ install_gemfile <<-G
+ str = "Il était une fois ..."
+ puts "The source encoding is: " + str.encoding.name
+ G
+
+ expect(out).to include("The source encoding is: UTF-8")
+ expect(out).not_to include("The source encoding is: ASCII-8BIT")
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "respects the magic encoding comment" do
+ skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9"
+
+ # NOTE: This works thanks to #eval interpreting the magic encoding comment
+ install_gemfile <<-G
+ # encoding: iso-8859-1
+ str = "Il #{"\xE9".dup.force_encoding("binary")}tait une fois ..."
+ puts "The source encoding is: " + str.encoding.name
+ G
+
+ expect(out).to include("The source encoding is: ISO-8859-1")
+ expect(out).to include("Bundle complete!")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
new file mode 100644
index 0000000000..02a37a77d5
--- /dev/null
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -0,0 +1,940 @@
+# frozen_string_literal: true
+
+RSpec.describe "compact index api" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "compact_index"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle case sensitivity conflicts" do
+ build_repo4 do
+ build_gem "rack", "1.0" do |s|
+ s.add_runtime_dependency("Rack", "0.1")
+ end
+ build_gem "Rack", "0.1"
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4 }
+ source "#{source_uri}"
+ gem "rack", "1.0"
+ gem "Rack", "0.1"
+ G
+
+ # can't use `include_gems` here since the `require` will conflict on a
+ # case-insensitive FS
+ run! "Bundler.require; puts Gem.loaded_specs.values_at('rack', 'Rack').map(&:full_name)"
+ expect(last_command.stdout).to eq("rack-1.0\nRack-0.1")
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle! :install, :artifice => "compact_index"
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle").merge(:artifice => "compact_index")
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ bundle "install --deployment", :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "compact_index"
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "compact_index")
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle! :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_forbidden"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the versions endpoint has a checksum mismatch" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_checksum_mismatch"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(out).to include <<-'WARN'
+The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\"").
+ WARN
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the user's home directory does not exist or is not writable" do
+ ENV["HOME"] = tmp("missing_home").to_s
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle! :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "times out when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "compact_index_redirects"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "compact_index"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! "update --full-index", :artifice => "compact_index", :all => bundle_update_requires_all?
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "does not double check for gems that are only installed locally" do
+ system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0]
+ bundle! "config --local path.system true"
+ ENV["BUNDLER_SPEC_ALL_REQUESTS"] = strip_whitespace(<<-EOS).strip
+ #{source_uri}/versions
+ #{source_uri}/info/rack
+ EOS
+
+ install_gemfile! <<-G, :artifice => "compact_index", :verbose => true
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(last_command.stdboth).not_to include "Double checking"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources with source blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index_extra", :verbose => true
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources when using blocks", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
+ expect(out).to include("Fetching source index from http://localgemserver.test/extra")
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps & everything is the compact index" do
+ build_repo4 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index_extra_api_missing"
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ expect(the_bundle).to include_gem "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle! :install, :artifice => "compact_index_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ bundle "install --deployment", :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment with blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ bundle "install --deployment", :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ end
+
+ it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
+ sudo "mkdir -p #{system_gem_path("bin")}"
+ sudo "chown -R root #{system_gem_path("bin")}"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle! :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs the binstubs", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "compact_index"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl" do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when RubyGems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle! :install, :artifice => "compact_index_forbidden"
+ end
+ end
+
+ it "performs partial update with a non-empty range" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ # Initial install creates the cached versions file
+ bundle! :install, :artifice => "compact_index"
+
+ # Update the Gemfile so we can check subsequent install was successful
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ # Second install should make only a partial request to /versions
+ bundle! :install, :artifice => "compact_index_partial_update"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs partial update while local cache is updated by another process" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ # Create an empty file to trigger a partial download
+ versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
+ FileUtils.mkdir_p(File.dirname(versions))
+ FileUtils.touch(versions)
+
+ bundle! :install, :artifice => "compact_index_concurrent_download"
+
+ expect(File.read(versions)).to start_with("created_at")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs full update of compact index info cache if range is not satisfiable" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack")
+
+ bundle! :install, :artifice => "compact_index"
+
+ expected_rack_info_content = File.read(rake_info_path)
+
+ # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install
+ File.open(rake_info_path, "w") {|f| f << (expected_rack_info_content + "this is different") }
+
+ # Update the Gemfile so the next install does its normal things
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ # The cache files now being longer means the requested range is going to be not satisfiable
+ # Bundler must end up requesting the whole file to fix things up.
+ bundle! :install, :artifice => "compact_index_range_not_satisfiable"
+
+ resulting_rack_info_content = File.read(rake_info_path)
+
+ expect(resulting_rack_info_content).to eq(expected_rack_info_content)
+ end
+
+ it "fails gracefully when the source URI has an invalid scheme" do
+ install_gemfile <<-G
+ source "htps://rubygems.org"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to end_with(<<-E.strip)
+ The request uri `htps://index.rubygems.org/versions` has an invalid scheme (`htps`). Did you mean `http` or `https`?
+ E
+ end
+
+ describe "checksum validation", :rubygems => ">= 2.3.0" do
+ it "raises when the checksum does not match" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(exitstatus).to eq(19) if exitstatus
+ expect(out).
+ to include("Bundler cannot continue installing rack (1.0.0).").
+ and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server.").
+ and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue.").
+ and include("To resolve this issue:").
+ and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`").
+ and include("2. run `bundle install`").
+ and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:").
+ and include("1. run `bundle config disable_checksum_validation true` to turn off checksum verification").
+ and include("2. run `bundle install`").
+ and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/)
+ end
+
+ it "raises when the checksum is the wrong length" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!" }
+ source "#{source_uri}"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(5) if exitstatus
+ expect(out).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ end
+
+ it "does not raise when disable_checksum_validation is set" do
+ bundle! "config disable_checksum_validation true"
+ install_gemfile! <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+ end
+
+ it "works when cache dir is world-writable" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ File.umask(0000)
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "doesn't explode when the API dependencies are wrong" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }
+ source "#{source_uri}"
+ gem "rails"
+ G
+ deps = [Gem::Dependency.new("rake", "= 10.0.2"),
+ Gem::Dependency.new("actionpack", "= 2.3.2"),
+ Gem::Dependency.new("activerecord", "= 2.3.2"),
+ Gem::Dependency.new("actionmailer", "= 2.3.2"),
+ Gem::Dependency.new("activeresource", "= 2.3.2")]
+ expect(out).to include(<<-E.strip).and include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
+Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}).
+Either installing with `--full-index` or running `bundle update rails` should fix the problem.
+ E
+ end
+
+ it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source_uri}" do
+ gem "rails"
+ gem "activemerchant"
+ end
+ G
+ gem_command! :uninstall, "activemerchant"
+ bundle! "update rails", :artifice => "compact_index"
+ expect(lockfile.scan(/activemerchant \(/).size).to eq(1)
+ end
+end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
new file mode 100644
index 0000000000..2ffe4b62d7
--- /dev/null
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -0,0 +1,760 @@
+# frozen_string_literal: true
+
+RSpec.describe "gemcutter's dependency API" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}/...")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle :install, :artifice => "endpoint"
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle").merge(:artifice => "endpoint")
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ bundle "install --deployment", :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "endpoint"
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "endpoint")
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when hitting the Gemcutter Dependency Limit" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "activesupport"
+ gem "actionpack"
+ gem "actionmailer"
+ gem "activeresource"
+ gem "thin"
+ gem "rack"
+ gem "rails"
+ G
+ bundle :install, :artifice => "endpoint_fallback"
+ expect(out).to include("Fetching source index from #{source_uri}")
+
+ expect(the_bundle).to include_gems(
+ "activesupport 2.3.2",
+ "actionpack 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2",
+ "thin 1.0.0",
+ "rack 1.0.0",
+ "rails 2.3.2"
+ )
+ end
+
+ it "falls back when Gemcutter API doesn't return proper Marshal format" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
+ expect(out).to include("could not fetch from the dependency API, trying the full index")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "timeouts when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_redirect"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "endpoint"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! "update --full-index", :artifice => "endpoint", :all => bundle_update_requires_all?
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle! :install, :artifice => "endpoint_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources using blocks" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/.")
+ expect(out).to include("Fetching source index from http://localgemserver.test/extra")
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle :install, :artifice => "endpoint_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ bundle "install --deployment", :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment with blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ bundle "install --deployment", :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ end
+
+ it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
+ sudo "mkdir -p #{system_gem_path("bin")}"
+ sudo "chown -R root #{system_gem_path("bin")}"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle :install, :artifice => "endpoint"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs the binstubs", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "endpoint"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl" do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when RubyGems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle "install", :artifice => "endpoint_marshal_fail"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/env_spec.rb b/spec/bundler/install/gems/env_spec.rb
new file mode 100644
index 0000000000..0dccbbfd24
--- /dev/null
+++ b/spec/bundler/install/gems/env_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with ENV conditionals" do
+ describe "when just setting an ENV key as a string" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when just setting an ENV key as a symbol" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env :BUNDLER_TEST do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a string to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => "foo" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foo"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a regex to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => /foo/ do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "fo"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foobar"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
new file mode 100644
index 0000000000..37d2e4958a
--- /dev/null
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -0,0 +1,351 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle flex_install" do
+ it "installs the gems as expected" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "installs even when the lockfile is invalid" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', '1.0'
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "keeps child dependencies at the same version" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "1.0"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+ end
+
+ describe "adding new gems" do
+ it "installs added gems without updating previously installed gems" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ it "keeps child dependencies pinned" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ gem "thin"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0", "thin 1.0"
+ end
+ end
+
+ describe "removing gems" do
+ it "removes gems without changing the versions of remaining gems" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.2'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
+ end
+
+ it "removes top level dependencies when removed from the Gemfile while leaving other dependencies intact" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "removes child dependencies" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack-obama'
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0", "activesupport 2.3.5"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(the_bundle).not_to include_gems "rack-obama", "rack"
+ end
+ end
+
+ describe "when Gemfile conflicts with lockfile" do
+ before(:each) do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "rack_middleware 1.0", "rack 0.9.1"
+
+ build_repo2
+ update_repo2 do
+ build_gem "rack-obama", "2.0" do |s|
+ s.add_dependency "rack", "=1.2"
+ end
+ build_gem "rack_middleware", "2.0" do |s|
+ s.add_dependency "rack", ">=1.0"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "2.0"
+ gem "rack_middleware"
+ G
+ end
+
+ it "does not install gems whose dependencies are not met" do
+ bundle :install
+ ruby <<-RUBY
+ require 'bundler/setup'
+ RUBY
+ expect(err).to match(/could not find gem 'rack-obama/i)
+ end
+
+ it "suggests bundle update when the Gemfile requires different versions than the lock" do
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "rack":
+ In snapshot (Gemfile.lock):
+ rack (= 0.9.1)
+
+ In Gemfile:
+ rack-obama (= 2.0) was resolved to 2.0, which depends on
+ rack (= 1.2)
+
+ rack_middleware was resolved to 1.0, which depends on
+ rack (= 0.9.1)
+
+ Running `bundle update` will rebuild your snapshot from scratch, using only
+ the gems in your Gemfile, which may resolve the conflict.
+ E
+
+ bundle :install, :retry => 0
+ expect(last_command.bundler_err).to end_with(nice_error)
+ end
+ end
+
+ describe "subtler cases" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "rack-obama"
+ G
+ end
+
+ it "does something" do
+ expect do
+ bundle "install"
+ end.not_to change { File.read(bundled_app("Gemfile.lock")) }
+
+ expect(out).to include("rack = 0.9.1")
+ expect(out).to include("locked at 1.0.0")
+ expect(out).to include("bundle update rack")
+ end
+
+ it "should work when you update" do
+ bundle "update rack"
+ end
+ end
+
+ describe "when adding a new source" do
+ it "updates the lockfile", :bundler => "< 2" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "updates the lockfile", :bundler => "2" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}" do
+ end
+ gem "rack"
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ # This was written to test github issue #636
+ describe "when a locked child dependency conflicts" do
+ before(:each) do
+ build_repo2 do
+ build_gem "capybara", "0.3.9" do |s|
+ s.add_dependency "rack", ">= 1.0.0"
+ end
+
+ build_gem "rack", "1.1.0"
+ build_gem "rails", "3.0.0.rc4" do |s|
+ s.add_dependency "rack", "~> 1.1.0"
+ end
+
+ build_gem "rack", "1.2.1"
+ build_gem "rails", "3.0.0" do |s|
+ s.add_dependency "rack", "~> 1.2.1"
+ end
+ end
+ end
+
+ it "prints the correct error message" do
+ # install Rails 3.0.0.rc
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0.rc4"
+ gem "capybara", "0.3.9"
+ G
+
+ # upgrade Rails to 3.0.0 and then install again
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0"
+ gem "capybara", "0.3.9"
+ G
+
+ expect(out).to include("Gemfile.lock")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_spec.rb b/spec/bundler/install/gems/mirror_spec.rb
new file mode 100644
index 0000000000..4c35b8f206
--- /dev/null
+++ b/spec/bundler/install/gems/mirror_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with a mirror configured" do
+ describe "when the mirror does not match the gem source" do
+ before :each do
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ end
+
+ it "installs from the normal location" do
+ bundle :install
+ expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when the gem source matches a configured mirror" do
+ before :each do
+ gemfile <<-G
+ # This source is bogus and doesn't have the gem we're looking for
+ source "file://localhost#{gem_repo2}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.file://localhost#{gem_repo2} file://localhost#{gem_repo1}"
+ end
+
+ it "installs the gem from the mirror" do
+ bundle :install
+ expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}"))
+ expect(out).not_to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo2}"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
new file mode 100644
index 0000000000..ea616f60d3
--- /dev/null
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+RSpec.describe "installing a gem with native extensions", :ruby_repo do
+ it "installs" do
+ build_repo2 do
+ build_gem "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "c_extension"
+ G
+
+ bundle "config build.c_extension --with-c_extension=hello"
+ bundle "install"
+
+ expect(out).not_to include("extconf.rb failed")
+ expect(out).to include("Installing c_extension 1.0 with native extensions")
+
+ run "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+
+ it "installs from git" do
+ build_git "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+
+ bundle! "config build.c_extension --with-c_extension=hello"
+
+ install_gemfile! <<-G
+ gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
+ G
+
+ expect(out).not_to include("extconf.rb failed")
+
+ run! "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+end
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
new file mode 100644
index 0000000000..c6e348fb65
--- /dev/null
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "with gem sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ gem 'rack-obama'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from thin:")
+ expect(out).to include("Thin's post install message")
+ expect(out).to include("Post-install message from rack-obama:")
+ expect(out).to include("Rack-obama's post install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when a dependecy includes a post install message" do
+ it "should display the post install message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+ end
+ end
+
+ context "with git sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+ end
+
+ it "should display the post-install messages if repo is updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle :install
+
+ build_git "foo", "1.1" do |s|
+ s.post_install_message = "Foo's 1.1 post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.1")}'
+ G
+ bundle :install
+
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's 1.1 post install message")
+ end
+
+ it "should not display the post-install messages if repo is not updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ build_git "foo" do |s|
+ s.post_install_message = nil
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+ end
+
+ context "when ignore post-install messages for gem is set" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages.rack true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when ignore post-install messages for all gems" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
new file mode 100644
index 0000000000..01c03ac793
--- /dev/null
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with install-time dependencies" do
+ it "installs gems with implicit rake dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_implicit_rake_dep"
+ gem "another_implicit_rake_dep"
+ gem "rake"
+ G
+
+ run <<-R
+ require 'implicit_rake_dep'
+ require 'another_implicit_rake_dep'
+ puts IMPLICIT_RAKE_DEP
+ puts ANOTHER_IMPLICIT_RAKE_DEP
+ R
+ expect(out).to eq("YES\nYES")
+ end
+
+ it "installs gems with a dependency with no type" do
+ build_repo2
+
+ path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.read(path)))
+ spec.dependencies.each do |d|
+ d.instance_variable_set(:@type, :fail)
+ end
+ File.open(path, "w") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "actionpack", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "actionpack 2.3.2", "activesupport 2.3.2"
+ end
+
+ describe "with crazy rubygem plugin stuff" do
+ it "installs plugins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_b"
+ G
+
+ expect(the_bundle).to include_gems "net_b 1.0"
+ end
+
+ it "installs plugins depended on by other plugins", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_a"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0"
+ end
+
+ it "installs multiple levels of dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0", "net_c 1.0", "net_d 1.0", "net_e 1.0"
+ end
+
+ context "with ENV['DEBUG_RESOLVER'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER" => "1" }
+
+ expect(err).to include("Creating possibility state for net_c")
+ end
+ end
+
+ context "with ENV['DEBUG_RESOLVER_TREE'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1" }
+
+ expect(err).to include(" net_b").
+ and include("Starting resolution").
+ and include("Finished resolution").
+ and include("Attempting to activate")
+ end
+ end
+ end
+
+ describe "when a required ruby version" do
+ context "allows only an older version" do
+ it "installs the older version" do
+ build_repo2 do
+ build_gem "rack", "9001.0.0" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ ruby "#{RUBY_VERSION}"
+ source "http://localgemserver.test/"
+ gem 'rack'
+ G
+
+ expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("rack 1.2")
+ end
+ end
+
+ context "allows no gems" do
+ before do
+ build_repo2 do
+ build_gem "require_ruby" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+ end
+
+ let(:ruby_requirement) { %("#{RUBY_VERSION}") }
+ let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
+
+ shared_examples_for "ruby version conflicts" do
+ it "raises an error during resolution" do
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ source "http://localgemserver.test/"
+ ruby #{ruby_requirement}
+ gem 'require_ruby'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
+
+ nice_error = strip_whitespace(<<-E).strip
+ Bundler could not find compatible versions for gem "ruby\0":
+ In Gemfile:
+ ruby\0 (#{error_message_requirement})
+
+ require_ruby was resolved to 1.0, which depends on
+ ruby\0 (> 9000)
+
+ Could not find gem 'ruby\0 (> 9000)', which is required by gem 'require_ruby', in any of the relevant sources:
+ the local ruby installation
+ E
+ expect(last_command.bundler_err).to end_with(nice_error)
+ end
+ end
+
+ it_behaves_like "ruby version conflicts"
+
+ describe "with a < requirement" do
+ let(:ruby_requirement) { %("< 5000") }
+ let(:error_message_requirement) { "< 5000" }
+
+ it_behaves_like "ruby version conflicts"
+ end
+
+ describe "with a compound requirement" do
+ let(:reqs) { ["> 0.1", "< 5000"] }
+ let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
+ let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s }
+
+ it_behaves_like "ruby version conflicts"
+ end
+ end
+ end
+
+ describe "when a required rubygems version disallows a gem" do
+ it "does not try to install those gems" do
+ build_repo2 do
+ build_gem "require_rubygems" do |s|
+ s.required_rubygems_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'require_rubygems'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
+ expect(out).to include("require_rubygems-1.0 requires rubygems version > 9000, which is incompatible with the current version, #{Gem::VERSION}")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
new file mode 100644
index 0000000000..10ce589eef
--- /dev/null
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -0,0 +1,337 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples "bundle install --standalone" do
+ shared_examples "common functionality" do
+ it "still makes the gems available to normal bundler" do
+ args = expected_gems.map {|k, v| "#{k} #{v}" }
+ expect(the_bundle).to include_gems(*args)
+ end
+
+ it "generates a bundle/bundler/setup.rb" do
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
+ end
+
+ it "makes the gems available without bundler" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir(bundled_app) do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "works on a different system" do
+ FileUtils.mv(bundled_app, "#{bundled_app}2")
+
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir("#{bundled_app}2") do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+ end
+
+ describe "with simple gems" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with gems with native extension", :ruby_repo do
+ before do
+ install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+ end
+
+ it "generates a bundle/bundler/setup.rb with the proper paths", :rubygems => "2.4" do
+ expected_path = bundled_app("bundle/bundler/setup.rb")
+ extension_line = File.read(expected_path).each_line.find {|line| line.include? "/extensions/" }.strip
+ expect(extension_line).to start_with '$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/'
+ expect(extension_line).to end_with '/very_simple_binary-1.0"'
+ end
+ end
+
+ describe "with gem that has an invalid gemspec" do
+ before do
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ lib = File.expand_path('../lib/', __FILE__)
+ $:.unshift lib unless $:.include?(lib)
+ require 'bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'Anonymous'
+ s.require_path = [1,2]
+ end
+ G
+ end
+ install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+ end
+
+ it "outputs a helpful error message" do
+ expect(out).to include("You have one or more invalid gemspecs that need to be fixed.")
+ expect(out).to include("bar 1.0 has an invalid gemspec")
+ end
+ end
+
+ describe "with a combination of gems and git repos" do
+ before do
+ build_git "devise", "1.0"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "devise", :git => "#{lib_path("devise-1.0")}"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "devise" => "1.0",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with groups" do
+ before do
+ build_git "devise", "1.0"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ group :test do
+ gem "rspec"
+ gem "rack-test"
+ end
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "allows creating a standalone file with limited groups" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => "default")
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --without to limit the groups used in a standalone" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle"), :without => "test").merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --path to change the location of the standalone bundle", :bundler => "< 2" do
+ bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("path/to/bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ end
+
+ it "allows --path to change the location of the standalone bundle", :bundler => "2" do
+ bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true)
+ path = File.expand_path("path/to/bundle")
+
+ Dir.chdir(bundled_app) do
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path(#{path.dump})
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ end
+
+ it "allows remembered --without to limit the groups used in a standalone" do
+ bundle! :install, forgotten_command_line_options(:without => "test")
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+ end
+
+ describe "with gemcutter's dependency API" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ describe "simple gems" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :artifice => "endpoint")
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+ end
+
+ describe "with --binstubs", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :binstubs => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "creates stubs that use the standalone load path" do
+ Dir.chdir(bundled_app) do
+ expect(`bin/rails -v`.chomp).to eql "2.3.2"
+ end
+ end
+
+ it "creates stubs that can be executed from anywhere" do
+ require "tmpdir"
+ Dir.chdir(Dir.tmpdir) do
+ sys_exec!(%(#{bundled_app("bin/rails")} -v))
+ expect(out).to eq("2.3.2")
+ end
+ end
+
+ it "creates stubs that can be symlinked" do
+ pending "File.symlink is unsupported on Windows" if Bundler::WINDOWS
+
+ symlink_dir = tmp("symlink")
+ FileUtils.mkdir_p(symlink_dir)
+ symlink = File.join(symlink_dir, "rails")
+
+ File.symlink(bundled_app("bin/rails"), symlink)
+ sys_exec!("#{symlink} -v")
+ expect(out).to eq("2.3.2")
+ end
+
+ it "creates stubs with the correct load path" do
+ extension_line = File.read(bundled_app("bin/rails")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(extension_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ end
+ end
+end
+
+RSpec.describe "bundle install --standalone" do
+ include_examples("bundle install --standalone")
+end
+
+RSpec.describe "bundle install --standalone run in a subdirectory" do
+ before do
+ Dir.chdir(bundled_app("bob").tap(&:mkpath))
+ end
+
+ include_examples("bundle install --standalone")
+end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
new file mode 100644
index 0000000000..1781451c98
--- /dev/null
+++ b/spec/bundler/install/gems/sudo_spec.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+RSpec.describe "when using sudo", :sudo => true do
+ describe "and BUNDLE_PATH is writable" do
+ context "but BUNDLE_PATH/build_info is not writable" do
+ before do
+ bundle! "config path.system true"
+ subdir = system_gem_path("cache")
+ subdir.mkpath
+ sudo "chmod u-w #{subdir}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(out).to_not match(/an error occurred/i)
+ expect(system_gem_path("cache/rack-1.0.0.gem")).to exist
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+ end
+
+ describe "and GEM_HOME is owned by root" do
+ before :each do
+ bundle! "config path.system true"
+ chown_system_gems_to_root
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ gem "thin"
+ G
+
+ expect(system_gem_path("gems/rack-1.0.0")).to exist
+ expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs rake and a gem dependent on rake in the same session" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "another_implicit_rake_dep"
+ G
+ bundle "install"
+ expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist
+ end
+
+ it "installs when BUNDLE_PATH is owned by root" do
+ bundle! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes
+
+ bundle_path = tmp("owned_by_root")
+ FileUtils.mkdir_p bundle_path
+ sudo "chown -R root #{bundle_path}"
+
+ ENV["BUNDLE_PATH"] = bundle_path.to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs when BUNDLE_PATH does not exist" do
+ bundle! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes
+
+ root_path = tmp("owned_by_root")
+ FileUtils.mkdir_p root_path
+ sudo "chown -R root #{root_path}"
+ bundle_path = root_path.join("does_not_exist")
+
+ ENV["BUNDLE_PATH"] = bundle_path.to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs extensions/ compiled by RubyGems 2.2", :rubygems => "2.2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+
+ expect(system_gem_path("gems/very_simple_binary-1.0")).to exist
+ binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0")
+ expect(Dir.glob(binary_glob).first).to be
+ end
+ end
+
+ describe "and BUNDLE_PATH is not writable" do
+ before do
+ sudo "chmod ugo-w #{default_bundle_path}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(default_bundle_path("gems/rack-1.0.0")).to exist
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "cleans up the tmpdirs generated" do
+ require "tmpdir"
+ Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir|
+ FileUtils.remove_entry_secure(tmpdir)
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*")
+
+ expect(tmpdirs).to be_empty
+ end
+ end
+
+ describe "and GEM_HOME is not writable" do
+ it "installs" do
+ bundle! "config path.system true"
+ gem_home = tmp("sudo_gem_home")
+ sudo "mkdir -p #{gem_home}"
+ sudo "chmod ugo-w #{gem_home}"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
+ expect(gem_home.join("bin/rackup")).to exist
+ expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
+ end
+ end
+
+ describe "and root runs install" do
+ let(:warning) { "Don't run Bundler as root." }
+
+ before do
+ gemfile %(source "file://#{gem_repo1}")
+ end
+
+ it "warns against that" do
+ bundle :install, :sudo => true
+ expect(out).to include(warning)
+ end
+
+ context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do
+ it "skips the warning" do
+ bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => true }
+ expect(out).to_not include(warning)
+ end
+ end
+
+ context "when silence_root_warning = false" do
+ it "warns against that" do
+ bundle :install, :sudo => true, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" }
+ expect(out).to include(warning)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/win32_spec.rb b/spec/bundler/install/gems/win32_spec.rb
new file mode 100644
index 0000000000..ad758b94fa
--- /dev/null
+++ b/spec/bundler/install/gems/win32_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with win32-generated lockfile" do
+ it "should read lockfile" do
+ File.open(bundled_app("Gemfile.lock"), "wb") do |f|
+ f << "GEM\r\n"
+ f << " remote: file:#{gem_repo1}/\r\n"
+ f << " specs:\r\n"
+ f << "\r\n"
+ f << " rack (1.0.0)\r\n"
+ f << "\r\n"
+ f << "PLATFORMS\r\n"
+ f << " ruby\r\n"
+ f << "\r\n"
+ f << "DEPENDENCIES\r\n"
+ f << " rack\r\n"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
new file mode 100644
index 0000000000..666707b295
--- /dev/null
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -0,0 +1,154 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "when a gem has a YAML gemspec" do
+ before :each do
+ build_repo2 do
+ build_gem "yaml_spec", :gemspec => :yaml
+ end
+ end
+
+ it "still installs correctly" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "yaml_spec"
+ G
+ bundle :install
+ expect(err).to lack_errors
+ end
+
+ it "still installs correctly when using path" do
+ build_lib "yaml_spec", :gemspec => :yaml
+
+ install_gemfile <<-G
+ gem 'yaml_spec', :path => "#{lib_path("yaml_spec-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+ end
+
+ it "should use gemspecs in the system cache when available" do
+ gemfile <<-G
+ source "http://localtestserver.gem"
+ gem 'rack'
+ G
+
+ FileUtils.mkdir_p "#{default_bundle_path}/specifications"
+ File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f|
+ spec = Gem::Specification.new do |s|
+ s.name = "rack"
+ s.version = "1.0.0"
+ s.add_runtime_dependency "activesupport", "2.3.2"
+ end
+ f.write spec.to_ruby
+ end
+ bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "does not hang when gemspec has incompatible encoding" do
+ create_file "foo.gemspec", <<-G
+ Gem::Specification.new do |gem|
+ gem.name = "pry-byebug"
+ gem.version = "3.4.2"
+ gem.author = "David Rodriguez"
+ gem.summary = "Good stuff"
+ end
+ G
+
+ install_gemfile <<-G, :env => { "LANG" => "C" }
+ gemspec
+ G
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "reads gemspecs respecting their encoding" do
+ skip "Unicode is not supported on Ruby 1.x without extra work" if RUBY_VERSION < "2.0"
+
+ create_file "version.rb", <<-RUBY
+ module Persistent💎
+ VERSION = "0.0.1"
+ end
+ RUBY
+
+ create_file "persistent-dmnd.gemspec", <<-G
+ require_relative "version"
+
+ Gem::Specification.new do |gem|
+ gem.name = "persistent-dmnd"
+ gem.version = Persistent💎::VERSION
+ gem.author = "Ivo Anjo"
+ gem.summary = "Unscratchable stuff"
+ end
+ G
+
+ install_gemfile <<-G
+ gemspec
+ G
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ context "when ruby version is specified in gemspec and gemfile" do
+ it "installs when patch level is not specified and the version matches" do
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "~> #{RUBY_VERSION}.0"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "installs when patch level is specified and the version still matches the current version",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fails and complains about patchlevel on patchlevel mismatch",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ patchlevel = RUBY_PATCHLEVEL.to_i + 1
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
+ gemspec
+ G
+
+ expect(out).to include("Ruby patchlevel")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+
+ it "fails and complains about version on version mismatch" do
+ version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version
+
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = version
+ end
+
+ install_gemfile <<-G
+ ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
+ gemspec
+ G
+
+ expect(out).to include("Ruby version")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
new file mode 100644
index 0000000000..6ae718c2a4
--- /dev/null
+++ b/spec/bundler/install/git_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "git sources" do
+ it "displays the revision hash of the gem repository", :bundler => "< 2" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ bundle! :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "displays the ref of the gem repository when using branch~num as a ref", :bundler => "< 2" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+ rev = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
+ rev2 = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
+
+ install_gemfile! <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :ref => "master~2"
+ G
+
+ bundle! :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master~2@#{rev})")
+ expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+
+ update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true
+
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{lib_path("foo")} (at master~2@#{rev2})")
+ expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "should allows git repos that are missing but not being installed" do
+ revision = build_git("foo").ref_for("HEAD")
+
+ gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :group => :development
+ G
+
+ lockfile <<-L
+ GIT
+ remote: file://#{lib_path("foo-1.0")}
+ revision: #{revision}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ L
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle", :without => "development")
+
+ expect(out).to include("Bundle complete!")
+ end
+ end
+end
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
new file mode 100644
index 0000000000..e41e7e0157
--- /dev/null
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -0,0 +1,235 @@
+# frozen_string_literal: true
+
+RSpec.describe "global gem caching" do
+ before { bundle! "config global_gem_cache true" }
+
+ describe "using the cross-application user cache" do
+ let(:source) { "http://localgemserver.test" }
+ let(:source2) { "http://gemserver.example.org" }
+
+ def source_global_cache(*segments)
+ home(".bundle", "cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
+ end
+
+ def source2_global_cache(*segments)
+ home(".bundle", "cache", "gems", "gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
+ end
+
+ it "caches gems into the global cache on download" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ end
+
+ it "uses globally cached gems if they exist" do
+ source_global_cache.mkpath
+ FileUtils.cp(gem_repo1("gems/rack-1.0.0.gem"), source_global_cache("rack-1.0.0.gem"))
+
+ install_gemfile! <<-G, :artifice => "compact_index_no_gem"
+ source "#{source}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "when the same gem from different sources is installed" do
+ it "should use the appropriate one from the global cache" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ G
+
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ # rack 1.0.0 is not installed and it is in the global cache
+
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ # rack 0.9.1 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source}"
+ gem "rack", "1.0.0"
+ G
+
+ bundle! :install, :artifice => "compact_index_no_gem"
+ # rack 1.0.0 is installed and rack 0.9.1 is not
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ FileUtils.rm_r(default_bundle_path)
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ bundle! :install, :artifice => "compact_index_no_gem"
+ # rack 0.9.1 is installed and rack 1.0.0 is not
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ end
+
+ it "should not install if the wrong source is provided" do
+ gemfile <<-G
+ source "#{source}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ # rack 1.0.0 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ # rack 0.9.1 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "1.0.0"
+ G
+
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ bundle :install, :artifice => "compact_index_no_gem"
+ expect(out).to include("Internal Server Error 500")
+ # rack 1.0.0 is not installed and rack 0.9.1 is not
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+
+ gemfile <<-G
+ source "#{source}"
+ gem "rack", "0.9.1"
+ G
+
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ bundle :install, :artifice => "compact_index_no_gem"
+ expect(out).to include("Internal Server Error 500")
+ # rack 0.9.1 is not installed and rack 1.0.0 is not
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "when installing gems from a different directory" do
+ it "uses the global cache as a source" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ # Both gems are installed and in the global cache
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+ FileUtils.rm_r(default_bundle_path)
+ # Both gems are now only in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+
+ install_gemfile! <<-G, :artifice => "compact_index_no_gem"
+ source "#{source}"
+ gem "rack"
+ G
+
+ # rack is installed and both are in the global cache
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+
+ Dir.chdir bundled_app2 do
+ create_file bundled_app2("gems.rb"), <<-G
+ source "#{source}"
+ gem "activesupport"
+ G
+
+ # Neither gem is installed and both are in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+
+ # Install using the global cache instead of by downloading the .gem
+ # from the server
+ bundle! :install, :artifice => "compact_index_no_gem"
+
+ # activesupport is installed and both are in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+ end
+ end
+ end
+ end
+
+ describe "extension caching", :ruby_repo, :rubygems => "2.2" do
+ it "works" do
+ build_git "very_simple_git_binary", &:add_c_extension
+ build_lib "very_simple_path_binary", &:add_c_extension
+ revision = revision_for(lib_path("very_simple_git_binary-1.0"))[0, 12]
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+
+ gem "very_simple_binary"
+ gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}"
+ gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}"
+ G
+
+ gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0")
+ git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ "very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0")
+
+ cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort
+ expect(cached_extensions).to eq [gem_binary_cache, git_binary_cache].sort
+
+ run! <<-R
+ require 'very_simple_binary_c'; puts ::VERY_SIMPLE_BINARY_IN_C
+ require 'very_simple_git_binary_c'; puts ::VERY_SIMPLE_GIT_BINARY_IN_C
+ R
+ expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C"
+
+ FileUtils.rm Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
+
+ gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
+ git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
+
+ bundle! "config --local path different_path"
+ bundle! :install
+
+ expect(Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]).to all(end_with(".rb"))
+
+ run! <<-R
+ require 'very_simple_binary_c'
+ require 'very_simple_git_binary_c'
+ R
+ expect(out).to eq "very_simple_binary_c.rb\nvery_simple_git_binary_c.rb"
+ end
+ end
+end
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
new file mode 100644
index 0000000000..44439c275e
--- /dev/null
+++ b/spec/bundler/install/path_spec.rb
@@ -0,0 +1,256 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "with --path" do
+ before :each do
+ build_gem "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "puts 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "does not use available system gems with bundle --path vendor/bundle", :bundler => "< 2" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles paths with regex characters in them" do
+ dir = bundled_app("bun++dle")
+ dir.mkpath
+
+ Dir.chdir(dir) do
+ bundle! :install, forgotten_command_line_options(:path => dir.join("vendor/bundle"))
+ expect(out).to include("installed into `./vendor/bundle`")
+ end
+
+ dir.rmtree
+ end
+
+ it "prints a warning to let the user know what has happened with bundle --path vendor/bundle" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(out).to include("gems are installed into `./vendor/bundle`")
+ end
+
+ it "disallows --path vendor/bundle --system", :bundler => "< 2" do
+ bundle "install --path vendor/bundle --system"
+ expect(out).to include("Please choose only one option.")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "remembers to disable system gems after the first time with bundle --path vendor/bundle", :bundler => "< 2" do
+ bundle "install --path vendor/bundle"
+ FileUtils.rm_rf bundled_app("vendor")
+ bundle "install"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with path_relative_to_cwd set to true" do
+ before { bundle! "config path_relative_to_cwd true" }
+
+ it "installs the bundle relatively to current working directory", :bundler => "< 2" do
+ Dir.chdir(bundled_app.parent) do
+ bundle! "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle"
+ expect(out).to include("installed into `./vendor/bundle`")
+ expect(bundled_app("../vendor/bundle")).to be_directory
+ end
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the standalone bundle relative to the cwd" do
+ Dir.chdir(bundled_app.parent) do
+ bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true
+ expect(out).to include("installed into `./bundled_app/bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
+ end
+
+ bundle! "config unset path"
+
+ Dir.chdir(bundled_app("subdir").tap(&:mkpath)) do
+ bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true
+ expect(out).to include("installed into `../bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
+ end
+ end
+ end
+ end
+
+ describe "when BUNDLE_PATH or the global path config is set" do
+ before :each do
+ build_lib "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ def set_bundle_path(type, location)
+ if type == :env
+ ENV["BUNDLE_PATH"] = location
+ elsif type == :global
+ bundle! "config path #{location}", "no-color" => nil
+ end
+ end
+
+ [:env, :global].each do |type|
+ context "when set via #{type}" do
+ it "installs gems to a path if one is specified" do
+ set_bundle_path(type, bundled_app("vendor2").to_s)
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(bundled_app("vendor2")).not_to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with global_path_appends_ruby_scope set", :bundler => "2" do
+ it "installs gems to ." do
+ set_bundle_path(type, ".")
+ bundle! "config --global disable_shared_gems true"
+
+ bundle! :install
+
+ paths_to_exist = %w[cache/rack-1.0.0.gem gems/rack-1.0.0 specifications/rack-1.0.0.gemspec].map {|path| bundled_app(Bundler.ruby_scope, path) }
+ expect(paths_to_exist).to all exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to the path" do
+ set_bundle_path(type, bundled_app("vendor").to_s)
+
+ bundle! :install
+
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to the path relative to root when relative" do
+ set_bundle_path(type, "vendor")
+
+ FileUtils.mkdir_p bundled_app("lol")
+ Dir.chdir(bundled_app("lol")) do
+ bundle! :install
+ end
+
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with global_path_appends_ruby_scope unset", :bundler => "< 2" do
+ it "installs gems to ." do
+ set_bundle_path(type, ".")
+ bundle! "config --global disable_shared_gems true"
+
+ bundle! :install
+
+ expect([bundled_app("cache/rack-1.0.0.gem"), bundled_app("gems/rack-1.0.0"), bundled_app("specifications/rack-1.0.0.gemspec")]).to all exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH with #{type}" do
+ set_bundle_path(type, bundled_app("vendor").to_s)
+
+ bundle :install
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH relative to root when relative" do
+ set_bundle_path(type, "vendor")
+
+ FileUtils.mkdir_p bundled_app("lol")
+ Dir.chdir(bundled_app("lol")) do
+ bundle :install
+ end
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ it "installs gems to BUNDLE_PATH from .bundle/config" do
+ config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
+
+ bundle :install
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "sets BUNDLE_PATH as the first argument to bundle install" do
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "disables system gems when passing a path to install" do
+ # This is so that vendored gems can be distributed to others
+ build_gem "rack", "1.1.0", :to_system => true
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "re-installs gems whose extensions have been deleted", :ruby_repo, :rubygems => ">= 2.3" do
+ build_lib "very_simple_binary", "1.0.0", :to_system => true do |s|
+ s.write "lib/very_simple_binary.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+
+ vendored_gems("extensions").rmtree
+
+ run "require 'very_simple_binary_c'"
+ expect(err).to include("Bundler::GemNotFound")
+
+ bundle :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ end
+ end
+
+ describe "to a file" do
+ before do
+ in_app_root do
+ FileUtils.touch "bundle"
+ end
+ end
+
+ it "reports the file exists" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, forgotten_command_line_options(:path => "bundle")
+ expect(out).to include("file already exists")
+ end
+ end
+end
diff --git a/spec/bundler/install/post_bundle_message_spec.rb b/spec/bundler/install/post_bundle_message_spec.rb
new file mode 100644
index 0000000000..eadc8a4d85
--- /dev/null
+++ b/spec/bundler/install/post_bundle_message_spec.rb
@@ -0,0 +1,206 @@
+# frozen_string_literal: true
+
+RSpec.describe "post bundle message" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", "2.3.5", :group => [:emo, :test]
+ group :test do
+ gem "rspec"
+ end
+ gem "rack-obama", :group => :obama
+ G
+ end
+
+ let(:bundle_path) { "./.bundle" }
+ let(:bundle_show_system_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." }
+ let(:bundle_show_path_message) { "Bundled gems are installed into `#{bundle_path}`" }
+ let(:bundle_complete_message) { "Bundle complete!" }
+ let(:bundle_updated_message) { "Bundle updated!" }
+ let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." }
+ let(:bundle_show_message) { Bundler::VERSION.split(".").first.to_i < 2 ? bundle_show_system_message : bundle_show_path_message }
+
+ describe "for fresh bundle install" do
+ it "without any options" do
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).not_to include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 3 gems now installed.")
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
+ end
+
+ describe "with --path and" do
+ let(:bundle_path) { "./vendor" }
+
+ it "without any options" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path inside the cwd" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("cache"))
+ expect(out).to include("Bundled gems are installed into `./cache`")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path outside the cwd" do
+ bundle! :install, forgotten_command_line_options(:path => tmp("not_bundled_app"))
+ expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "with misspelled or non-existent gem name" do
+ it "should report a helpful error message", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile.")
+ end
+
+ it "should report a helpful error message", :bundler => "2" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include normalize_uri_file(<<-EOS.strip)
+Could not find gem 'not-a-gem' in rubygems repository file://localhost#{gem_repo1}/ or installed locally.
+The source does not contain any versions of 'not-a-gem'
+ EOS
+ end
+
+ it "should report a helpful error message with reference to cache if available" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in").
+ and include("or in gems cached in vendor/cache.")
+ end
+ end
+ end
+
+ describe "for second bundle install run" do
+ it "without any options" do
+ 2.times { bundle :install }
+ expect(out).to include(bundle_show_message)
+ expect(out).to_not include("Gems in the groups")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ bundle! :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ bundle! :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "for bundle update" do
+ it "without any options" do
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).not_to include("Gems in the groups")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+ end
+end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
new file mode 100644
index 0000000000..7af8c3b304
--- /dev/null
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "when prerelease gems are available" do
+ it "finds prereleases" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "not_released"
+ G
+ expect(the_bundle).to include_gems "not_released 1.0.pre"
+ end
+
+ it "uses regular releases if available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.0"
+ end
+
+ it "uses prereleases if requested" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease", "1.1.pre"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
+ end
+ end
+
+ describe "when prerelease gems are not available" do
+ it "still works" do
+ build_repo3
+ install_gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/process_lock_spec.rb b/spec/bundler/install/process_lock_spec.rb
new file mode 100644
index 0000000000..be8fd04fdd
--- /dev/null
+++ b/spec/bundler/install/process_lock_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+RSpec.describe "process lock spec" do
+ describe "when an install operation is already holding a process lock" do
+ before { FileUtils.mkdir_p(default_bundle_path) }
+
+ it "will not run a second concurrent bundle install until the lock is released" do
+ thread = Thread.new do
+ Bundler::ProcessLock.lock(default_bundle_path) do
+ sleep 1 # ignore quality_spec
+ expect(the_bundle).not_to include_gems "rack 1.0"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ thread.join
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "when creating a lock raises Errno::ENOTSUP", :ruby => ">= 1.9" do
+ before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) }
+
+ it "skips creating the lock file and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb
new file mode 100644
index 0000000000..665c64d49a
--- /dev/null
+++ b/spec/bundler/install/redownload_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install", :bundler => "< 2", :ruby => ">= 2.0" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ before { bundle "config major_deprecations yes" }
+
+ shared_examples_for "an option to force redownloading gems" do
+ it "re-installs installed gems" do
+ rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb")
+
+ bundle! :install
+ rack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle! :install, flag => true
+
+ expect(out).to include "Installing rack 1.0.0"
+ expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "works on first bundle install" do
+ bundle! :install, flag => true
+
+ expect(out).to include "Installing rack 1.0.0"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with a git gem" do
+ let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) }
+
+ before do
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "re-installs installed gems" do
+ foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb")
+
+ bundle! :install
+ foo_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle! :install, flag => true
+
+ expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works on first bundle install" do
+ bundle! :install, flag => true
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ describe "with --force" do
+ it_behaves_like "an option to force redownloading gems" do
+ let(:flag) { "force" }
+ end
+
+ it "shows a deprecation when single flag passed" do
+ bundle! "install --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "shows a deprecation when multiple flags passed" do
+ bundle! "install --no-color --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+
+ describe "with --redownload" do
+ it_behaves_like "an option to force redownloading gems" do
+ let(:flag) { "redownload" }
+ end
+
+ it "does not show a deprecation when single flag passed" do
+ bundle! "install --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "does not show a deprecation when single multiple flags passed" do
+ bundle! "install --no-color --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+end
diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb
new file mode 100644
index 0000000000..7be09d6bd4
--- /dev/null
+++ b/spec/bundler/install/security_policy_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require "rubygems/security"
+
+# unfortunately, testing signed gems with a provided CA is extremely difficult
+# as 'gem cert' is currently the only way to add CAs to the system.
+
+RSpec.describe "policies with unsigned gems" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "rack"
+ gem "signed_gem"
+ G
+ end
+
+ it "will work after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle :install
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0"
+ end
+
+ it "will fail when given invalid security policy" do
+ bundle "install --trust-policy=InvalidPolicyName"
+ expect(out).to include("RubyGems doesn't know about trust policy")
+ end
+
+ it "will fail with High Security setting due to presence of unsigned gem" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ # This spec will fail on RubyGems 2 rc1 due to a bug in policy.rb. the bug is fixed in rc3.
+ it "will fail with Medium Security setting due to presence of unsigned gem", :unless => ENV["RGV"] == "v2.0.0.rc.1" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
+
+RSpec.describe "policies with signed gems and no CA" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "signed_gem"
+ G
+ end
+
+ it "will fail with High Security setting, gem is self-signed" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will fail with Medium Security setting, gem is self-signed" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with Low Security setting, low security accepts self signed gem" do
+ bundle "install --trust-policy=LowSecurity"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
new file mode 100644
index 0000000000..7c4b98bfdf
--- /dev/null
+++ b/spec/bundler/install/yanked_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+RSpec.context "when installing a bundle that includes yanked gems" do
+ before(:each) do
+ build_repo4 do
+ build_gem "foo", "9.0.0"
+ end
+ end
+
+ it "throws an error when the original gem version is yanked" do
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ foo (10.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo (= 10.0.0)
+
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).to include("Your bundle is locked to foo (10.0.0)")
+ end
+
+ it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).not_to include("Your bundle is locked to foo (10.0.0)")
+ expect(out).to include("Could not find gem 'foo (= 10.0.0)' in")
+ end
+end
+
+RSpec.context "when using gem before installing" do
+ it "does not suggest the author has yanked the gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo1}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 0.9.1)
+ L
+
+ bundle :list
+
+ expect(out).to include("Could not find rack-0.9.1 in any of the sources")
+ expect(out).to_not include("Your bundle is locked to rack (0.9.1), but that version could not be found in any of the sources listed in your Gemfile.")
+ expect(out).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
+ expect(out).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+ end
+end
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
new file mode 100644
index 0000000000..14b80483ee
--- /dev/null
+++ b/spec/bundler/lock/git_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle lock with git gems" do
+ before :each do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "doesn't break right after running lock" do
+ expect(the_bundle).to include_gems "foo 1.0.0"
+ end
+
+ it "locks a git source to the current ref" do
+ update_git "foo"
+ bundle :install
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides correct #full_gem_path" do
+ run <<-RUBY
+ puts Bundler.rubygems.find_name('foo').first.full_gem_path
+ RUBY
+ expect(out).to eq(bundle("info foo --path"))
+ end
+end
diff --git a/spec/bundler/lock/lockfile_bundler_1_spec.rb b/spec/bundler/lock/lockfile_bundler_1_spec.rb
new file mode 100644
index 0000000000..fcdf6ebf0d
--- /dev/null
+++ b/spec/bundler/lock/lockfile_bundler_1_spec.rb
@@ -0,0 +1,1386 @@
+# frozen_string_literal: true
+
+RSpec.describe "the lockfile format", :bundler => "< 2" do
+ include Bundler::GemHelpers
+
+ before { ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"] = "TRUE" }
+
+ it "generates a simple lockfile for a single source, gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if current ver. is newer" do
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ 1.8.2
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not update the lockfile's bundler version if nothing changed during bundle install", :ruby_repo do
+ version = "#{Bundler::VERSION.split(".").first}.0.0.0.a"
+
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ G
+ end
+
+ it "updates the lockfile's bundler version if not present" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack", "> 0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack (> 0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "outputs a warning if the current is older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ warning_message = "the running version of Bundler (9999999.0.0) is older " \
+ "than the version that created the lockfile (9999999.1.0)"
+ expect(out.scan(warning_message).size).to eq(1)
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ G
+ end
+
+ it "errors if the current is a major version older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(exitstatus > 0) if exitstatus
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "shows a friendly error when running with a new bundler 2 lockfile" do
+ lockfile <<-L
+ GEM
+ remote: https://rails-assets.org/
+ specs:
+ rails-assets-bootstrap (3.3.4)
+ rails-assets-jquery (>= 1.9.1)
+ rails-assets-jquery (2.1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.4.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rails-assets-bootstrap!
+ rake
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+ gem 'rake'
+
+ source 'https://rails-assets.org' do
+ gem 'rails-assets-bootstrap'
+ end
+ G
+
+ expect(exitstatus > 0) if exitstatus
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "warns when updating bundler major version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ expect(out).to include("Warning: the lockfile is being updated to Bundler " \
+ "9999999, after which you will be unable to return to Bundler 1.")
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with a version requirement" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile wihout credentials for a configured source" do
+ bundle "config http://localgemserver.test/ user:pass"
+
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://localgemserver.test/"
+ source "http://user:pass@othergemserver.test/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: http://localgemserver.test/
+ remote: http://user:pass@othergemserver.test/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates lockfiles with multiple requirements" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "net-sftp"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ net-sftp (1.1.1)
+ net-ssh (>= 1.0.0, < 1.99.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ net-sftp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
+ build_lib "omg", :path => lib_path("omg")
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ platforms :#{not_local_tag} do
+ gem "omg", :path => "#{lib_path("omg")}"
+ end
+
+ gem "rack"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "serializes global git sources" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
+ git = build_git "foo"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ branch: omg
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
+ git = build_git "foo"
+ update_git "foo", :tag => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :tag => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ tag: omg
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile even when packaging" do
+ build_lib "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! :install, :local => true
+
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "sorts serialized sources by type" do
+ build_lib "foo"
+ bar = build_git "bar"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("bar-1.0")}
+ revision: #{bar.ref_for("master")}
+ specs:
+ bar (1.0)
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ bar!
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "lists gems alphabetically" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "thin"
+ gem "actionpack"
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ actionpack
+ rack-obama
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies' dependencies in alphabetical order" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rails"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rails
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies by version" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem 'double_deps'
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ double_deps (1.0)
+ net-ssh
+ net-ssh (>= 1.0.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ double_deps
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :require option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0", :require => "rack/obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :group option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0", :group => :test
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path "foo"
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
+ build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+
+ install_gemfile <<-G
+ path "../foo"
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in an absolute fashion but is relative" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path File.expand_path("../foo", __FILE__)
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided for gemspec" do
+ build_lib("foo", :path => tmp.join("foo"))
+
+ install_gemfile <<-G
+ gemspec :path => "../foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "keeps existing platforms in the lockfile" do
+ lockfile <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ platforms = ["java", generic_local_platform.to_s].sort
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{platforms[0]}
+ #{platforms[1]}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "persists the spec's platform to the lockfile" do
+ build_gem "platform_specific", "1.0.0", :to_system => true do |s|
+ s.platform = Gem::Platform.new("universal-java-16")
+ end
+
+ simulate_platform "universal-java-16"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate gems" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ activesupport
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies with versions" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies in different groups" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0", :group => :one
+ gem "rack", "1.0", :group => :two
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "raises if two different versions are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (= 1.0) and rack (= 1.1)"
+ end
+
+ it "raises if two different sources are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)"
+ end
+
+ it "works correctly with multiple version dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "captures the Ruby version in the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ ruby '#{RUBY_VERSION}'
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ RUBY VERSION
+ ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ # Some versions of the Bundler 1.1 RC series introduced corrupted
+ # lockfiles. There were two major problems:
+ #
+ # * multiple copies of the same GIT section appeared in the lockfile
+ # * when this happened, those sections got multiple copies of gems
+ # in those sections.
+ it "fixes corrupted lockfiles" do
+ build_git "omg", :path => lib_path("omg")
+ revision = revision_for(lib_path("omg"))
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
+ G
+
+ bundle "install --path vendor"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Create a Gemfile.lock that has duplicate GIT sections
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Confirm that duplicate specs do not appear
+ lockfile_should_be(<<-L)
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a helpful error message when the lockfile is missing deps" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack_middleware (1.0)
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ rack_middleware
+ L
+
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem "rack_middleware"
+ G
+
+ expect(out).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")}).").
+ and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
+ end
+
+ describe "a line ending" do
+ def set_lockfile_mtime_to_known_value
+ time = Time.local(2000, 1, 1, 0, 0, 0)
+ File.utime(time, time, bundled_app("Gemfile.lock"))
+ end
+ before(:each) do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+ set_lockfile_mtime_to_known_value
+ end
+
+ it "generates Gemfile.lock with \\n line endings" do
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "during updates" do
+ it "preserves Gemfile.lock \\n line endings" do
+ update_repo2
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ update_repo2
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ context "when nothing changes" do
+ it "preserves Gemfile.lock \\n line endings" do
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+ end
+ end
+
+ it "refuses to install if Gemfile.lock contains conflict markers" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ <<<<<<<
+ rack (1.0.0)
+ =======
+ rack (1.0.1)
+ >>>>>>>
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile(<<-G)
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(last_command.bundler_err).to match(/your Gemfile.lock contains merge conflicts/i)
+ expect(last_command.bundler_err).to match(/git checkout HEAD -- Gemfile.lock/i)
+ end
+end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
new file mode 100644
index 0000000000..53c832445f
--- /dev/null
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -0,0 +1,1425 @@
+# frozen_string_literal: true
+
+RSpec.describe "the lockfile format", :bundler => "2" do
+ include Bundler::GemHelpers
+
+ before { ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"] = "TRUE" }
+
+ it "generates a simple lockfile for a single source, gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if current ver. is newer" do
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ 1.8.2
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not update the lockfile's bundler version if nothing changed during bundle install" do
+ version = "#{Bundler::VERSION.split(".").first}.0.0.0.a"
+
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ G
+ end
+
+ it "updates the lockfile's bundler version if not present" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack", "> 0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "outputs a warning if the current is older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ warning_message = "the running version of Bundler (9999999.0.0) is older " \
+ "than the version that created the lockfile (9999999.1.0)"
+ expect(last_command.bundler_err).to include warning_message
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ G
+ end
+
+ it "errors if the current is a major version older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+
+ expect(last_command).to be_failure
+ expect(last_command.bundler_err).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "shows a friendly error when running with a new bundler 2 lockfile" do
+ lockfile <<-L
+ GEM
+ remote: https://rails-assets.org/
+ specs:
+ rails-assets-bootstrap (3.3.4)
+ rails-assets-jquery (>= 1.9.1)
+ rails-assets-jquery (2.1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.4.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rails-assets-bootstrap!
+ rake
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+ gem 'rake'
+
+ source 'https://rails-assets.org' do
+ gem 'rails-assets-bootstrap'
+ end
+ G
+
+ expect(last_command).to be_failure
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "warns when updating bundler major version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+ end
+
+ expect(out).to include("Warning: the lockfile is being updated to Bundler " \
+ "9999999, after which you will be unable to return to Bundler 1.")
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with a version requirement" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile without credentials for a configured source" do
+ bundle "config http://localgemserver.test/ user:pass"
+
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://localgemserver.test/" do
+
+ end
+
+ source "http://user:pass@othergemserver.test/" do
+ gem "rack-obama", ">= 1.0"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+
+ GEM
+ remote: http://user:pass@othergemserver.test/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates lockfiles with multiple requirements" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "net-sftp"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ net-sftp (1.1.1)
+ net-ssh (>= 1.0.0, < 1.99.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-sftp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement", :bundler => "< 2" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
+ build_lib "omg", :path => lib_path("omg")
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ platforms :#{not_local_tag} do
+ gem "omg", :path => "#{lib_path("omg")}"
+ end
+
+ gem "rack"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}//
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "install"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "serializes global git sources" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
+ git = build_git "foo"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ branch: omg
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
+ git = build_git "foo"
+ update_git "foo", :tag => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :tag => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ tag: omg
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile even when packaging" do
+ build_lib "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! :install, :local => true
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "sorts serialized sources by type" do
+ build_lib "foo"
+ bar = build_git "bar"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ GIT
+ remote: #{lib_path("bar-1.0")}
+ revision: #{bar.ref_for("master")}
+ specs:
+ bar (1.0)
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ bar!
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "lists gems alphabetically" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "thin"
+ gem "actionpack"
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ actionpack
+ rack-obama
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies' dependencies in alphabetical order" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rails"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rails
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies by version" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem 'double_deps'
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ double_deps (1.0)
+ net-ssh
+ net-ssh (>= 1.0.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ double_deps
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :require option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0", :require => "rack/obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :group option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0", :group => :test
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path "foo" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
+ build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+
+ install_gemfile <<-G
+ path "../foo" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in an absolute fashion but is relative" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path File.expand_path("../foo", __FILE__) do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided for gemspec" do
+ build_lib("foo", :path => tmp.join("foo"))
+
+ install_gemfile <<-G
+ gemspec :path => "../foo"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "keeps existing platforms in the lockfile" do
+ lockfile <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms "java", generic_local_platform, specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "persists the spec's platform to the lockfile" do
+ build_repo2 do
+ build_gem "platform_specific", "1.0" do |s|
+ s.platform = Gem::Platform.new("universal-java-16")
+ end
+ end
+
+ simulate_platform "universal-java-16"
+
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "platform_specific"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0-java)
+ platform_specific (1.0-universal-java-16)
+
+ PLATFORMS
+ java
+ universal-java-16
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate gems" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies with versions" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies in different groups" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0", :group => :one
+ gem "rack", "1.0", :group => :two
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "raises if two different versions are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (= 1.0) and rack (= 1.1)"
+ end
+
+ it "raises if two different sources are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)"
+ end
+
+ it "works correctly with multiple version dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "captures the Ruby version in the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ ruby '#{RUBY_VERSION}'
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ RUBY VERSION
+ ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ # Some versions of the Bundler 1.1 RC series introduced corrupted
+ # lockfiles. There were two major problems:
+ #
+ # * multiple copies of the same GIT section appeared in the lockfile
+ # * when this happened, those sections got multiple copies of gems
+ # in those sections.
+ it "fixes corrupted lockfiles" do
+ build_git "omg", :path => lib_path("omg")
+ revision = revision_for(lib_path("omg"))
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor")
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Create a Gemfile.lock that has duplicate GIT sections
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Confirm that duplicate specs do not appear
+ lockfile_should_be(<<-L)
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a helpful error message when the lockfile is missing deps" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack_middleware
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack_middleware"
+ G
+
+ expect(out).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")}).").
+ and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
+ end
+
+ describe "a line ending" do
+ def set_lockfile_mtime_to_known_value
+ time = Time.local(2000, 1, 1, 0, 0, 0)
+ File.utime(time, time, bundled_app("Gemfile.lock"))
+ end
+ before(:each) do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+ set_lockfile_mtime_to_known_value
+ end
+
+ it "generates Gemfile.lock with \\n line endings" do
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "during updates" do
+ it "preserves Gemfile.lock \\n line endings" do
+ update_repo2
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ update_repo2
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ context "when nothing changes" do
+ it "preserves Gemfile.lock \\n line endings" do
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+ end
+ end
+
+ it "refuses to install if Gemfile.lock contains conflict markers" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}//
+ specs:
+ <<<<<<<
+ rack (1.0.0)
+ =======
+ rack (1.0.1)
+ >>>>>>>
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile(<<-G)
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ G
+
+ expect(last_command.bundler_err).to match(/your Gemfile.lock contains merge conflicts/i)
+ expect(last_command.bundler_err).to match(/git checkout HEAD -- Gemfile.lock/i)
+ end
+end
diff --git a/spec/bundler/other/bundle_ruby_spec.rb b/spec/bundler/other/bundle_ruby_spec.rb
new file mode 100644
index 0000000000..a7da9cbec9
--- /dev/null
+++ b/spec/bundler/other/bundle_ruby_spec.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle_ruby", :bundler => "< 2" do
+ context "without patchlevel" do
+ it "returns the ruby version" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3")
+ end
+
+ it "engine defaults to MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3"
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3")
+ end
+
+ it "handles jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.8.7 (jruby 1.6.5)")
+ end
+
+ it "handles rbx" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.8.7 (rbx 1.2.4)")
+ end
+
+ it "handles truffleruby", :rubygems => ">= 2.1.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
+ end
+
+ it "raises an error if engine is used but engine version is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("Please define :engine_version")
+ end
+
+ it "raises an error if engine_version is used but engine is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("Please define :engine")
+ end
+
+ it "raises an error if engine version doesn't match ruby version for MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("ruby_version must match the :engine_version for MRI")
+ end
+
+ it "should print if no ruby version is specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("No ruby version specified")
+ end
+ end
+
+ context "when using patchlevel" do
+ it "returns the ruby version" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :patchlevel => '429', :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3p429")
+ end
+
+ it "handles an engine" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :patchlevel => '392', :engine => 'jruby', :engine_version => '1.7.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3p392 (jruby 1.7.4)")
+ end
+ end
+end
diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb
new file mode 100644
index 0000000000..d17819b394
--- /dev/null
+++ b/spec/bundler/other/cli_dispatch_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle command names" do
+ it "work when given fully" do
+ bundle "install"
+ expect(last_command.bundler_err).to eq("Could not locate Gemfile")
+ expect(last_command.stdboth).not_to include("Ambiguous command")
+ end
+
+ it "work when not ambiguous" do
+ bundle "ins"
+ expect(last_command.bundler_err).to eq("Could not locate Gemfile")
+ expect(last_command.stdboth).not_to include("Ambiguous command")
+ end
+
+ it "print a friendly error when ambiguous" do
+ bundle "in"
+ expect(last_command.bundler_err).to eq("Ambiguous command in matches [info, init, inject, install]")
+ end
+
+ context "when cache_command_is_package is set" do
+ before { bundle! "config cache_command_is_package true" }
+
+ it "dispatches `bundle cache` to the package command" do
+ bundle "cache --verbose"
+ expect(last_command.stdout).to start_with "Running `bundle package --verbose`"
+ end
+ end
+end
diff --git a/spec/bundler/other/compatibility_guard_spec.rb b/spec/bundler/other/compatibility_guard_spec.rb
new file mode 100644
index 0000000000..ac05ebd918
--- /dev/null
+++ b/spec/bundler/other/compatibility_guard_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler compatibility guard" do
+ context "when the bundler version is 2+" do
+ before { simulate_bundler_version "2.0.a" }
+
+ context "when running on Ruby < 2.3", :ruby => "< 2.3" do
+ before { simulate_rubygems_version "2.6.11" }
+
+ it "raises a friendly error" do
+ bundle :version
+ expect(err).to eq("Bundler 2 requires Ruby 2.3 or later. Either install bundler 1 or update to a supported Ruby version.")
+ end
+ end
+
+ context "when running on RubyGems < 2.5", :ruby => ">= 2.5" do
+ before { simulate_rubygems_version "1.3.6" }
+
+ it "raises a friendly error" do
+ bundle :version
+ expect(err).to eq("Bundler 2 requires RubyGems 2.5 or later. Either install bundler 1 or update to a supported RubyGems version.")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb
new file mode 100644
index 0000000000..3f6f8b4928
--- /dev/null
+++ b/spec/bundler/other/ext_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+RSpec.describe "Gem::Specification#match_platform" do
+ it "does not match platforms other than the gem platform" do
+ darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10"
+ expect(darwin.match_platform(pl("java"))).to eq(false)
+ end
+
+ context "when platform is a string" do
+ it "matches when platform is a string" do
+ lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32")
+ expect(lazy_spec.match_platform(pl("x86-mingw32"))).to eq(true)
+ expect(lazy_spec.match_platform(pl("x64-mingw32"))).to eq(true)
+ end
+ end
+end
+
+RSpec.describe "Bundler::GemHelpers#generic" do
+ include Bundler::GemHelpers
+
+ it "converts non-windows platforms into ruby" do
+ expect(generic(pl("x86-darwin-10"))).to eq(pl("ruby"))
+ expect(generic(pl("ruby"))).to eq(pl("ruby"))
+ end
+
+ it "converts java platform variants into java" do
+ expect(generic(pl("universal-java-17"))).to eq(pl("java"))
+ expect(generic(pl("java"))).to eq(pl("java"))
+ end
+
+ it "converts mswin platform variants into x86-mswin32" do
+ expect(generic(pl("mswin32"))).to eq(pl("x86-mswin32"))
+ expect(generic(pl("i386-mswin32"))).to eq(pl("x86-mswin32"))
+ expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32"))
+ end
+
+ it "converts 32-bit mingw platform variants into x86-mingw32" do
+ expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32"))
+ expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32"))
+ expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32"))
+ end
+
+ it "converts 64-bit mingw platform variants into x64-mingw32" do
+ expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32"))
+ expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32"))
+ end
+end
+
+RSpec.describe "Gem::SourceIndex#refresh!" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "does not explode when called", :rubygems => "1.7" do
+ run "Gem.source_index.refresh!"
+ run "Gem::SourceIndex.new([]).refresh!"
+ end
+
+ it "does not explode when called", :rubygems => "< 1.7" do
+ run "Gem.source_index.refresh!"
+ run "Gem::SourceIndex.from_gems_in([]).refresh!"
+ end
+end
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
new file mode 100644
index 0000000000..fba177b497
--- /dev/null
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -0,0 +1,282 @@
+# frozen_string_literal: true
+
+RSpec.describe "major deprecations", :bundler => "< 2" do
+ let(:warnings) { last_command.bundler_err } # change to err in 2.0
+ let(:warnings_without_version_messages) { warnings.gsub(/#{Spec::Matchers::MAJOR_DEPRECATION}Bundler will only support ruby(gems)? >= .*/, "") }
+
+ context "in a .99 version" do
+ before do
+ simulate_bundler_version "1.99.1"
+ bundle "config --delete major_deprecations"
+ end
+
+ it "prints major deprecations without being configured" do
+ ruby <<-R
+ require "bundler"
+ Bundler::SharedHelpers.major_deprecation(Bundler::VERSION)
+ R
+
+ expect(warnings).to have_major_deprecation("1.99.1")
+ end
+ end
+
+ before do
+ bundle "config major_deprecations true"
+
+ create_file "gems.rb", <<-G
+ source "file:#{gem_repo1}"
+ ruby #{RUBY_VERSION.dump}
+ gem "rack"
+ G
+ bundle! "install"
+ end
+
+ describe "bundle_ruby" do
+ it "prints a deprecation" do
+ bundle_ruby
+ warnings.gsub! "\nruby #{RUBY_VERSION}", ""
+ expect(warnings).to have_major_deprecation "the bundle_ruby executable has been removed in favor of `bundle platform --ruby`"
+ end
+ end
+
+ describe "Bundler" do
+ describe ".clean_env" do
+ it "is deprecated in favor of .original_env" do
+ source = "Bundler.clean_env"
+ bundle "exec ruby -e #{source.dump}"
+ expect(warnings).to have_major_deprecation "`Bundler.clean_env` has weird edge cases, use `.original_env` instead"
+ end
+ end
+
+ describe ".environment" do
+ it "is deprecated in favor of .load" do
+ source = "Bundler.environment"
+ bundle "exec ruby -e #{source.dump}"
+ expect(warnings).to have_major_deprecation "Bundler.environment has been removed in favor of Bundler.load"
+ end
+ end
+
+ shared_examples_for "environmental deprecations" do |trigger|
+ describe "ruby version", :ruby => "< 2.0" do
+ it "requires a newer ruby version" do
+ instance_eval(&trigger)
+ expect(warnings).to have_major_deprecation "Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}"
+ end
+ end
+
+ describe "rubygems version", :rubygems => "< 2.0" do
+ it "requires a newer rubygems version" do
+ instance_eval(&trigger)
+ expect(warnings).to have_major_deprecation "Bundler will only support rubygems >= 2.0, you are running #{Gem::VERSION}"
+ end
+ end
+ end
+
+ describe "-rbundler/setup" do
+ it_behaves_like "environmental deprecations", proc { ruby "require 'bundler/setup'" }
+ end
+
+ describe "Bundler.setup" do
+ it_behaves_like "environmental deprecations", proc { ruby "require 'bundler'; Bundler.setup" }
+ end
+
+ describe "bundle check" do
+ it_behaves_like "environmental deprecations", proc { bundle :check }
+ end
+
+ describe "bundle update --quiet" do
+ it "does not print any deprecations" do
+ bundle :update, :quiet => true
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+ end
+
+ describe "bundle update" do
+ before do
+ create_file("gems.rb", "")
+ bundle! "install"
+ end
+
+ it "warns when no options are given" do
+ bundle! "update"
+ expect(warnings).to have_major_deprecation a_string_including("Pass --all to `bundle update` to update everything")
+ end
+
+ it "does not warn when --all is passed" do
+ bundle! "update --all"
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+ end
+
+ describe "bundle install --binstubs" do
+ it "should output a deprecation warning" do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ bundle :install, :binstubs => true
+ expect(warnings).to have_major_deprecation a_string_including("The --binstubs option will be removed")
+ end
+ end
+ end
+
+ context "when bundle is run" do
+ it "should not warn about gems.rb" do
+ create_file "gems.rb", <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+
+ it "should print a Gemfile deprecation warning" do
+ create_file "gems.rb"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ expect(the_bundle).to include_gem "rack 1.0"
+
+ expect(warnings).to have_major_deprecation a_string_including("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
+ end
+
+ context "with flags" do
+ it "should print a deprecation warning about autoremembering flags" do
+ install_gemfile <<-G, :path => "vendor/bundle"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(warnings).to have_major_deprecation a_string_including(
+ "flags passed to commands will no longer be automatically remembered."
+ )
+ end
+ end
+ end
+
+ context "when Bundler.setup is run in a ruby script" do
+ it "should print a single deprecation warning" do
+ create_file "gems.rb"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :test
+ G
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ require 'bundler/vendored_thor'
+
+ Bundler.ui = Bundler::UI::Shell.new
+ Bundler.setup
+ Bundler.setup
+ RUBY
+
+ expect(warnings_without_version_messages).to have_major_deprecation("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
+ end
+ end
+
+ context "when `bundler/deployment` is required in a ruby script" do
+ it "should print a capistrano deprecation warning" do
+ ruby(<<-RUBY)
+ require 'bundler/deployment'
+ RUBY
+
+ expect(warnings).to have_major_deprecation("Bundler no longer integrates " \
+ "with Capistrano, but Capistrano provides " \
+ "its own integration with Bundler via the " \
+ "capistrano-bundler gem. Use it instead.")
+ end
+ end
+
+ describe Bundler::Dsl do
+ before do
+ @rubygems = double("rubygems")
+ allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
+ end
+
+ context "with github gems" do
+ it "warns about the https change" do
+ msg = <<-EOS
+The :github git source is deprecated, and will be removed in Bundler 2.0. Change any "reponame" :github sources to "username/reponame". Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:github) {|repo_name| "https://github.com/\#{repo_name}.git" }
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("sparks", :github => "indirect/sparks")
+ end
+
+ it "upgrades to https on request" do
+ Bundler.settings.temporary "github.https" => true
+ msg = <<-EOS
+The :github git source is deprecated, and will be removed in Bundler 2.0. Change any "reponame" :github sources to "username/reponame". Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:github) {|repo_name| "https://github.com/\#{repo_name}.git" }
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, "The `github.https` setting will be removed")
+ subject.gem("sparks", :github => "indirect/sparks")
+ github_uri = "https://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+ end
+
+ context "with bitbucket gems" do
+ it "warns about removal" do
+ allow(Bundler.ui).to receive(:deprecate)
+ msg = <<-EOS
+The :bitbucket git source is deprecated, and will be removed in Bundler 2.0. Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:bitbucket) do |repo_name|
+ user_name, repo_name = repo_name.split("/")
+ repo_name ||= user_name
+ "https://\#{user_name}@bitbucket.org/\#{user_name}/\#{repo_name}.git"
+ end
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ end
+ end
+
+ context "with gist gems" do
+ it "warns about removal" do
+ allow(Bundler.ui).to receive(:deprecate)
+ msg = "The :gist git source is deprecated, and will be removed " \
+ "in Bundler 2.0. Add this code to the top of your Gemfile to ensure it " \
+ "continues to work:\n\n git_source(:gist) {|repo_name| " \
+ "\"https://gist.github.com/\#{repo_name}.git\" }\n\n"
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("not-really-a-gem", :gist => "1234")
+ end
+ end
+ end
+
+ context "bundle show" do
+ it "prints a deprecation warning" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :show
+
+ warnings.gsub!(/gems included.*?\[DEPRECATED/im, "[DEPRECATED")
+
+ expect(warnings).to have_major_deprecation a_string_including("use `bundle list` instead of `bundle show`")
+ end
+ end
+
+ context "bundle console" do
+ it "prints a deprecation warning" do
+ bundle "console"
+
+ expect(warnings).to have_major_deprecation \
+ a_string_including("bundle console will be replaced by `bin/console` generated by `bundle gem <name>`")
+ end
+ end
+end
diff --git a/spec/bundler/other/platform_spec.rb b/spec/bundler/other/platform_spec.rb
new file mode 100644
index 0000000000..ca74945563
--- /dev/null
+++ b/spec/bundler/other/platform_spec.rb
@@ -0,0 +1,1312 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle platform" do
+ context "without flags" do
+ let(:bundle_platform_platforms_string) do
+ platforms = [rb]
+ platforms.unshift(specific_local_platform) if Bundler.feature_flag.bundler_2_mode?
+ platforms.map {|pl| "* #{pl}" }.join("\n")
+ end
+
+ it "returns all the output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_correct}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{RUBY_VERSION}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "returns all the output including the patchlevel" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_correct_patchlevel}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "doesn't print ruby version requirement if it isn't specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile does not specify a Ruby version requirement.
+G
+ end
+
+ it "doesn't match the ruby version requirement" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_incorrect}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{not_local_ruby_version}
+
+Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}
+G
+ end
+ end
+
+ context "--ruby" do
+ it "returns ruby version when explicit" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "defaults to MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "handles jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)")
+ end
+
+ it "handles rbx" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)")
+ end
+
+ it "handles truffleruby", :rubygems => ">= 2.1.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
+ end
+
+ it "raises an error if engine is used but engine version is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "raises an error if engine_version is used but engine is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "raises an error if engine version doesn't match ruby version for MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "should print if no ruby version is specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("No ruby version specified")
+ end
+
+ it "handles when there is a locked requirement" do
+ gemfile <<-G
+ ruby "< 1.8.7"
+ G
+
+ lockfile <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 1.0.0p127
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.0.0p127")
+ end
+
+ it "handles when there is a requirement in the gemfile" do
+ gemfile <<-G
+ ruby ">= 1.8.7"
+ G
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ end
+
+ it "handles when there are multiple requirements in the gemfile" do
+ gemfile <<-G
+ ruby ">= 1.8.7", "< 2.0.0"
+ G
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ end
+ end
+
+ let(:ruby_version_correct) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" }
+ let(:ruby_version_correct_engineless) { "ruby \"#{RUBY_VERSION}\"" }
+ let(:ruby_version_correct_patchlevel) { "#{ruby_version_correct}, :patchlevel => '#{RUBY_PATCHLEVEL}'" }
+ let(:ruby_version_incorrect) { "ruby \"#{not_local_ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_ruby_version}\"" }
+ let(:engine_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{RUBY_VERSION}\"" }
+ let(:engine_version_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" }
+ let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" }
+ let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" }
+
+ def should_be_ruby_version_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}")
+ end
+
+ def should_be_engine_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your Ruby engine is #{local_ruby_engine}, but your Gemfile specified #{not_local_tag}")
+ end
+
+ def should_be_engine_version_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}")
+ end
+
+ def should_be_patchlevel_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}")
+ end
+
+ def should_be_patchlevel_fixnum
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("The Ruby patchlevel in your Gemfile must be a string")
+ end
+
+ context "bundle install" do
+ it "installs fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "installs fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ it "installs fine when the patchlevel matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_patchlevel}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "doesn't install when the ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "doesn't install when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "doesn't install when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "doesn't install when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle check" do
+ it "checks fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("Resolving dependencies...\nThe Gemfile's dependencies are satisfied")
+ end
+
+ it "checks fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("Resolving dependencies...\nThe Gemfile's dependencies are satisfied")
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :check
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ bundle :check
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "ruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :check
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :check
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle update" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ G
+ end
+
+ it "updates successfully when the ruby version matches" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_correct}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "updates fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_correct_engineless}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when ruby engine doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{engine_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_engine_incorrect
+ end
+
+ it "fails when ruby engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{engine_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle info" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "prints path if ruby version is correct" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "prints path if ruby version is correct for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+ end
+
+ it "fails if ruby version doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if engine doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{engine_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_engine_incorrect
+ end
+
+ it "fails if engine version doesn't match", :bundler => "< 2" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "show rails"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle cache" do
+ before do
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle! :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :cache
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :cache
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :cache
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :cache
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle pack" do
+ before do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :pack
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :pack
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :pack
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :pack
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :pack
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :pack
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle exec" do
+ before do
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path
+ end
+
+ it "activates the correct gem when ruby version matches" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+
+ it "activates the correct gem when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+ end
+
+ it "fails when the ruby version doesn't match" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{engine_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_engine_incorrect
+ end
+
+ # it "fails when the engine version doesn't match" do
+ # simulate_ruby_engine "jruby" do
+ # gemfile <<-G
+ # gem "rack", "0.9.1"
+ #
+ # #{engine_version_incorrect}
+ # G
+ #
+ # bundle "exec rackup"
+ # should_be_engine_version_incorrect
+ # end
+ # end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle console", :bundler => "< 2" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_correct}
+ G
+
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "console"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{engine_incorrect}
+ G
+
+ bundle "console"
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "console"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "console"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "Bundler.setup" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack", :group => :test
+ G
+
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ it "makes a Gemfile.lock if setup succeeds" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "makes a Gemfile.lock if setup succeeds for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle outdated" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+ end
+
+ it "returns list of outdated gems when the ruby version matches" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "outdated"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5")
+ expect(out).to include("foo (newest 1.0")
+ end
+
+ it "returns list of outdated gems when the ruby version matches for any engine" do
+ simulate_ruby_engine "jruby" do
+ bundle! :install
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "outdated"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("foo (newest 1.0")
+ end
+ end
+
+ it "fails when the ruby version doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_engine_incorrect
+ end
+
+ it "fails when the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when the patchlevel doesn't match" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ it "fails when the patchlevel is a fixnum" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{patchlevel_fixnum}
+ G
+
+ bundle "outdated"
+ should_be_patchlevel_fixnum
+ end
+ end
+ end
+end
diff --git a/spec/bundler/other/ssl_cert_spec.rb b/spec/bundler/other/ssl_cert_spec.rb
new file mode 100644
index 0000000000..6d957276fc
--- /dev/null
+++ b/spec/bundler/other/ssl_cert_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require "bundler/ssl_certs/certificate_manager"
+
+RSpec.describe "SSL Certificates", :rubygems_master do
+ hosts = %w[
+ rubygems.org
+ index.rubygems.org
+ rubygems.global.ssl.fastly.net
+ staging.rubygems.org
+ ]
+
+ hosts.each do |host|
+ it "can securely connect to #{host}", :realworld do
+ Bundler::SSLCerts::CertificateManager.new.connect_to(host)
+ end
+ end
+end
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
new file mode 100644
index 0000000000..999d8b722b
--- /dev/null
+++ b/spec/bundler/plugins/command_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+RSpec.describe "command plugins" do
+ before do
+ build_repo2 do
+ build_plugin "command-mah" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Mah
+ class Plugin < Bundler::Plugin::API
+ command "mahcommand" # declares the command
+
+ def exec(command, args)
+ puts "MahHello"
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install command-mah --source file://#{gem_repo2}"
+ end
+
+ it "executes without arguments" do
+ expect(out).to include("Installed plugin command-mah")
+
+ bundle "mahcommand"
+ expect(out).to eq("MahHello")
+ end
+
+ it "accepts the arguments" do
+ build_repo2 do
+ build_plugin "the-echoer" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Resonance
+ class Echoer
+ # Another method to declare the command
+ Bundler::Plugin::API.command "echo", self
+
+ def exec(command, args)
+ puts "You gave me \#{args.join(", ")}"
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install the-echoer --source file://#{gem_repo2}"
+ expect(out).to include("Installed plugin the-echoer")
+
+ bundle "echo tacos tofu lasange"
+ expect(out).to eq("You gave me tacos, tofu, lasange")
+ end
+
+ it "raises error on redeclaration of command" do
+ build_repo2 do
+ build_plugin "copycat" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module CopyCat
+ class Cheater < Bundler::Plugin::API
+ command "mahcommand", self
+
+ def exec(command, args)
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install copycat --source file://#{gem_repo2}"
+
+ expect(out).not_to include("Installed plugin copycat")
+
+ expect(out).to include("Failed to install plugin")
+
+ expect(out).to include("Command(s) `mahcommand` declared by copycat are already registered.")
+ end
+end
diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb
new file mode 100644
index 0000000000..53062095e2
--- /dev/null
+++ b/spec/bundler/plugins/hook_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+RSpec.describe "hook plugins" do
+ context "before-install-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-install-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL_ALL do |deps|
+ puts "gems to be installed \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-install-all-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs before all rubygems are installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "gems to be installed rake, rack"
+ end
+ end
+
+ context "before-install hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-install-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL do |spec_install|
+ puts "installing gem \#{spec_install.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-install-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs before each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installing gem rake"
+ expect(out).to include "installing gem rack"
+ end
+ end
+
+ context "after-install-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-install-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL_ALL do |deps|
+ puts "installed gems \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-install-all-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs after each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installed gems rake, rack"
+ end
+ end
+
+ context "after-install hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-install-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL do |spec_install|
+ puts "installed gem \#{spec_install.name} : \#{spec_install.state}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-install-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs after each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installed gem rake : installed"
+ expect(out).to include "installed gem rack : installed"
+ end
+ end
+end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
new file mode 100644
index 0000000000..9304d78062
--- /dev/null
+++ b/spec/bundler/plugins/install_spec.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler plugin install" do
+ before do
+ build_repo2 do
+ build_plugin "foo"
+ build_plugin "kung-foo"
+ end
+ end
+
+ it "shows proper message when gem in not found in the source" do
+ bundle "plugin install no-foo --source file://#{gem_repo1}"
+
+ expect(out).to include("Could not find")
+ plugin_should_not_be_installed("no-foo")
+ end
+
+ it "installs from rubygems source" do
+ bundle "plugin install foo --source file://#{gem_repo2}"
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
+ it "installs multiple plugins" do
+ bundle "plugin install foo kung-foo --source file://#{gem_repo2}"
+
+ expect(out).to include("Installed plugin foo")
+ expect(out).to include("Installed plugin kung-foo")
+
+ plugin_should_be_installed("foo", "kung-foo")
+ end
+
+ it "uses the same version for multiple plugins" do
+ update_repo2 do
+ build_plugin "foo", "1.1"
+ build_plugin "kung-foo", "1.1"
+ end
+
+ bundle "plugin install foo kung-foo --version '1.0' --source file://#{gem_repo2}"
+
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("Installing kung-foo 1.0")
+ plugin_should_be_installed("foo", "kung-foo")
+ end
+
+ it "works with different load paths" do
+ build_repo2 do
+ build_plugin "testing" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "fubar"
+ class Test < Bundler::Plugin::API
+ command "check2"
+
+ def exec(command, args)
+ puts "mate"
+ end
+ end
+ RUBY
+ s.require_paths = %w[lib src]
+ s.write("src/fubar.rb")
+ end
+ end
+ bundle "plugin install testing --source file://#{gem_repo2}"
+
+ bundle "check2", "no-color" => false
+ expect(out).to eq("mate")
+ end
+
+ context "malformatted plugin" do
+ it "fails when plugins.rb is missing" do
+ update_repo2 do
+ build_plugin "foo", "1.1"
+ build_plugin "kung-foo", "1.1"
+ end
+
+ bundle "plugin install foo kung-foo --version '1.0' --source file://#{gem_repo2}"
+
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("Installing kung-foo 1.0")
+ plugin_should_be_installed("foo", "kung-foo")
+
+ build_repo2 do
+ build_gem "charlie"
+ end
+
+ bundle "plugin install charlie --source file://#{gem_repo2}"
+
+ expect(out).to include("plugins.rb was not found")
+
+ expect(global_plugin_gem("charlie-1.0")).not_to be_directory
+
+ plugin_should_be_installed("foo", "kung-foo")
+ plugin_should_not_be_installed("charlie")
+ end
+
+ it "fails when plugins.rb throws exception on load" do
+ build_repo2 do
+ build_plugin "chaplin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ raise "I got you man"
+ RUBY
+ end
+ end
+
+ bundle "plugin install chaplin --source file://#{gem_repo2}"
+
+ expect(global_plugin_gem("chaplin-1.0")).not_to be_directory
+
+ plugin_should_not_be_installed("chaplin")
+ end
+ end
+
+ context "git plugins" do
+ it "installs form a git source" do
+ build_git "foo" do |s|
+ s.write "plugins.rb"
+ end
+
+ bundle "plugin install foo --git file://#{lib_path("foo-1.0")}"
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+ end
+
+ context "Gemfile eval" do
+ it "installs plugins listed in gemfile" do
+ gemfile <<-G
+ source 'file://#{gem_repo2}'
+ plugin 'foo'
+ gem 'rack', "1.0.0"
+ G
+
+ bundle "install"
+
+ expect(out).to include("Installed plugin foo")
+
+ expect(out).to include("Bundle complete!")
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ plugin_should_be_installed("foo")
+ end
+
+ it "accepts plugin version" do
+ update_repo2 do
+ build_plugin "foo", "1.1.0"
+ end
+
+ install_gemfile <<-G
+ source 'file://#{gem_repo2}'
+ plugin 'foo', "1.0"
+ G
+
+ bundle "install"
+
+ expect(out).to include("Installing foo 1.0")
+
+ plugin_should_be_installed("foo")
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "accepts git sources" do
+ build_git "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ install_gemfile <<-G
+ plugin 'ga-plugin', :git => "#{lib_path("ga-plugin-1.0")}"
+ G
+
+ expect(out).to include("Installed plugin ga-plugin")
+ plugin_should_be_installed("ga-plugin")
+ end
+ end
+
+ context "inline gemfiles" do
+ it "installs the listed plugins" do
+ code = <<-RUBY
+ require "bundler/inline"
+
+ gemfile do
+ source 'file://#{gem_repo2}'
+ plugin 'foo'
+ end
+ RUBY
+
+ ruby code
+ expect(local_plugin_gem("foo-1.0", "plugins.rb")).to exist
+ end
+ end
+
+ describe "local plugin" do
+ it "is installed when inside an app" do
+ gemfile ""
+ bundle "plugin install foo --source file://#{gem_repo2}"
+
+ plugin_should_be_installed("foo")
+ expect(local_plugin_gem("foo-1.0")).to be_directory
+ end
+
+ context "conflict with global plugin" do
+ before do
+ update_repo2 do
+ build_plugin "fubar" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Fubar < Bundler::Plugin::API
+ command "shout"
+
+ def exec(command, args)
+ puts "local_one"
+ end
+ end
+ RUBY
+ end
+ end
+
+ # inside the app
+ gemfile "source 'file://#{gem_repo2}'\nplugin 'fubar'"
+ bundle "install"
+
+ update_repo2 do
+ build_plugin "fubar", "1.1" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Fubar < Bundler::Plugin::API
+ command "shout"
+
+ def exec(command, args)
+ puts "global_one"
+ end
+ end
+ RUBY
+ end
+ end
+
+ # outside the app
+ Dir.chdir tmp
+ bundle "plugin install fubar --source file://#{gem_repo2}"
+ end
+
+ it "inside the app takes precedence over global plugin" do
+ Dir.chdir bundled_app
+
+ bundle "shout"
+ expect(out).to eq("local_one")
+ end
+
+ it "outside the app global plugin is used" do
+ Dir.chdir tmp
+
+ bundle "shout"
+ expect(out).to eq("global_one")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
new file mode 100644
index 0000000000..fd30892f63
--- /dev/null
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -0,0 +1,505 @@
+# frozen_string_literal: true
+
+RSpec.describe "real source plugins" do
+ context "with a minimal source plugin" do
+ before do
+ build_repo2 do
+ build_plugin "bundler-source-mpath" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "bundler/vendored_fileutils"
+ require "bundler-source-mpath"
+
+ class MPath < Bundler::Plugin::API
+ source "mpath"
+
+ attr_reader :path
+
+ def initialize(opts)
+ super
+
+ @path = Pathname.new options["uri"]
+ end
+
+ def fetch_gemspec_files
+ @spec_files ||= begin
+ glob = "{,*,*/*}.gemspec"
+ if installed?
+ search_path = install_path
+ else
+ search_path = path
+ end
+ Dir["\#{search_path.to_s}/\#{glob}"]
+ end
+ end
+
+ def install(spec, opts)
+ mkdir_p(install_path.parent)
+ FileUtils.cp_r(path, install_path)
+
+ spec_path = install_path.join("\#{spec.full_name}.gemspec")
+ spec_path.open("wb") {|f| f.write spec.to_ruby }
+ spec.loaded_from = spec_path.to_s
+
+ post_install(spec)
+
+ nil
+ end
+ end
+ RUBY
+ end # build_plugin
+ end
+
+ build_lib "a-path-gem"
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo2}" # plugin source
+ source "#{lib_path("a-path-gem-1.0")}", :type => :mpath do
+ gem "a-path-gem"
+ end
+ G
+ end
+
+ it "installs" do
+ bundle "install"
+
+ expect(out).to include("Bundle complete!")
+
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "writes to lock file", :bundler => "< 2" do
+ bundle "install"
+
+ lockfile_should_be <<-G
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "writes to lock file", :bundler => "2" do
+ bundle "install"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "provides correct #full_gem_path" do
+ bundle "install"
+ run <<-RUBY
+ puts Bundler.rubygems.find_name('a-path-gem').first.full_gem_path
+ RUBY
+ expect(out).to eq(bundle("info a-path-gem --path"))
+ end
+
+ it "installs the gem executables" do
+ build_lib "gem-with-bin" do |s|
+ s.executables = ["foo"]
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source "#{lib_path("gem-with-bin-1.0")}", :type => :mpath do
+ gem "gem-with-bin"
+ end
+ G
+
+ bundle "exec foo"
+ expect(out).to eq("1.0")
+ end
+
+ describe "bundle cache/package" do
+ let(:uri_hash) { Digest(:SHA1).hexdigest(lib_path("a-path-gem-1.0").to_s) }
+ it "copies repository to vendor cache and uses it" do
+ bundle "install"
+ bundle :cache, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "copies repository to vendor cache and uses it even when installed with bundle --path" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :cache, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "bundler package copies repository to vendor cache" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+ end
+
+ context "with lockfile" do
+ before do
+ lockfile <<-G
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "installs" do
+ bundle! "install"
+
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+ end
+ end
+
+ context "with a more elaborate source plugin" do
+ before do
+ build_repo2 do
+ build_plugin "bundler-source-gitp" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class SPlugin < Bundler::Plugin::API
+ source "gitp"
+
+ attr_reader :ref
+
+ def initialize(opts)
+ super
+
+ @ref = options["ref"] || options["branch"] || options["tag"] || "master"
+ @unlocked = false
+ end
+
+ def eql?(other)
+ other.is_a?(self.class) && uri == other.uri && ref == other.ref
+ end
+
+ alias_method :==, :eql?
+
+ def fetch_gemspec_files
+ @spec_files ||= begin
+ glob = "{,*,*/*}.gemspec"
+ if !cached?
+ cache_repo
+ end
+
+ if installed? && !@unlocked
+ path = install_path
+ else
+ path = cache_path
+ end
+
+ Dir["\#{path}/\#{glob}"]
+ end
+ end
+
+ def install(spec, opts)
+ mkdir_p(install_path.dirname)
+ rm_rf(install_path)
+ `git clone --no-checkout --quiet "\#{cache_path}" "\#{install_path}"`
+ Dir.chdir install_path do
+ `git reset --hard \#{revision}`
+ end
+
+ spec_path = install_path.join("\#{spec.full_name}.gemspec")
+ spec_path.open("wb") {|f| f.write spec.to_ruby }
+ spec.loaded_from = spec_path.to_s
+
+ post_install(spec)
+
+ nil
+ end
+
+ def options_to_lock
+ opts = {"revision" => revision}
+ opts["ref"] = ref if ref != "master"
+ opts
+ end
+
+ def unlock!
+ @unlocked = true
+ @revision = latest_revision
+ end
+
+ def app_cache_dirname
+ "\#{base_name}-\#{shortref_for_path(revision)}"
+ end
+
+ private
+
+ def cache_path
+ @cache_path ||= cache_dir.join("gitp", base_name)
+ end
+
+ def cache_repo
+ `git clone --quiet \#{@options["uri"]} \#{cache_path}`
+ end
+
+ def cached?
+ File.directory?(cache_path)
+ end
+
+ def locked_revision
+ options["revision"]
+ end
+
+ def revision
+ @revision ||= locked_revision || latest_revision
+ end
+
+ def latest_revision
+ if !cached? || @unlocked
+ rm_rf(cache_path)
+ cache_repo
+ end
+
+ Dir.chdir cache_path do
+ `git rev-parse --verify \#{@ref}`.strip
+ end
+ end
+
+ def base_name
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*}, ""), ".git")
+ end
+
+ def shortref_for_path(ref)
+ ref[0..11]
+ end
+
+ def install_path
+ @install_path ||= begin
+ git_scope = "\#{base_name}-\#{shortref_for_path(revision)}"
+
+ path = gem_install_dir.join(git_scope)
+
+ if !path.exist? && requires_sudo?
+ user_bundle_path.join(ruby_scope).join(git_scope)
+ else
+ path
+ end
+ end
+ end
+
+ def installed?
+ File.directory?(install_path)
+ end
+ end
+ RUBY
+ end
+ end
+
+ build_git "ma-gitp-gem"
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo2}" # plugin source
+ source "file://#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
+ gem "ma-gitp-gem"
+ end
+ G
+ end
+
+ it "handles the source option" do
+ bundle "install"
+ expect(out).to include("Bundle complete!")
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
+ end
+
+ it "writes to lock file", :bundler => "< 2" do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ bundle "install"
+
+ lockfile_should_be <<-G
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "writes to lock file", :bundler => "2" do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ bundle "install"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ context "with lockfile" do
+ before do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ lockfile <<-G
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "installs" do
+ bundle "install"
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
+ end
+
+ it "uses the locked ref" do
+ update_git "ma-gitp-gem"
+ bundle "install"
+
+ run <<-RUBY
+ require 'ma-gitp-gem'
+ puts "WIN" unless defined?(MAGITPGEM_PREV_REF)
+ RUBY
+ expect(out).to eq("WIN")
+ end
+
+ it "updates the deps on bundler update" do
+ update_git "ma-gitp-gem"
+ bundle "update ma-gitp-gem"
+
+ run <<-RUBY
+ require 'ma-gitp-gem'
+ puts "WIN" if defined?(MAGITPGEM_PREV_REF)
+ RUBY
+ expect(out).to eq("WIN")
+ end
+
+ it "updates the deps on change in gemfile" do
+ update_git "ma-gitp-gem", "1.1", :path => lib_path("ma-gitp-gem-1.0"), :gemspec => true
+ gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source "file://#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
+ gem "ma-gitp-gem", "1.1"
+ end
+ G
+ bundle "install"
+
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.1")
+ end
+ end
+
+ describe "bundle cache with gitp" do
+ it "copies repository to vendor cache and uses it" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source '#{lib_path("foo-1.0")}', :type => :gitp do
+ gem "foo"
+ end
+ G
+
+ bundle :cache, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/plugins/source_spec.rb b/spec/bundler/plugins/source_spec.rb
new file mode 100644
index 0000000000..543e90eb60
--- /dev/null
+++ b/spec/bundler/plugins/source_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler source plugin" do
+ describe "plugins dsl eval for #source with :type option" do
+ before do
+ update_repo2 do
+ build_plugin "bundler-source-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class OPSource < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+ end
+
+ it "installs bundler-source-* gem when no handler for source is present" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+
+ plugin_should_be_installed("bundler-source-psource")
+ end
+
+ it "enables the plugin to require a lib path" do
+ update_repo2 do
+ build_plugin "bundler-source-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "bundler-source-psource"
+ class PSource < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ context "with an explicit handler" do
+ before do
+ update_repo2 do
+ build_plugin "another-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Cheater < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+ end
+
+ context "explicit presence in gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ plugin "another-psource"
+
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+ end
+
+ it "completes successfully" do
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "installs the explicit one" do
+ plugin_should_be_installed("another-psource")
+ end
+
+ it "doesn't install the default one" do
+ plugin_should_not_be_installed("bundler-source-psource")
+ end
+ end
+
+ context "explicit default source" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ plugin "bundler-source-psource"
+
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+ end
+
+ it "completes successfully" do
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "installs the default one" do
+ plugin_should_be_installed("bundler-source-psource")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
new file mode 100644
index 0000000000..14d6bb99b9
--- /dev/null
+++ b/spec/bundler/quality_spec.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+if defined?(Encoding) && Encoding.default_external.name != "UTF-8"
+ # Poor man's ruby -E UTF-8, since it works on 1.8.7
+ Encoding.default_external = Encoding.find("UTF-8")
+end
+
+RSpec.describe "The library itself" do
+ def check_for_debugging_mechanisms(filename)
+ debugging_mechanisms_regex = /
+ (binding\.pry)|
+ (debugger)|
+ (sleep\s*\(?\d+)|
+ (fit\s*\(?("|\w))
+ /x
+
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ if line =~ debugging_mechanisms_regex && !line.end_with?("# ignore quality_spec\n")
+ failing_lines << number + 1
+ end
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has debugging mechanisms (like binding.pry, sleep, debugger, rspec focusing, etc.) on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_git_merge_conflicts(filename)
+ merge_conflicts_regex = /
+ <<<<<<<|
+ =======|
+ >>>>>>>
+ /x
+
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ failing_lines << number + 1 if line =~ merge_conflicts_regex
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has unresolved git merge conflicts on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_tab_characters(filename)
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ failing_lines << number + 1 if line =~ /\t/
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has tab characters on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_extra_spaces(filename)
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ next if line =~ /^\s+#.*\s+\n$/
+ next if %w[LICENCE.md].include?(line)
+ failing_lines << number + 1 if line =~ /\s+\n$/
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has spaces on the EOL on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_expendable_words(filename)
+ failing_line_message = []
+ useless_words = %w[
+ actually
+ basically
+ clearly
+ just
+ obviously
+ really
+ simply
+ ]
+ pattern = /\b#{Regexp.union(useless_words)}\b/i
+
+ File.readlines(filename).each_with_index do |line, number|
+ next unless word_found = pattern.match(line)
+ failing_line_message << "#{filename}:#{number.succ} has '#{word_found}'. Avoid using these kinds of weak modifiers."
+ end
+
+ failing_line_message unless failing_line_message.empty?
+ end
+
+ def check_for_specific_pronouns(filename)
+ failing_line_message = []
+ specific_pronouns = /\b(he|she|his|hers|him|her|himself|herself)\b/i
+
+ File.readlines(filename).each_with_index do |line, number|
+ next unless word_found = specific_pronouns.match(line)
+ failing_line_message << "#{filename}:#{number.succ} has '#{word_found}'. Use more generic pronouns in documentation."
+ end
+
+ failing_line_message unless failing_line_message.empty?
+ end
+
+ RSpec::Matchers.define :be_well_formed do
+ match(&:empty?)
+
+ failure_message do |actual|
+ actual.join("\n")
+ end
+ end
+
+ it "has no malformed whitespace" do
+ exempt = /\.gitmodules|\.marshal|fixtures|vendor|ssl_certs|LICENSE|vcr_cassettes/
+ error_messages = []
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_tab_characters(filename)
+ error_messages << check_for_extra_spaces(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "does not include any leftover debugging or development mechanisms" do
+ exempt = %r{quality_spec.rb|support/helpers|vcr_cassettes|\.md|\.ronn}
+ error_messages = []
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_debugging_mechanisms(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "does not include any unresolved merge conflicts" do
+ error_messages = []
+ exempt = %r{lock/lockfile_(bundler_1_)?spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_git_merge_conflicts(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "maintains language quality of the documentation" do
+ included = /ronn/
+ error_messages = []
+ Dir.chdir(root) do
+ `git ls-files -z -- man`.split("\x0").each do |filename|
+ next unless filename =~ included
+ error_messages << check_for_expendable_words(filename)
+ error_messages << check_for_specific_pronouns(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "maintains language quality of sentences used in source code" do
+ error_messages = []
+ exempt = /vendor/
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_expendable_words(filename)
+ error_messages << check_for_specific_pronouns(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "documents all used settings" do
+ exemptions = %w[
+ auto_config_jobs
+ cache_command_is_package
+ console_command
+ deployment_means_frozen
+ forget_cli_options
+ gem.coc
+ gem.mit
+ inline
+ lockfile_uses_separate_rubygems_sources
+ use_gem_version_promoter_for_major_updates
+ viz_command
+ ]
+
+ all_settings = Hash.new {|h, k| h[k] = [] }
+ documented_settings = []
+
+ Bundler::Settings::BOOL_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::BOOL_KEYS" }
+ Bundler::Settings::NUMBER_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::NUMBER_KEYS" }
+ Bundler::Settings::ARRAY_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::ARRAY_KEYS" }
+
+ Dir.chdir(root) do
+ key_pattern = /([a-z\._-]+)/i
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ File.readlines(filename).each_with_index do |line, number|
+ line.scan(/Bundler\.settings\[:#{key_pattern}\]/).flatten.each {|s| all_settings[s] << "referenced at `#{filename}:#{number.succ}`" }
+ end
+ end
+ documented_settings = File.read("man/bundle-config.ronn")[/LIST OF AVAILABLE KEYS.*/m].scan(/^\* `#{key_pattern}`/).flatten
+ end
+
+ documented_settings.each do |s|
+ all_settings.delete(s)
+ expect(exemptions.delete(s)).to be_nil, "setting #{s} was exempted but was actually documented"
+ end
+
+ exemptions.each do |s|
+ expect(all_settings.delete(s)).to be_truthy, "setting #{s} was exempted but unused"
+ end
+ error_messages = all_settings.map do |setting, refs|
+ "The `#{setting}` setting is undocumented\n\t- #{refs.join("\n\t- ")}\n"
+ end
+
+ expect(error_messages.sort).to be_well_formed
+
+ expect(documented_settings).to be_sorted
+ end
+
+ it "can still be built" do
+ Dir.chdir(root) do
+ begin
+ gem_command! :build, gemspec
+ if Bundler.rubygems.provides?(">= 2.4")
+ # there's no way aroudn this warning
+ last_command.stderr.sub!(/^YAML safe loading.*/, "")
+
+ # older rubygems have weird warnings, and we won't actually be using them
+ # to build the gem for releases anyways
+ expect(last_command.stderr).to be_empty, "bundler should build as a gem without warnings, but\n#{err}"
+ end
+ ensure
+ # clean up the .gem generated
+ path_prefix = ruby_core? ? "lib/" : "./"
+ FileUtils.rm("#{path_prefix}bundler-#{Bundler::VERSION}.gem")
+ end
+ end
+ end
+
+ it "does not contain any warnings" do
+ Dir.chdir(root) do
+ exclusions = %w[
+ lib/bundler/capistrano.rb
+ lib/bundler/deployment.rb
+ lib/bundler/gem_tasks.rb
+ lib/bundler/vlad.rb
+ lib/bundler/templates/gems.rb
+ ]
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files = lib_files.split("\x0").grep(/\.rb$/) - exclusions
+ lib_files.reject! {|f| f.start_with?("lib/bundler/vendor") }
+ lib_files.map! {|f| f.chomp(".rb") }
+ sys_exec!("ruby -w -Ilib") do |input, _, _|
+ lib_files.each do |f|
+ input.puts "require '#{f.sub(%r{\Alib/}, "")}'"
+ end
+ end
+
+ expect(last_command.stdboth.split("\n")).to be_well_formed
+ end
+ end
+end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
new file mode 100644
index 0000000000..13527ce5d1
--- /dev/null
+++ b/spec/bundler/realworld/dependency_api_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+RSpec.describe "gemcutter's dependency API", :realworld => true do
+ context "when Gemcutter API takes too long to respond" do
+ before do
+ require_rack
+
+ port = find_unused_port
+ @server_uri = "http://127.0.0.1:#{port}"
+
+ require File.expand_path("../../support/artifice/endpoint_timeout", __FILE__)
+ require "thread"
+ @t = Thread.new do
+ server = Rack::Server.start(:app => EndpointTimeout,
+ :Host => "0.0.0.0",
+ :Port => port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ server.start
+ end
+ @t.run
+
+ wait_for_server("127.0.0.1", port)
+ bundle! "config timeout 1"
+ end
+
+ after do
+ Artifice.deactivate
+ @t.kill
+ @t.join
+ end
+
+ it "times out and falls back on the modern index" do
+ install_gemfile! <<-G, :artifice => nil
+ source "#{@server_uri}"
+ gem "rack"
+ G
+
+ expect(out).to include("Fetching source index from #{@server_uri}/")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+end
diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb
new file mode 100644
index 0000000000..94ab49ba2a
--- /dev/null
+++ b/spec/bundler/realworld/double_check_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec.describe "double checking sources", :realworld => true do
+ it "finds already-installed gems", :ruby => ">= 2.2" do
+ create_file("rails.gemspec", <<-RUBY)
+ Gem::Specification.new do |s|
+ s.name = "rails"
+ s.version = "5.1.4"
+ s.summary = ""
+ s.description = ""
+ s.author = ""
+ s.add_dependency "actionpack", "5.1.4"
+ end
+ RUBY
+
+ create_file("actionpack.gemspec", <<-RUBY)
+ Gem::Specification.new do |s|
+ s.name = "actionpack"
+ s.version = "5.1.4"
+ s.summary = ""
+ s.description = ""
+ s.author = ""
+ s.add_dependency "rack", "~> 2.0.0"
+ end
+ RUBY
+
+ cmd = <<-RUBY
+ require "bundler"
+ require #{File.expand_path("../../support/artifice/vcr.rb", __FILE__).dump}
+ require "bundler/inline"
+ gemfile(true) do
+ source "https://rubygems.org"
+ gem "rails", path: "."
+ end
+ RUBY
+
+ ruby! cmd
+ ruby! cmd
+ end
+end
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
new file mode 100644
index 0000000000..aa60e20b8a
--- /dev/null
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -0,0 +1,382 @@
+# frozen_string_literal: true
+
+RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do
+ def rubygems_version(name, requirement)
+ ruby! <<-RUBY
+ require #{File.expand_path("../../support/artifice/vcr.rb", __FILE__).dump}
+ require "bundler"
+ require "bundler/source/rubygems/remote"
+ require "bundler/fetcher"
+ source = Bundler::Source::Rubygems::Remote.new(URI("https://rubygems.org"))
+ fetcher = Bundler::Fetcher.new(source)
+ index = fetcher.specs([#{name.dump}], nil)
+ rubygem = index.search(Gem::Dependency.new(#{name.dump}, #{requirement.dump})).last
+ if rubygem.nil?
+ raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
+ "Found specs:\n\#{index.send(:specs).inspect}"
+ end
+ "#{name} (\#{rubygem.version})"
+ RUBY
+ end
+
+ # there is no rbx-relative-require gem that will install on 1.9
+ it "ignores extra gems with bad platforms", :ruby => "~> 1.8.7" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem "linecache", "0.46"
+ G
+ bundle :lock
+ expect(err).to lack_errors
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ # https://github.com/bundler/bundler/issues/1202
+ it "bundle cache works with rubygems 1.3.7 and pre gems",
+ :ruby => "~> 1.8.7", :rubygems => "~> 1.3.7" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+ gem "rack", "1.3.0.beta2"
+ gem "will_paginate", "3.0.pre2"
+ G
+ bundle :cache
+ expect(out).not_to include("Removing outdated .gem files from vendor/cache")
+ end
+
+ # https://github.com/bundler/bundler/issues/1486
+ # this is a hash collision that only manifests on 1.8.7
+ it "finds the correct child versions", :ruby => "~> 1.8.7" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'i18n', '~> 0.6.0'
+ gem 'activesupport', '~> 3.0.5'
+ gem 'activerecord', '~> 3.0.5'
+ gem 'builder', '~> 2.1.2'
+ G
+ bundle :lock
+ expect(lockfile).to include("activemodel (3.0.5)")
+ end
+
+ it "resolves dependencies correctly", :ruby => "1.9.3" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'rails', '~> 3.0'
+ gem 'capybara', '~> 2.2.0'
+ gem 'rack-cache', '1.2.0' # last version that works on Ruby 1.9
+ G
+ bundle! :lock
+ expect(lockfile).to include(rubygems_version("rails", "~> 3.0"))
+ expect(lockfile).to include("capybara (2.2.1)")
+ end
+
+ it "installs the latest version of gxapi_rails", :ruby => "1.9.3" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "sass-rails"
+ gem "rails", "~> 3"
+ gem "gxapi_rails", "< 0.1.0" # 0.1.0 was released way after the test was written
+ gem 'rack-cache', '1.2.0' # last version that works on Ruby 1.9
+ G
+ bundle! :lock
+ expect(lockfile).to include("gxapi_rails (0.0.6)")
+ end
+
+ it "installs the latest version of i18n" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "i18n", "~> 0.6.0"
+ gem "activesupport", "~> 3.0"
+ gem "activerecord", "~> 3.0"
+ gem "builder", "~> 2.1.2"
+ G
+ bundle! :lock
+ expect(lockfile).to include(rubygems_version("i18n", "~> 0.6.0"))
+ expect(lockfile).to include(rubygems_version("activesupport", "~> 3.0"))
+ end
+
+ it "is able to update a top-level dependency when there is a conflict on a shared transitive child", :ruby => "2.1" do
+ # from https://github.com/bundler/bundler/issues/5031
+
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'rails', '~> 4.2.7.1'
+ gem 'paperclip', '~> 5.1.0'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ actionmailer (4.2.7.1)
+ actionpack (= 4.2.7.1)
+ actionview (= 4.2.7.1)
+ activejob (= 4.2.7.1)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ actionpack (4.2.7.1)
+ actionview (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ rack (~> 1.6)
+ rack-test (~> 0.6.2)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ builder (~> 3.1)
+ erubis (~> 2.7.0)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ activejob (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ globalid (>= 0.3.0)
+ activemodel (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ builder (~> 3.1)
+ activerecord (4.2.7.1)
+ activemodel (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ arel (~> 6.0)
+ activesupport (4.2.7.1)
+ i18n (~> 0.7)
+ json (~> 1.7, >= 1.7.7)
+ minitest (~> 5.1)
+ thread_safe (~> 0.3, >= 0.3.4)
+ tzinfo (~> 1.1)
+ arel (6.0.3)
+ builder (3.2.2)
+ climate_control (0.0.3)
+ activesupport (>= 3.0)
+ cocaine (0.5.8)
+ climate_control (>= 0.0.3, < 1.0)
+ concurrent-ruby (1.0.2)
+ erubis (2.7.0)
+ globalid (0.3.7)
+ activesupport (>= 4.1.0)
+ i18n (0.7.0)
+ json (1.8.3)
+ loofah (2.0.3)
+ nokogiri (>= 1.5.9)
+ mail (2.6.4)
+ mime-types (>= 1.16, < 4)
+ mime-types (3.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0521)
+ mimemagic (0.3.2)
+ mini_portile2 (2.1.0)
+ minitest (5.9.1)
+ nokogiri (1.6.8)
+ mini_portile2 (~> 2.1.0)
+ pkg-config (~> 1.1.7)
+ paperclip (5.1.0)
+ activemodel (>= 4.2.0)
+ activesupport (>= 4.2.0)
+ cocaine (~> 0.5.5)
+ mime-types
+ mimemagic (~> 0.3.0)
+ pkg-config (1.1.7)
+ rack (1.6.4)
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rails (4.2.7.1)
+ actionmailer (= 4.2.7.1)
+ actionpack (= 4.2.7.1)
+ actionview (= 4.2.7.1)
+ activejob (= 4.2.7.1)
+ activemodel (= 4.2.7.1)
+ activerecord (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.2.7.1)
+ sprockets-rails
+ rails-deprecated_sanitizer (1.0.3)
+ activesupport (>= 4.2.0.alpha)
+ rails-dom-testing (1.0.7)
+ activesupport (>= 4.2.0.beta, < 5.0)
+ nokogiri (~> 1.6.0)
+ rails-deprecated_sanitizer (>= 1.0.1)
+ rails-html-sanitizer (1.0.3)
+ loofah (~> 2.0)
+ railties (4.2.7.1)
+ actionpack (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (11.3.0)
+ sprockets (3.7.0)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.0)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ thor (0.19.1)
+ thread_safe (0.3.5)
+ tzinfo (1.2.2)
+ thread_safe (~> 0.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ paperclip (~> 5.1.0)
+ rails (~> 4.2.7.1)
+ L
+
+ bundle! "lock --update paperclip"
+
+ expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
+ end
+
+ # https://github.com/bundler/bundler/issues/1500
+ it "does not fail install because of gem plugins" do
+ realworld_system_gems("open_gem --version 1.4.2", "rake --version 0.9.2")
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'rack', '1.0.1'
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(err).not_to include("Could not find rake")
+ expect(err).to lack_errors
+ end
+
+ it "checks out git repos when the lockfile is corrupted" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ git_source(:github) {|repo| "https://github.com/\#{repo}.git" }
+
+ gem 'activerecord', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ gem 'activesupport', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ gem 'actionpack', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ G
+
+ lockfile <<-L
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ arel (3.0.2)
+ builder (3.0.0)
+ erubis (2.7.0)
+ hike (1.2.1)
+ i18n (0.6.0)
+ journey (1.0.3)
+ multi_json (1.1.0)
+ rack (1.4.1)
+ rack-cache (1.2)
+ rack (>= 0.4)
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ sprockets (2.1.2)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ tilt (1.3.3)
+ tzinfo (0.3.32)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ actionpack!
+ activerecord!
+ activesupport!
+ L
+
+ bundle! :lock
+ expect(last_command.stderr).to lack_errors
+ end
+
+ it "outputs a helpful error message when gems have invalid gemspecs" do
+ install_gemfile <<-G, :standalone => true
+ source 'https://rubygems.org'
+ gem "resque-scheduler", "2.2.0"
+ G
+ expect(out).to include("You have one or more invalid gemspecs that need to be fixed.")
+ expect(out).to include("resque-scheduler 2.2.0 has an invalid gemspec")
+ end
+end
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
new file mode 100644
index 0000000000..59c1916874
--- /dev/null
+++ b/spec/bundler/realworld/gemfile_source_header_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require "thread"
+
+RSpec.describe "fetching dependencies with a mirrored source", :realworld => true, :rubygems => ">= 2.0" do
+ let(:mirror) { "https://server.example.org" }
+ let(:original) { "http://127.0.0.1:#{@port}" }
+
+ before do
+ setup_server
+ bundle "config --local mirror.#{mirror} #{original}"
+ end
+
+ after do
+ Artifice.deactivate
+ @t.kill
+ @t.join
+ end
+
+ it "sets the 'X-Gemfile-Source' header and bundles successfully" do
+ gemfile <<-G
+ source "#{mirror}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+
+ private
+
+ def setup_server
+ require_rack
+ @port = find_unused_port
+ @server_uri = "http://127.0.0.1:#{@port}"
+
+ require File.expand_path("../../support/artifice/endpoint_mirror_source", __FILE__)
+
+ @t = Thread.new do
+ Rack::Server.start(:app => EndpointMirrorSource,
+ :Host => "0.0.0.0",
+ :Port => @port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ end.run
+
+ wait_for_server("127.0.0.1", @port)
+ end
+end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
new file mode 100644
index 0000000000..ab74886329
--- /dev/null
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+require "thread"
+
+RSpec.describe "fetching dependencies with a not available mirror", :realworld => true do
+ let(:mirror) { @mirror_uri }
+ let(:original) { @server_uri }
+ let(:server_port) { @server_port }
+ let(:host) { "127.0.0.1" }
+
+ before do
+ require_rack
+ setup_server
+ setup_mirror
+ end
+
+ after do
+ Artifice.deactivate
+ @server_thread.kill
+ @server_thread.join
+ end
+
+ context "with a specific fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/__FALLBACK_TIMEOUT/" => "true",
+ "BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a global fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
+ "BUNDLE_MIRROR__ALL" => mirror)
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a specific mirror without a fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
+ end
+
+ it "fails to install the gem with a timeout error" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Fetching source index from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Could not fetch specs from #{mirror}")
+ end
+
+ it "prints each error and warning on a new line" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(last_command.stdout).to include "Fetching source index from #{mirror}/"
+ expect(last_command.bundler_err).to include <<-EOS.strip
+Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Could not fetch specs from #{mirror}/
+ EOS
+ end
+ end
+
+ context "with a global mirror without a fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__ALL" => mirror)
+ end
+
+ it "fails to install the gem with a timeout error" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Fetching source index from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Could not fetch specs from #{mirror}")
+ end
+ end
+
+ def setup_server
+ @server_port = find_unused_port
+ @server_uri = "http://#{host}:#{@server_port}"
+
+ require File.expand_path("../../support/artifice/endpoint", __FILE__)
+
+ @server_thread = Thread.new do
+ Rack::Server.start(:app => Endpoint,
+ :Host => host,
+ :Port => @server_port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ end.run
+
+ wait_for_server(host, @server_port)
+ end
+
+ def setup_mirror
+ mirror_port = find_unused_port
+ @mirror_uri = "http://#{host}:#{mirror_port}"
+ end
+end
diff --git a/spec/bundler/realworld/parallel_spec.rb b/spec/bundler/realworld/parallel_spec.rb
new file mode 100644
index 0000000000..ed4430c68b
--- /dev/null
+++ b/spec/bundler/realworld/parallel_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+RSpec.describe "parallel", :realworld => true, :sometimes => true do
+ it "installs" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.13'
+ gem 'faker', '~> 1.1.2'
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
+ G
+
+ bundle :install, :jobs => 4, :env => { "DEBUG" => "1" }
+
+ if Bundler.rubygems.provides?(">= 2.1.0")
+ expect(out).to match(/[1-3]: /)
+ else
+ expect(out).to include("is not threadsafe")
+ end
+
+ bundle "info activesupport --path"
+ expect(out).to match(/activesupport/)
+
+ bundle "info faker --path"
+ expect(out).to match(/faker/)
+ end
+
+ it "updates" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '3.2.12'
+ gem 'faker', '~> 1.1.2'
+ G
+
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.12'
+ gem 'faker', '~> 1.1.2'
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
+ G
+
+ bundle :update, :jobs => 4, :env => { "DEBUG" => "1" }, :all => bundle_update_requires_all?
+
+ if Bundler.rubygems.provides?(">= 2.1.0")
+ expect(out).to match(/[1-3]: /)
+ else
+ expect(out).to include("is not threadsafe")
+ end
+
+ bundle "info activesupport --path"
+ expect(out).to match(/activesupport-3\.2\.\d+/)
+
+ bundle "info faker --path"
+ expect(out).to match(/faker/)
+ end
+
+ it "works with --standalone" do
+ gemfile <<-G, :standalone => true
+ source "https://rubygems.org"
+ gem "diff-lcs"
+ G
+
+ bundle :install, :standalone => true, :jobs => 4
+
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "diff/lcs"
+ puts Diff::LCS
+ RUBY
+
+ expect(out).to eq("Diff::LCS")
+ end
+end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
new file mode 100644
index 0000000000..c023f5d7aa
--- /dev/null
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -0,0 +1,308 @@
+# frozen_string_literal: true
+
+RSpec.describe "Resolving" do
+ before :each do
+ @index = an_awesome_index
+ end
+
+ it "resolves a single gem" do
+ dep "rack"
+
+ should_resolve_as %w[rack-1.1]
+ end
+
+ it "resolves a gem with dependencies" do
+ dep "actionpack"
+
+ should_resolve_as %w[actionpack-2.3.5 activesupport-2.3.5 rack-1.0]
+ end
+
+ it "resolves a conflicting index" do
+ @index = a_conflict_index
+ dep "my_app"
+ should_resolve_as %w[activemodel-3.2.11 builder-3.0.4 grape-0.2.6 my_app-1.0.0]
+ end
+
+ it "resolves a complex conflicting index" do
+ @index = a_complex_conflict_index
+ dep "my_app"
+ should_resolve_as %w[a-1.4.0 b-0.3.5 c-3.2 d-0.9.8 my_app-1.1.0]
+ end
+
+ it "resolves a index with conflict on child" do
+ @index = index_with_conflict_on_child
+ dep "chef_app"
+ should_resolve_as %w[berkshelf-2.0.7 chef-10.26 chef_app-1.0.0 json-1.7.7]
+ end
+
+ it "prefers expicitly requested dependencies when resolving an index which would otherwise be ambiguous" do
+ @index = an_ambiguous_index
+ dep "a"
+ dep "b"
+ should_resolve_as %w[a-1.0.0 b-2.0.0 c-1.0.0 d-1.0.0]
+ end
+
+ it "prefers non-prerelease resolutions in sort order" do
+ @index = optional_prereleases_index
+ dep "a"
+ dep "b"
+ should_resolve_as %w[a-1.0.0 b-1.5.0]
+ end
+
+ it "resolves a index with root level conflict on child" do
+ @index = a_index_with_root_conflict_on_child
+ dep "i18n", "~> 0.4"
+ dep "activesupport", "~> 3.0"
+ dep "activerecord", "~> 3.0"
+ dep "builder", "~> 2.1.2"
+ should_resolve_as %w[activesupport-3.0.5 i18n-0.4.2 builder-2.1.2 activerecord-3.0.5 activemodel-3.0.5]
+ end
+
+ it "resolves a gem specified with a pre-release version" do
+ dep "activesupport", "~> 3.0.0.beta"
+ dep "activemerchant"
+ should_resolve_as %w[activemerchant-2.3.5 activesupport-3.0.0.beta1]
+ end
+
+ it "doesn't select a pre-release if not specified in the Gemfile" do
+ dep "activesupport"
+ dep "reform"
+ should_resolve_as %w[reform-1.0.0 activesupport-2.3.5]
+ end
+
+ it "doesn't select a pre-release for sub-dependencies" do
+ dep "reform"
+ should_resolve_as %w[reform-1.0.0 activesupport-2.3.5]
+ end
+
+ it "selects a pre-release for sub-dependencies if it's the only option" do
+ dep "need-pre"
+ should_resolve_as %w[need-pre-1.0.0 activesupport-3.0.0.beta1]
+ end
+
+ it "selects a pre-release if it's specified in the Gemfile" do
+ dep "activesupport", "= 3.0.0.beta"
+ dep "actionpack"
+
+ should_resolve_as %w[activesupport-3.0.0.beta actionpack-3.0.0.beta rack-1.1 rack-mount-0.6]
+ end
+
+ it "prefers non-pre-releases when doing conservative updates" do
+ @index = build_index do
+ gem "mail", "2.7.0"
+ gem "mail", "2.7.1.rc1"
+ gem "RubyGems\0", Gem::VERSION
+ end
+ dep "mail"
+ @locked = locked ["mail", "2.7.0"]
+ @base = locked
+ should_conservative_resolve_and_include [:patch], [], ["mail-2.7.0"]
+ end
+
+ it "raises an exception if a child dependency is not resolved" do
+ @index = a_unresovable_child_index
+ dep "chef_app_error"
+ expect do
+ resolve
+ end.to raise_error(Bundler::VersionConflict)
+ end
+
+ it "raises an exception with the minimal set of conflicting dependencies" do
+ @index = build_index do
+ %w[0.9 1.0 2.0].each {|v| gem("a", v) }
+ gem("b", "1.0") { dep "a", ">= 2" }
+ gem("c", "1.0") { dep "a", "< 1" }
+ end
+ dep "a"
+ dep "b"
+ dep "c"
+ expect do
+ resolve
+ end.to raise_error(Bundler::VersionConflict, <<-E.strip)
+Bundler could not find compatible versions for gem "a":
+ In Gemfile:
+ b was resolved to 1.0, which depends on
+ a (>= 2)
+
+ c was resolved to 1.0, which depends on
+ a (< 1)
+ E
+ end
+
+ it "should throw error in case of circular dependencies" do
+ @index = a_circular_index
+ dep "circular_app"
+
+ expect do
+ resolve
+ end.to raise_error(Bundler::CyclicDependencyError, /please remove either gem 'bar' or gem 'foo'/i)
+ end
+
+ # Issue #3459
+ it "should install the latest possible version of a direct requirement with no constraints given" do
+ @index = a_complicated_index
+ dep "foo"
+ should_resolve_and_include %w[foo-3.0.5]
+ end
+
+ # Issue #3459
+ it "should install the latest possible version of a direct requirement with constraints given" do
+ @index = a_complicated_index
+ dep "foo", ">= 3.0.0"
+ should_resolve_and_include %w[foo-3.0.5]
+ end
+
+ it "takes into account required_ruby_version" do
+ @index = build_index do
+ gem "foo", "1.0.0" do
+ dep "bar", ">= 0"
+ end
+
+ gem "foo", "2.0.0" do |s|
+ dep "bar", ">= 0"
+ s.required_ruby_version = "~> 2.0.0"
+ end
+
+ gem "bar", "1.0.0"
+
+ gem "bar", "2.0.0" do |s|
+ s.required_ruby_version = "~> 2.0.0"
+ end
+
+ gem "ruby\0", "1.8.7"
+ end
+ dep "foo"
+ dep "ruby\0", "1.8.7"
+
+ deps = []
+ @deps.each do |d|
+ deps << Bundler::DepProxy.new(d, "ruby")
+ end
+
+ should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]]
+ end
+
+ context "conservative" do
+ before :each do
+ @index = build_index do
+ gem("foo", "1.3.7") { dep "bar", "~> 2.0" }
+ gem("foo", "1.3.8") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.3") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.4") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.5") { dep "bar", "~> 2.1" }
+ gem("foo", "1.5.0") { dep "bar", "~> 2.1" }
+ gem("foo", "1.5.1") { dep "bar", "~> 3.0" }
+ gem("foo", "2.0.0") { dep "bar", "~> 3.0" }
+ gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ end
+ dep "foo"
+
+ # base represents declared dependencies in the Gemfile that are still satisfied by the lockfile
+ @base = Bundler::SpecSet.new([])
+
+ # locked represents versions in lockfile
+ @locked = locked(%w[foo 1.4.3], %w[bar 2.0.3])
+ end
+
+ it "resolves all gems to latest patch" do
+ # strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5
+ should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1]
+ end
+
+ it "resolves all gems to latest patch strict" do
+ # strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5
+ should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5]
+ end
+
+ it "resolves foo only to latest patch - same dependency case" do
+ @locked = locked(%w[foo 1.3.7], %w[bar 2.0.3])
+ # bar is locked, and the lock holds here because the dependency on bar doesn't change on the matching foo version.
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.3.8 bar-2.0.3]
+ end
+
+ it "resolves foo only to latest patch - changing dependency not declared case" do
+ # foo is the only gem being requested for update, therefore bar is locked, but bar is NOT
+ # declared as a dependency in the Gemfile. In this case, locks don't apply to _changing_
+ # dependencies and since the dependency of the selected foo gem changes, the latest matching
+ # dependency of "bar", "~> 2.1" -- bar-2.1.1 -- is selected. This is not a bug and follows
+ # the long-standing documented Conservative Updating behavior of bundle install.
+ # http://bundler.io/v1.12/man/bundle-install.1.html#CONSERVATIVE-UPDATING
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.5 bar-2.1.1]
+ end
+
+ it "resolves foo only to latest patch - changing dependency declared case" do
+ # bar is locked AND a declared dependency in the Gemfile, so it will not move, and therefore
+ # foo can only move up to 1.4.4.
+ @base << build_spec("bar", "2.0.3").first
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.4 bar-2.0.3]
+ end
+
+ it "resolves foo only to latest patch strict" do
+ # adding strict helps solve the possibly unexpected behavior of bar changing in the prior test case,
+ # because no versions will be returned for bar ~> 2.1, so the engine falls back to ~> 2.0 (turn on
+ # debugging to see this happen).
+ should_conservative_resolve_and_include [:patch, :strict], ["foo"], %w[foo-1.4.4 bar-2.0.3]
+ end
+
+ it "resolves bar only to latest patch" do
+ # bar is locked, so foo can only go up to 1.4.4
+ should_conservative_resolve_and_include :patch, ["bar"], %w[foo-1.4.3 bar-2.0.5]
+ end
+
+ it "resolves all gems to latest minor" do
+ # strict is not set, so bar goes up a major version due to dependency from foo 1.4.5
+ should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0]
+ end
+
+ it "resolves all gems to latest minor strict" do
+ # strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version
+ should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1]
+ end
+
+ it "resolves all gems to latest major" do
+ should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0]
+ end
+
+ it "resolves all gems to latest major strict" do
+ should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0]
+ end
+
+ # Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass
+ # by reverting the dependency, the author of foo could release a new gem with an older requirement.
+ context "revert to previous" do
+ before :each do
+ @index = build_index do
+ gem("foo", "1.4.3") { dep "bar", "~> 2.2" }
+ gem("foo", "1.4.4") { dep "bar", "~> 2.1.0" }
+ gem("foo", "1.5.0") { dep "bar", "~> 2.0.0" }
+ gem "bar", %w[2.0.5 2.1.1 2.2.3]
+ end
+ dep "foo"
+
+ # base represents declared dependencies in the Gemfile that are still satisfied by the lockfile
+ @base = Bundler::SpecSet.new([])
+
+ # locked represents versions in lockfile
+ @locked = locked(%w[foo 1.4.3], %w[bar 2.2.3])
+ end
+
+ it "could revert to a previous version level patch" do
+ should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1]
+ end
+
+ it "cannot revert to a previous version in strict mode level patch" do
+ # fall back to the locked resolution since strict means we can't regress either version
+ should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ end
+
+ it "could revert to a previous version level minor" do
+ should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5]
+ end
+
+ it "cannot revert to a previous version in strict mode level minor" do
+ # fall back to the locked resolution since strict means we can't regress either version
+ should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
new file mode 100644
index 0000000000..fee0cf1f1c
--- /dev/null
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+RSpec.describe "Resolving platform craziness" do
+ describe "with cross-platform gems" do
+ before :each do
+ @index = an_awesome_index
+ end
+
+ it "resolves a simple multi platform gem" do
+ dep "nokogiri"
+ platforms "ruby", "java"
+
+ should_resolve_as %w[nokogiri-1.4.2 nokogiri-1.4.2-java weakling-0.0.3]
+ end
+
+ it "doesn't pull gems that don't exist for the current platform" do
+ dep "nokogiri"
+ platforms "ruby"
+
+ should_resolve_as %w[nokogiri-1.4.2]
+ end
+
+ it "doesn't pull gems when the version is available for all requested platforms" do
+ dep "nokogiri"
+ platforms "mswin32"
+
+ should_resolve_as %w[nokogiri-1.4.2.1-x86-mswin32]
+ end
+ end
+
+ describe "with mingw32" do
+ before :each do
+ @index = build_index do
+ platforms "mingw32 mswin32 x64-mingw32" do |platform|
+ gem "thin", "1.2.7", platform
+ end
+ gem "win32-api", "1.5.1", "universal-mingw32"
+ end
+ end
+
+ it "finds mswin gems" do
+ # win32 is hardcoded to get CPU x86 in rubygems
+ platforms "mswin32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-x86-mswin32]
+ end
+
+ it "finds mingw gems" do
+ # mingw is _not_ hardcoded to add CPU x86 in rubygems
+ platforms "x86-mingw32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-mingw32]
+ end
+
+ it "finds x64-mingw gems" do
+ platforms "x64-mingw32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-x64-mingw32]
+ end
+
+ it "finds universal-mingw gems on x86-mingw" do
+ platform "x86-mingw32"
+ dep "win32-api"
+ should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
+ end
+
+ it "finds universal-mingw gems on x64-mingw" do
+ platform "x64-mingw32"
+ dep "win32-api"
+ should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
+ end
+ end
+
+ describe "with conflicting cases" do
+ before :each do
+ @index = build_index do
+ gem "foo", "1.0.0" do
+ dep "bar", ">= 0"
+ end
+
+ gem "bar", "1.0.0" do
+ dep "baz", "~> 1.0.0"
+ end
+
+ gem "bar", "1.0.0", "java" do
+ dep "baz", " ~> 1.1.0"
+ end
+
+ gem "baz", %w[1.0.0 1.1.0 1.2.0]
+ end
+ end
+
+ it "reports on the conflict" do
+ platforms "ruby", "java"
+ dep "foo"
+
+ should_conflict_on "baz"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
new file mode 100644
index 0000000000..dcee234e15
--- /dev/null
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+RSpec.describe "Running bin/* commands" do
+ before :each do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "runs the bundled command when in the bundle" do
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "allows the location of the gem stubs to be specified" do
+ bundle! "binstubs rack", :path => "gbin"
+
+ expect(bundled_app("bin")).not_to exist
+ expect(bundled_app("gbin/rackup")).to exist
+
+ gembin bundled_app("gbin/rackup")
+ expect(out).to eq("1.0.0")
+ end
+
+ it "allows absolute paths as a specification of where to install bin stubs" do
+ bundle! "binstubs rack", :path => tmp("bin")
+
+ gembin tmp("bin/rackup")
+ expect(out).to eq("1.0.0")
+ end
+
+ it "uses the default ruby install name when shebang is not specified" do
+ bundle! "binstubs rack"
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}\n")
+ end
+
+ it "allows the name of the shebang executable to be specified" do
+ bundle! "binstubs rack", :shebang => "ruby-foo"
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env ruby-foo\n")
+ end
+
+ it "runs the bundled command when out of the bundle" do
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ Dir.chdir(tmp) do
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ it "works with gems in path" do
+ build_lib "rack", :path => lib_path("rack") do |s|
+ s.executables = "rackup"
+ end
+
+ gemfile <<-G
+ gem "rack", :path => "#{lib_path("rack")}"
+ G
+
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ gembin "rackup"
+ expect(out).to eq("1.0")
+ end
+
+ it "creates a bundle binstub" do
+ build_gem "bundler", Bundler::VERSION, :to_system => true do |s|
+ s.executables = "bundle"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bundler"
+ G
+
+ bundle! "binstubs bundler"
+
+ expect(bundled_app("bin/bundle")).to exist
+ end
+
+ it "does not generate bin stubs if the option was not specified" do
+ bundle! "install"
+
+ expect(bundled_app("bin/rackup")).not_to exist
+ end
+
+ it "allows you to stop installing binstubs", :bundler => "< 2" do
+ bundle! "install --binstubs bin/"
+ bundled_app("bin/rackup").rmtree
+ bundle! "install --binstubs \"\""
+
+ expect(bundled_app("bin/rackup")).not_to exist
+
+ bundle! "config bin"
+ expect(out).to include("You have not configured a value for `bin`")
+ end
+
+ it "remembers that the option was specified", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle! :install, forgotten_command_line_options([:binstubs, :bin] => "bin")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ gem "rack"
+ G
+
+ bundle "install"
+
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "rewrites bins on --binstubs (to maintain backwards compatibility)", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, forgotten_command_line_options([:binstubs, :bin] => "bin")
+
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ bundle "install"
+
+ expect(bundled_app("bin/rackup").read).to_not eq("OMG")
+ end
+
+ it "rewrites bins on binstubs (to maintain backwards compatibility)" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ create_file("bin/rackup", "OMG")
+
+ bundle! "binstubs rack"
+
+ expect(bundled_app("bin/rackup").read).to_not eq("OMG")
+ end
+
+ it "use BUNDLE_GEMFILE gemfile for binstub" do
+ # context with bin/bunlder w/ default Gemfile
+ bundle! "binstubs bundler"
+
+ # generate other Gemfile with executable gem
+ build_repo2 do
+ build_gem("bindir") {|s| s.executables = "foo" }
+ end
+
+ create_file("OtherGemfile", <<-G)
+ source "file://#{gem_repo2}"
+ gem 'bindir'
+ G
+
+ # generate binstub for executable from non default Gemfile (other then bin/bundler version)
+ ENV["BUNDLE_GEMFILE"] = "OtherGemfile"
+ bundle "install"
+ bundle! "binstubs bindir"
+
+ # remove user settings
+ ENV["BUNDLE_GEMFILE"] = nil
+
+ # run binstub for non default Gemfile
+ gembin "foo"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("1.0")
+ end
+end
diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb
new file mode 100644
index 0000000000..0510c80632
--- /dev/null
+++ b/spec/bundler/runtime/gem_tasks_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+RSpec.describe "require 'bundler/gem_tasks'", :ruby_repo do
+ before :each do
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write <<-GEMSPEC
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ end
+ GEMSPEC
+ end
+ bundled_app("Rakefile").open("w") do |f|
+ f.write <<-RAKEFILE
+ $:.unshift("#{bundler_path}")
+ require "bundler/gem_tasks"
+ RAKEFILE
+ end
+ end
+
+ it "includes the relevant tasks" do
+ with_gem_path_as(Spec::Path.base_system_gems.to_s) do
+ sys_exec "ruby -S rake -T"
+ end
+
+ expect(err).to eq("")
+ expected_tasks = [
+ "rake build",
+ "rake clean",
+ "rake clobber",
+ "rake install",
+ "rake release[remote]",
+ ]
+ tasks = out.lines.to_a.map {|s| s.split("#").first.strip }
+ expect(tasks & expected_tasks).to eq(expected_tasks)
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "adds 'pkg' to rake/clean's CLOBBER" do
+ with_gem_path_as(Spec::Path.base_system_gems.to_s) do
+ sys_exec! %('#{Gem.ruby}' -S rake -e 'load "Rakefile"; puts CLOBBER.inspect')
+ end
+ expect(last_command.stdout).to eq '["pkg"]'
+ end
+end
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
new file mode 100644
index 0000000000..18ca246199
--- /dev/null
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler/inline#gemfile" do
+ def script(code, options = {})
+ requires = ["bundler/inline"]
+ requires.unshift File.expand_path("../../support/artifice/" + options.delete(:artifice) + ".rb", __FILE__) if options.key?(:artifice)
+ requires = requires.map {|r| "require '#{r}'" }.join("\n")
+ @out = ruby("#{requires}\n\n" + code, options)
+ end
+
+ before :each do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/baz.rb", "puts 'baz'"
+ s.write "lib/qux.rb", "puts 'qux'"
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", "puts 'two'"
+ s.add_dependency "three", "= 1.0.0"
+ end
+
+ build_lib "three", "1.0.0" do |s|
+ s.write "lib/three.rb", "puts 'three'"
+ s.add_dependency "seven", "= 1.0.0"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ build_lib "five", "1.0.0", :no_default => true do |s|
+ s.write "lib/mofive.rb", "puts 'five'"
+ end
+
+ build_lib "six", "1.0.0" do |s|
+ s.write "lib/six.rb", "puts 'six'"
+ end
+
+ build_lib "seven", "1.0.0" do |s|
+ s.write "lib/seven.rb", "puts 'seven'"
+ end
+
+ build_lib "eight", "1.0.0" do |s|
+ s.write "lib/eight.rb", "puts 'eight'"
+ end
+ end
+
+ it "requires the gems" do
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+ RUBY
+
+ expect(out).to eq("two")
+ expect(exitstatus).to be_zero if exitstatus
+
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "eleven"
+ end
+ end
+
+ puts "success"
+ RUBY
+
+ expect(err).to include "Could not find gem 'eleven'"
+ expect(out).not_to include "success"
+
+ script <<-RUBY
+ gemfile(true) do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Rack's post install message")
+ expect(exitstatus).to be_zero if exitstatus
+
+ script <<-RUBY, :artifice => "endpoint"
+ gemfile(true) do
+ source "https://notaserver.com"
+ gem "activesupport", :require => true
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ err.gsub! %r{.*lib/sinatra/base\.rb:\d+: warning: constant ::Fixnum is deprecated$}, ""
+ err.strip!
+ expect(err).to lack_errors
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "lets me use my own ui object" do
+ script <<-RUBY, :artifice => "endpoint"
+ require 'bundler'
+ class MyBundlerUI < Bundler::UI::Silent
+ def confirm(msg, newline = nil)
+ puts "CONFIRMED!"
+ end
+ end
+ gemfile(true, :ui => MyBundlerUI.new) do
+ source "https://notaserver.com"
+ gem "activesupport", :require => true
+ end
+ RUBY
+
+ expect(out).to eq("CONFIRMED!\nCONFIRMED!")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "raises an exception if passed unknown arguments" do
+ script <<-RUBY
+ gemfile(true, :arglebargle => true) do
+ path "#{lib_path}"
+ gem "two"
+ end
+
+ puts "success"
+ RUBY
+ expect(err).to include "Unknown options: arglebargle"
+ expect(out).not_to include "success"
+ end
+
+ it "does not mutate the option argument" do
+ script <<-RUBY
+ require 'bundler'
+ options = { :ui => Bundler::UI::Shell.new }
+ gemfile(false, options) do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+ puts "OKAY" if options.key?(:ui)
+ RUBY
+
+ expect(out).to match("OKAY")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs quietly if necessary when the install option is not set" do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs quietly from git if necessary when the install option is not set" do
+ build_git "foo", "1.0.0"
+ baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
+ script <<-RUBY
+ gemfile do
+ gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
+ gem "baz", :git => #{lib_path("baz-2.0.0").to_s.dump}, :ref => #{baz_ref.dump}
+ end
+
+ puts FOO
+ puts BAZ
+ RUBY
+
+ expect(out).to eq("1.0.0\n2.0.0")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "allows calling gemfile twice" do
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+
+ gemfile do
+ path "#{lib_path}" do
+ gem "four"
+ end
+ end
+ RUBY
+
+ expect(out).to eq("two\nfour")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when a Gemfile.lock is present" do
+ gemfile <<-G
+ source "https://notaserver.com"
+ gem "rake"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (11.3.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rake
+
+ BUNDLED WITH
+ 1.13.6
+ G
+
+ in_app_root do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+ end
+
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when BUNDLE_GEMFILE is set to an empty string" do
+ ENV["BUNDLE_GEMFILE"] = ""
+
+ in_app_root do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+ end
+
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when BUNDLE_BIN is set" do
+ ENV["BUNDLE_BIN"] = "/usr/local/bundle/bin"
+
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack" # has the rackup executable
+ end
+
+ puts RACK
+ RUBY
+ expect(last_command).to be_success
+ expect(last_command.stdout).to eq "1.0.0"
+ end
+end
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
new file mode 100644
index 0000000000..b74dbde3f6
--- /dev/null
+++ b/spec/bundler/runtime/load_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.load" do
+ describe "with a gemfile" do
+ before(:each) do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "provides a list of the env dependencies" do
+ expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ end
+
+ it "provides a list of the resolved gems" do
+ expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ end
+
+ it "ignores blank BUNDLE_GEMFILEs" do
+ expect do
+ ENV["BUNDLE_GEMFILE"] = ""
+ Bundler.load
+ end.not_to raise_error
+ end
+ end
+
+ describe "with a gems.rb file" do
+ before(:each) do
+ create_file "gems.rb", <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle! :install
+ end
+
+ it "provides a list of the env dependencies" do
+ expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ end
+
+ it "provides a list of the resolved gems" do
+ expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ end
+ end
+
+ describe "without a gemfile" do
+ it "raises an exception if the default gemfile is not found" do
+ expect do
+ Bundler.load
+ end.to raise_error(Bundler::GemfileNotFound, /could not locate gemfile/i)
+ end
+
+ it "raises an exception if a specified gemfile is not found" do
+ expect do
+ ENV["BUNDLE_GEMFILE"] = "omg.rb"
+ Bundler.load
+ end.to raise_error(Bundler::GemfileNotFound, /omg\.rb/)
+ end
+
+ it "does not find a Gemfile above the testing directory" do
+ bundler_gemfile = tmp.join("../Gemfile")
+ unless File.exist?(bundler_gemfile)
+ FileUtils.touch(bundler_gemfile)
+ @remove_bundler_gemfile = true
+ end
+ begin
+ expect { Bundler.load }.to raise_error(Bundler::GemfileNotFound)
+ ensure
+ bundler_gemfile.rmtree if @remove_bundler_gemfile
+ end
+ end
+ end
+
+ describe "when called twice" do
+ it "doesn't try to load the runtime twice" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ G
+
+ ruby! <<-RUBY
+ require "bundler"
+ Bundler.setup :default
+ Bundler.require :default
+ puts RACK
+ begin
+ require "activesupport"
+ rescue LoadError
+ puts "no activesupport"
+ end
+ RUBY
+
+ expect(out.split("\n")).to eq(["1.0.0", "no activesupport"])
+ end
+ end
+
+ describe "not hurting brittle rubygems" do
+ it "does not inject #source into the generated YAML of the gem specs" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "activerecord"
+ G
+
+ Bundler.load.specs.each do |spec|
+ expect(spec.to_yaml).not_to match(/^\s+source:/)
+ expect(spec.to_yaml).not_to match(/^\s+groups:/)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
new file mode 100644
index 0000000000..eecf162427
--- /dev/null
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.setup with multi platform stuff" do
+ it "raises a friendly error when gems are missing locally" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0)
+
+ PLATFORMS
+ #{local_tag}
+
+ DEPENDENCIES
+ rack
+ G
+
+ ruby <<-R
+ begin
+ require 'bundler'
+ Bundler.setup
+ rescue Bundler::GemNotFound => e
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "will resolve correctly on the current platform when the lockfile was targeted for a different one" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ nokogiri (1.4.2-java)
+ weakling (= 0.0.3)
+ weakling (0.0.3)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ nokogiri
+ G
+
+ simulate_platform "x86-darwin-10"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "will add the resolve for the current platform" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ nokogiri (1.4.2-java)
+ weakling (= 0.0.3)
+ weakling (0.0.3)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ nokogiri
+ G
+
+ simulate_platform "x86-darwin-100"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100"
+ end
+
+ it "allows specifying only-ruby-platform" do
+ simulate_platform "java"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ bundle! "config force_ruby_platform true"
+
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY"
+ end
+
+ it "allows specifying only-ruby-platform on windows with dependency platforms" do
+ simulate_windows do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platforms => [:mingw, :mswin, :x64_mingw, :jruby]
+ gem "platform_specific"
+ G
+
+ bundle! "config force_ruby_platform true"
+
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
+ end
+ end
+
+ it "recovers when the lockfile is missing a platform-specific gem" do
+ build_repo2 do
+ build_gem "requires_platform_specific" do |s|
+ s.add_dependency "platform_specific"
+ end
+ end
+ simulate_windows x64_mingw do
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+ platform_specific (1.0-x86-mingw32)
+ requires_platform_specific (1.0)
+ platform_specific
+
+ PLATFORMS
+ x64-mingw32
+ x86-mingw32
+
+ DEPENDENCIES
+ requires_platform_specific
+ L
+
+ install_gemfile! <<-G, :verbose => true
+ source "file://#{gem_repo2}"
+ gem "requires_platform_specific"
+ G
+
+ expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
new file mode 100644
index 0000000000..0484e38845
--- /dev/null
+++ b/spec/bundler/runtime/require_spec.rb
@@ -0,0 +1,452 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.require" do
+ before :each do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/baz.rb", "puts 'baz'"
+ s.write "lib/qux.rb", "puts 'qux'"
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", "puts 'two'"
+ s.add_dependency "three", "= 1.0.0"
+ end
+
+ build_lib "three", "1.0.0" do |s|
+ s.write "lib/three.rb", "puts 'three'"
+ s.add_dependency "seven", "= 1.0.0"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ build_lib "five", "1.0.0", :no_default => true do |s|
+ s.write "lib/mofive.rb", "puts 'five'"
+ end
+
+ build_lib "six", "1.0.0" do |s|
+ s.write "lib/six.rb", "puts 'six'"
+ end
+
+ build_lib "seven", "1.0.0" do |s|
+ s.write "lib/seven.rb", "puts 'seven'"
+ end
+
+ build_lib "eight", "1.0.0" do |s|
+ s.write "lib/eight.rb", "puts 'eight'"
+ end
+
+ build_lib "nine", "1.0.0" do |s|
+ s.write "lib/nine.rb", "puts 'nine'"
+ end
+
+ build_lib "ten", "1.0.0" do |s|
+ s.write "lib/ten.rb", "puts 'ten'"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "one", :group => :bar, :require => %w[baz qux]
+ gem "two"
+ gem "three", :group => :not
+ gem "four", :require => false
+ gem "five"
+ gem "six", :group => "string"
+ gem "seven", :group => :not
+ gem "eight", :require => true, :group => :require_true
+ env "BUNDLER_TEST" => "nine" do
+ gem "nine", :require => true
+ end
+ gem "ten", :install_if => lambda { ENV["BUNDLER_TEST"] == "ten" }
+ end
+ G
+ end
+
+ it "requires the gems" do
+ # default group
+ run "Bundler.require"
+ expect(out).to eq("two")
+
+ # specific group
+ run "Bundler.require(:bar)"
+ expect(out).to eq("baz\nqux")
+
+ # default and specific group
+ run "Bundler.require(:default, :bar)"
+ expect(out).to eq("baz\nqux\ntwo")
+
+ # specific group given as a string
+ run "Bundler.require('bar')"
+ expect(out).to eq("baz\nqux")
+
+ # specific group declared as a string
+ run "Bundler.require(:string)"
+ expect(out).to eq("six")
+
+ # required in resolver order instead of gemfile order
+ run("Bundler.require(:not)")
+ expect(out.split("\n").sort).to eq(%w[seven three])
+
+ # test require: true
+ run "Bundler.require(:require_true)"
+ expect(out).to eq("eight")
+ end
+
+ it "allows requiring gems with non standard names explicitly" do
+ run "Bundler.require ; require 'mofive'"
+ expect(out).to eq("two\nfive")
+ end
+
+ it "allows requiring gems which are scoped by env" do
+ ENV["BUNDLER_TEST"] = "nine"
+ run "Bundler.require"
+ expect(out).to eq("two\nnine")
+ end
+
+ it "allows requiring gems which are scoped by install_if" do
+ ENV["BUNDLER_TEST"] = "ten"
+ run "Bundler.require"
+ expect(out).to eq("two\nten")
+ end
+
+ it "raises an exception if a require is specified but the file does not exist" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "two", :require => 'fail'
+ end
+ G
+
+ load_error_run <<-R, "fail"
+ Bundler.require
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "displays a helpful message if the required gem throws an error" do
+ build_lib "faulty", "1.0.0" do |s|
+ s.write "lib/faulty.rb", "raise RuntimeError.new(\"Gem Internal Error Message\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "faulty"
+ end
+ G
+
+ run "Bundler.require"
+ expect(err).to match("error while trying to load the gem 'faulty'")
+ expect(err).to match("Gem Internal Error Message")
+ end
+
+ it "doesn't swallow the error when the library has an unrelated error" do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "loadfuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR: \#{e.message}"
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ end
+
+ describe "with namespaced gems" do
+ before :each do
+ build_lib "jquery-rails", "1.0.0" do |s|
+ s.write "lib/jquery/rails.rb", "puts 'jquery/rails'"
+ end
+ lib_path("jquery-rails-1.0.0/lib/jquery-rails.rb").rmtree
+ end
+
+ it "requires gem names that are namespaced" do
+ gemfile <<-G
+ path '#{lib_path}' do
+ gem 'jquery-rails'
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("jquery/rails")
+ end
+
+ it "silently passes if the require fails" do
+ build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s|
+ s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'"
+ end
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "bcrypt-ruby"
+ end
+ G
+
+ cmd = <<-RUBY
+ require 'bundler'
+ Bundler.require
+ RUBY
+ ruby(cmd)
+
+ expect(err).to lack_errors
+ end
+
+ it "does not mangle explicitly given requires" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem 'jquery-rails', :require => 'jquery-rails'
+ end
+ G
+
+ load_error_run <<-R, "jquery-rails"
+ Bundler.require
+ R
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "handles the case where regex fails" do
+ build_lib "load-fuuu", "1.0.0" do |s|
+ s.write "lib/load-fuuu.rb", "raise LoadError.new(\"Could not open library 'libfuuu-1.0': libfuuu-1.0: cannot open shared object file: No such file or directory.\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "load-fuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'")
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "doesn't swallow the error when the library has an unrelated error" do
+ build_lib "load-fuuu", "1.0.0" do |s|
+ s.write "lib/load/fuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")"
+ end
+ lib_path("load-fuuu-1.0.0/lib/load-fuuu.rb").rmtree
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "load-fuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR: \#{e.message}"
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ end
+ end
+
+ describe "using bundle exec" do
+ it "requires the locked gems" do
+ bundle "exec ruby -e 'Bundler.require'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("two")
+
+ bundle "exec ruby -e 'Bundler.require(:bar)'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("baz\nqux")
+
+ bundle "exec ruby -e 'Bundler.require(:default, :bar)'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("baz\nqux\ntwo")
+ end
+ end
+
+ describe "order" do
+ before(:each) do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/one.rb", <<-ONE
+ if defined?(Two)
+ Two.two
+ else
+ puts "two_not_loaded"
+ end
+ puts 'one'
+ ONE
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", <<-TWO
+ module Two
+ def self.two
+ puts 'module_two'
+ end
+ end
+ puts 'two'
+ TWO
+ end
+ end
+
+ it "works when the gems are in the Gemfile in the correct order" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "two"
+ gem "one"
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("two\nmodule_two\none")
+ end
+
+ describe "a gem with different requires for different envs" do
+ before(:each) do
+ build_gem "multi_gem", :to_bundle => true do |s|
+ s.write "lib/one.rb", "puts 'ONE'"
+ s.write "lib/two.rb", "puts 'TWO'"
+ end
+
+ install_gemfile <<-G
+ gem "multi_gem", :require => "one", :group => :one
+ gem "multi_gem", :require => "two", :group => :two
+ G
+ end
+
+ it "requires both with Bundler.require(both)" do
+ run "Bundler.require(:one, :two)"
+ expect(out).to eq("ONE\nTWO")
+ end
+
+ it "requires one with Bundler.require(:one)" do
+ run "Bundler.require(:one)"
+ expect(out).to eq("ONE")
+ end
+
+ it "requires :two with Bundler.require(:two)" do
+ run "Bundler.require(:two)"
+ expect(out).to eq("TWO")
+ end
+ end
+
+ it "fails when the gems are in the Gemfile in the wrong order" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "one"
+ gem "two"
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("two_not_loaded\none\ntwo")
+ end
+
+ describe "with busted gems" do
+ it "should be busted" do
+ build_gem "busted_require", :to_bundle => true do |s|
+ s.write "lib/busted_require.rb", "require 'no_such_file_omg'"
+ end
+
+ install_gemfile <<-G
+ gem "busted_require"
+ G
+
+ load_error_run <<-R, "no_such_file_omg"
+ Bundler.require
+ R
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+ end
+ end
+
+ it "does not load rubygems gemspecs that are used", :rubygems => ">= 2.5.2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ run! <<-R
+ path = File.join(Gem.dir, "specifications", "rack-1.0.0.gemspec")
+ contents = File.read(path)
+ contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join
+ File.open(path, "w") do |f|
+ f.write contents
+ end
+ R
+
+ run! <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not load git gemspecs that are used", :rubygems => ">= 2.5.2" do
+ build_git "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ path = Gem.loaded_specs["foo"].loaded_from
+ contents = File.read(path)
+ contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join
+ File.open(path, "w") do |f|
+ f.write contents
+ end
+ R
+
+ run! <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+end
+
+RSpec.describe "Bundler.require with platform specific dependencies" do
+ it "does not require the gems that are pinned to other platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{not_local_tag} do
+ gem "fail", :require => "omgomg"
+ end
+
+ gem "rack", "1.0.0"
+ G
+
+ run "Bundler.require"
+ expect(err).to lack_errors
+ end
+
+ it "requires gems pinned to multiple platforms, including the current one" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{not_local_tag}, :#{local_tag} do
+ gem "rack", :require => "rack"
+ end
+ G
+
+ run "Bundler.require; puts RACK"
+
+ expect(out).to eq("1.0.0")
+ expect(err).to lack_errors
+ end
+end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
new file mode 100644
index 0000000000..646227d931
--- /dev/null
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -0,0 +1,1445 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.setup" do
+ describe "with no arguments" do
+ it "makes all groups available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :test
+ G
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ describe "when called with groups" do
+ before(:each) do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack", :group => :test
+ G
+ end
+
+ it "doesn't make all groups available" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default)
+
+ begin
+ require 'rack'
+ rescue LoadError
+ puts "WIN"
+ end
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("WIN")
+ end
+
+ it "accepts string for group name" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default, 'test')
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+
+ it "leaves all groups available if they were already" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ Bundler.setup(:default)
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+
+ it "leaves :default available if setup is called twice" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default)
+ Bundler.setup(:default, :test)
+
+ begin
+ require 'yard'
+ puts "WIN"
+ rescue LoadError
+ puts "FAIL"
+ end
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to match("WIN")
+ end
+
+ it "handles multiple non-additive invocations" do
+ ruby <<-RUBY
+ require 'bundler'
+ Bundler.setup(:default, :test)
+ Bundler.setup(:default)
+ require 'rack'
+
+ puts "FAIL"
+ RUBY
+
+ expect(err).to match("rack")
+ expect(err).to match("LoadError")
+ expect(out).not_to match("FAIL")
+ end
+ end
+
+ context "load order" do
+ def clean_load_path(lp)
+ without_bundler_load_path = ruby!("puts $LOAD_PATH").split("\n")
+ lp = lp - [
+ bundler_path.to_s,
+ bundler_path.join("gems/bundler-#{Bundler::VERSION}/lib").to_s,
+ tmp("rubygems/lib").to_s,
+ root.join("../lib").expand_path.to_s,
+ ] - without_bundler_load_path
+ lp.map! {|p| p.sub(/^#{Regexp.union system_gem_path.to_s, default_bundle_path.to_s}/i, "") }
+ end
+
+ it "puts loaded gems after -I and RUBYLIB", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ENV["RUBYOPT"] = "-Idash_i_dir"
+ ENV["RUBYLIB"] = "rubylib_dir"
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = out.split("\n")
+ rack_load_order = load_path.index {|path| path.include?("rack") }
+
+ expect(err).to eq("")
+ expect(load_path[1]).to include "dash_i_dir"
+ expect(load_path[2]).to include "rubylib_dir"
+ expect(rack_load_order).to be > 0
+ end
+
+ it "orders the load path correctly when there are dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ ruby! <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = clean_load_path(out.split("\n"))
+
+ unless Bundler.load.specs["bundler"].empty?
+ load_path.delete_if {|path| path =~ /bundler/ }
+ end
+
+ expect(load_path).to start_with(
+ "/gems/rails-2.3.2/lib",
+ "/gems/activeresource-2.3.2/lib",
+ "/gems/activerecord-2.3.2/lib",
+ "/gems/actionpack-2.3.2/lib",
+ "/gems/actionmailer-2.3.2/lib",
+ "/gems/activesupport-2.3.2/lib",
+ "/gems/rake-10.0.2/lib"
+ )
+ end
+
+ it "falls back to order the load path alphabetically for backwards compatibility" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "weakling"
+ gem "duradura"
+ gem "terranova"
+ G
+
+ ruby! <<-RUBY
+ require 'rubygems'
+ require 'bundler/setup'
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = clean_load_path(out.split("\n"))
+
+ expect(load_path).to start_with(
+ "/gems/weakling-0.0.3/lib",
+ "/gems/terranova-8/lib",
+ "/gems/duradura-7.0/lib"
+ )
+ end
+ end
+
+ it "raises if the Gemfile was not yet installed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Bundler::GemNotFound
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "doesn't create a Gemfile.lock if the setup fails" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ Bundler.setup
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "doesn't change the Gemfile.lock if the setup fails" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "nosuchgem", "10.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ Bundler.setup
+ R
+
+ expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile)
+ end
+
+ it "makes a Gemfile.lock if setup succeeds" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ describe "$BUNDLE_GEMFILE" do
+ context "user provides an absolute path" do
+ it "uses BUNDLE_GEMFILE to locate the gemfile if present" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile bundled_app("4realz"), <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", "2.3.5"
+ G
+
+ ENV["BUNDLE_GEMFILE"] = bundled_app("4realz").to_s
+ bundle :install
+
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+ end
+
+ context "an absolute path is not provided" do
+ it "uses BUNDLE_GEMFILE to locate the gemfile if present" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "install"
+ bundle "install --deployment"
+
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "WIN"
+ rescue ArgumentError => e
+ puts "FAIL"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+ end
+ end
+
+ it "prioritizes gems in BUNDLE_PATH over gems in GEM_HOME" do
+ ENV["BUNDLE_PATH"] = bundled_app(".bundle").to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ G
+
+ build_gem "rack", "1.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "integrate with rubygems" do
+ describe "by replacing #gem" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+ end
+
+ it "replaces #gem but raises when the gem is missing" do
+ run <<-R
+ begin
+ gem "activesupport"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+ when gem is missing" do
+ run <<-R
+ begin
+ gem "activesupport"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+
+ it "replaces #gem but raises when the version is wrong" do
+ run <<-R
+ begin
+ gem "rack", "1.0.0"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+ when the version is wrong" do
+ run <<-R
+ begin
+ gem "rack", "1.0.0"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "by hiding system gems" do
+ before :each do
+ system_gems "activesupport-2.3.5"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ G
+ end
+
+ it "removes system gems from Gem.source_index" do
+ run "require 'yard'"
+ expect(out).to eq("bundler-#{Bundler::VERSION}\nyard-1.0")
+ end
+
+ context "when the ruby stdlib is a substring of Gem.path" do
+ it "does not reject the stdlib from $LOAD_PATH" do
+ substring = "/" + $LOAD_PATH.find {|p| p =~ /vendor_ruby/ }.split("/")[2]
+ run "puts 'worked!'", :env => { "GEM_PATH" => substring }
+ expect(out).to eq("worked!")
+ end
+ end
+ end
+ end
+
+ describe "with paths" do
+ it "activates the gems in the path source" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "puts 'WIN'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ path "#{lib_path("rack-1.0.0")}" do
+ gem "rack"
+ end
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN")
+ end
+ end
+
+ describe "with git" do
+ before do
+ build_git "rack", "1.0.0"
+
+ gemfile <<-G
+ gem "rack", :git => "#{lib_path("rack-1.0.0")}"
+ G
+ end
+
+ it "provides a useful exception when the git repo is not checked out yet" do
+ run "1"
+ expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i)
+ end
+
+ it "does not hit the git binary if the lockfile is available and up to date" do
+ bundle "install"
+
+ break_git!
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "WIN"
+ rescue Exception => e
+ puts "FAIL"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides a good exception if the lockfile is unavailable" do
+ bundle "install"
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ break_git!
+
+ ruby <<-R
+ require "rubygems"
+ require "bundler"
+
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Bundler::GitError => e
+ puts e.message
+ end
+ R
+
+ run "puts 'FAIL'"
+
+ expect(err).not_to include "This is not the git you are looking for"
+ end
+
+ it "works even when the cache directory has been deleted" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ FileUtils.rm_rf vendored_gems("cache")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ with_read_only("**/*") do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "finds git gem when default bundle path becomes read only" do
+ bundle "install"
+
+ with_read_only("#{Bundler.bundle_path}/**/*") do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ describe "when specifying local override" do
+ it "explodes if given path does not exist on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ FileUtils.rm_rf(lib_path("local-rack"))
+ run "require 'rack'"
+ expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ end
+
+ it "explodes if branch is not given on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ run "require 'rack'"
+ expect(err).to match(/because :branch is not specified in Gemfile/)
+ end
+
+ it "explodes on different branches on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed"
+ G
+
+ run "require 'rack'"
+ expect(err).to match(/is using branch master but Gemfile specifies changed/)
+ end
+
+ it "explodes on refs with different branches on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+ expect(err).to match(/is using branch master but Gemfile specifies nonexistant/)
+ end
+ end
+
+ describe "when excluding groups" do
+ it "doesn't change the resolve if --without is used" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ install_gems "activesupport-2.3.5"
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => :default
+ end
+
+ it "remembers --without and does not bail on bare Bundler.setup" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ install_gems "activesupport-2.3.5"
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "remembers --without and does not include groups passed to Bundler.setup" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rack do
+ gem "rack"
+ end
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ expect(the_bundle).not_to include_gems "activesupport 2.3.2", :groups => :rack
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => :rack
+ end
+ end
+
+ # Unfortunately, gem_prelude does not record the information about
+ # activated gems, so this test cannot work on 1.9 :(
+ if RUBY_VERSION < "1.9"
+ describe "preactivated gems" do
+ it "raises an exception if a pre activated gem conflicts with the bundle" do
+ system_gems "thin-1.0", "rack-1.0.0"
+ build_gem "thin", "1.1", :to_system => true do |s|
+ s.add_dependency "rack"
+ end
+
+ gemfile <<-G
+ gem "thin", "1.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ gem "thin"
+ require 'bundler'
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Gem::LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(out).to eq("You have already activated thin 1.1, but your Gemfile requires thin 1.0. Prepending `bundle exec` to your command may solve this.")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+" do
+ system_gems "thin-1.0", "rack-1.0.0"
+ build_gem "thin", "1.1", :to_system => true do |s|
+ s.add_dependency "rack"
+ end
+
+ gemfile <<-G
+ gem "thin", "1.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ gem "thin"
+ require 'bundler'
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Gem::LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+ end
+ end
+
+ # RubyGems returns loaded_from as a string
+ it "has loaded_from as a string on all specs" do
+ build_git "foo"
+ build_git "no-gemspec", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
+ G
+
+ run <<-R
+ Gem.loaded_specs.each do |n, s|
+ puts "FAIL" unless s.loaded_from.is_a?(String)
+ end
+ R
+
+ expect(out).to be_empty
+ end
+
+ it "does not load all gemspecs", :rubygems => ">= 2.3" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ run! <<-R
+ File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f|
+ f.write <<-RUBY
+# -*- encoding: utf-8 -*-
+# stub: broken 1.0.0 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "broken"
+ s.version = "1.0.0"
+ raise "BROKEN GEMSPEC"
+end
+ RUBY
+ end
+ R
+
+ run! <<-R
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "ignores empty gem paths" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ENV["GEM_HOME"] = ""
+ bundle %(exec ruby -e "require 'set'"), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(err).to lack_errors
+ end
+
+ describe "$MANPATH" do
+ before do
+ build_repo4 do
+ build_gem "with_man" do |s|
+ s.write("man/man1/page.1", "MANPAGE")
+ end
+ end
+ end
+
+ context "when the user has one set" do
+ before { ENV["MANPATH"] = "/foo:" }
+
+ it "adds the gem's man dir to the MANPATH" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "with_man"
+ G
+
+ run! "puts ENV['MANPATH']"
+ expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}:/foo")
+ end
+ end
+
+ context "when the user does not have one set" do
+ before { ENV.delete("MANPATH") }
+
+ it "adds the gem's man dir to the MANPATH" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "with_man"
+ G
+
+ run! "puts ENV['MANPATH']"
+ expect(out).to eq(default_bundle_path("gems/with_man-1.0/man").to_s)
+ end
+ end
+ end
+
+ it "should prepend gemspec require paths to $LOAD_PATH in order" do
+ update_repo2 do
+ build_gem("requirepaths") do |s|
+ s.write("lib/rq.rb", "puts 'yay'")
+ s.write("src/rq.rb", "puts 'nooo'")
+ s.require_paths = %w[lib src]
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "requirepaths", :require => nil
+ G
+
+ run "require 'rq'"
+ expect(out).to eq("yay")
+ end
+
+ it "should clean $LOAD_PATH properly", :ruby_repo do
+ gem_name = "very_simple_binary"
+ full_gem_name = gem_name + "-1.0"
+ ext_dir = File.join(tmp "extenstions", full_gem_name)
+
+ install_gem full_gem_name
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ ruby <<-R
+ if Gem::Specification.method_defined? :extension_dir
+ s = Gem::Specification.find_by_name '#{gem_name}'
+ s.extension_dir = '#{ext_dir}'
+
+ # Don't build extensions.
+ s.class.send(:define_method, :build_extensions) { nil }
+ end
+
+ require 'bundler'
+ gem '#{gem_name}'
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2
+
+ Bundler.setup
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} == 0
+ R
+
+ expect(out).to eq("true\ntrue")
+ end
+
+ context "with bundler is located in symlinked GEM_HOME" do
+ let(:gem_home) { Dir.mktmpdir }
+ let(:symlinked_gem_home) { Tempfile.new("gem_home") }
+ let(:bundler_dir) { ruby_core? ? File.expand_path("../../../..", __FILE__) : File.expand_path("../../..", __FILE__) }
+ let(:bundler_lib) { File.join(bundler_dir, "lib") }
+
+ before do
+ FileUtils.ln_sf(gem_home, symlinked_gem_home.path)
+ gems_dir = File.join(gem_home, "gems")
+ specifications_dir = File.join(gem_home, "specifications")
+ Dir.mkdir(gems_dir)
+ Dir.mkdir(specifications_dir)
+
+ FileUtils.ln_s(bundler_dir, File.join(gems_dir, "bundler-#{Bundler::VERSION}"))
+
+ gemspec_file = ruby_core? ? "#{bundler_dir}/lib/bundler.gemspec" : "#{bundler_dir}/bundler.gemspec"
+ gemspec = File.read(gemspec_file).
+ sub("Bundler::VERSION", %("#{Bundler::VERSION}"))
+ gemspec = gemspec.lines.reject {|line| line =~ %r{lib/bundler/version} }.join
+
+ File.open(File.join(specifications_dir, "bundler.gemspec"), "wb") do |f|
+ f.write(gemspec)
+ end
+ end
+
+ it "should succesfully require 'bundler/setup'", :ruby_repo do
+ install_gemfile ""
+
+ ENV["GEM_PATH"] = symlinked_gem_home.path
+
+ ruby <<-R
+ if $LOAD_PATH.include?("#{bundler_lib}")
+ # We should use bundler from GEM_PATH for this test, so we should
+ # remove path to the bundler source tree
+ $LOAD_PATH.delete("#{bundler_lib}")
+ else
+ raise "We don't have #{bundler_lib} in $LOAD_PATH"
+ end
+ puts (require 'bundler/setup')
+ R
+
+ expect(out).to eql("true")
+ end
+ end
+
+ it "stubs out Gem.refresh so it does not reveal system gems" do
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ run <<-R
+ puts Bundler.rubygems.find_name("rack").inspect
+ Gem.refresh
+ puts Bundler.rubygems.find_name("rack").inspect
+ R
+
+ expect(out).to eq("[]\n[]")
+ end
+
+ describe "when a vendored gem specification uses the :path option" do
+ it "should resolve paths relative to the Gemfile" do
+ path = bundled_app(File.join("vendor", "foo"))
+ build_lib "foo", :path => path
+
+ # If the .gemspec exists, then Bundler handles the path differently.
+ # See Source::Path.load_spec_files for details.
+ FileUtils.rm(File.join(path, "foo.gemspec"))
+
+ install_gemfile <<-G
+ gem 'foo', '1.2.3', :path => 'vendor/foo'
+ G
+
+ Dir.chdir(bundled_app.parent) do
+ run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") }
+ require 'foo'
+ R
+ end
+ expect(err).to lack_errors
+ end
+
+ it "should make sure the Bundler.root is really included in the path relative to the Gemfile" do
+ relative_path = File.join("vendor", Dir.pwd[1..-1], "foo")
+ absolute_path = bundled_app(relative_path)
+ FileUtils.mkdir_p(absolute_path)
+ build_lib "foo", :path => absolute_path
+
+ # If the .gemspec exists, then Bundler handles the path differently.
+ # See Source::Path.load_spec_files for details.
+ FileUtils.rm(File.join(absolute_path, "foo.gemspec"))
+
+ gemfile <<-G
+ gem 'foo', '1.2.3', :path => '#{relative_path}'
+ G
+
+ bundle :install
+
+ Dir.chdir(bundled_app.parent) do
+ run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") }
+ require 'foo'
+ R
+ end
+
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "with git gems that don't have gemspecs" do
+ before :each do
+ build_git "no-gemspec", :gemspec => false
+
+ install_gemfile <<-G
+ gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
+ G
+ end
+
+ it "loads the library via a virtual spec" do
+ run <<-R
+ require 'no-gemspec'
+ puts NOGEMSPEC
+ R
+
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "with bundled and system gems" do
+ before :each do
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "activesupport", "2.3.5"
+ G
+ end
+
+ it "does not pull in system gems" do
+ run <<-R
+ require 'rubygems'
+
+ begin;
+ require 'rack'
+ rescue LoadError
+ puts 'WIN'
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides a gem method" do
+ run <<-R
+ gem 'activesupport'
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(out).to eq("2.3.5")
+ end
+
+ it "raises an exception if gem is used to invoke a system gem not in the bundle" do
+ run <<-R
+ begin
+ gem 'rack'
+ rescue LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.")
+ end
+
+ it "sets GEM_HOME appropriately" do
+ run "puts ENV['GEM_HOME']"
+ expect(out).to eq(default_bundle_path.to_s)
+ end
+ end
+
+ describe "with system gems in the bundle" do
+ before :each do
+ bundle! "config path.system true"
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ gem "activesupport", "2.3.5"
+ G
+ end
+
+ it "sets GEM_PATH appropriately" do
+ run "puts Gem.path"
+ paths = out.split("\n")
+ expect(paths).to include(system_gem_path.to_s)
+ end
+ end
+
+ describe "with a gemspec that requires other files" do
+ before :each do
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ lib = File.expand_path('../lib/', __FILE__)
+ $:.unshift lib unless $:.include?(lib)
+ require 'bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'no one'
+ end
+ G
+ end
+
+ gemfile <<-G
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+ end
+
+ it "evals each gemspec in the context of its parent directory" do
+ bundle :install
+ run "require 'bar'; puts BAR"
+ expect(out).to eq("1.0")
+ end
+
+ it "error intelligently if the gemspec has a LoadError" do
+ ref = update_git "bar", :gemspec => false do |s|
+ s.write "bar.gemspec", "require 'foobarbaz'"
+ end.ref_for("HEAD")
+ bundle :install
+
+ expect(out.lines.map(&:chomp)).to include(
+ a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"),
+ RUBY_VERSION >= "1.9" ? a_string_starting_with("Does it try to require a relative path? That's been removed in Ruby 1.9.") : "",
+ " # from #{default_bundle_path "bundler", "gems", "bar-1.0-#{ref[0, 12]}", "bar.gemspec"}:1",
+ " > require 'foobarbaz'"
+ )
+ end
+
+ it "evals each gemspec with a binding from the top level" do
+ bundle "install"
+
+ ruby <<-RUBY
+ require 'bundler'
+ def Bundler.require(path)
+ raise "LOSE"
+ end
+ Bundler.load
+ RUBY
+
+ expect(err).to lack_errors
+ expect(out).to eq("")
+ end
+ end
+
+ describe "when Bundler is bundled" do
+ it "doesn't blow up" do
+ install_gemfile <<-G
+ gem "bundler", :path => "#{File.expand_path("..", lib)}"
+ G
+
+ bundle %(exec ruby -e "require 'bundler'; Bundler.setup"), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "when BUNDLED WITH" do
+ def lock_with(bundler_version = nil)
+ lock = <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if bundler_version
+ lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ end
+
+ lock
+ end
+
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ lockfile lock_with(nil)
+ ruby "require 'bundler/setup'"
+ lockfile_should_be lock_with(nil)
+ end
+ end
+
+ context "is newer" do
+ it "does not change the lock or warn" do
+ lockfile lock_with(Bundler::VERSION.succ)
+ ruby "require 'bundler/setup'"
+ expect(out).to eq("")
+ expect(err).to eq("")
+ lockfile_should_be lock_with(Bundler::VERSION.succ)
+ end
+ end
+
+ context "is older" do
+ it "does not change the lock" do
+ lockfile lock_with("1.10.1")
+ ruby "require 'bundler/setup'"
+ lockfile_should_be lock_with("1.10.1")
+ end
+ end
+ end
+
+ describe "when RUBY VERSION" do
+ let(:ruby_version) { nil }
+
+ def lock_with(ruby_version = nil)
+ lock = <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if ruby_version
+ lock += "\n RUBY VERSION\n ruby #{ruby_version}\n"
+ end
+
+ lock += <<-L
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ normalize_uri_file(lock)
+ end
+
+ before do
+ install_gemfile <<-G
+ ruby ">= 0"
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ lockfile lock_with(ruby_version)
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ end
+ end
+
+ context "is newer" do
+ let(:ruby_version) { "5.5.5" }
+ it "does not change the lock or warn" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ expect(out).to eq("")
+ expect(err).to eq("")
+ end
+ end
+
+ context "is older" do
+ let(:ruby_version) { "1.0.0" }
+ it "does not change the lock" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ end
+ end
+ end
+
+ describe "with gemified standard libraries" do
+ it "does not load Psych", :ruby => "~> 2.2" do
+ gemfile ""
+ ruby <<-RUBY
+ require 'bundler/setup'
+ puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
+ require 'psych'
+ puts Psych::VERSION
+ RUBY
+ pre_bundler, post_bundler = out.split("\n")
+ expect(pre_bundler).to eq("undefined")
+ expect(post_bundler).to match(/\d+\.\d+\.\d+/)
+ end
+
+ it "does not load openssl" do
+ install_gemfile! ""
+ ruby! <<-RUBY
+ require "bundler/setup"
+ puts defined?(OpenSSL) || "undefined"
+ require "openssl"
+ puts defined?(OpenSSL) || "undefined"
+ RUBY
+ expect(out).to eq("undefined\nconstant")
+ end
+
+ describe "default gem activation" do
+ let(:exemptions) do
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7") || ENV["RGV"] == "master"
+ []
+ else
+ %w[io-console openssl]
+ end << "bundler"
+ end
+
+ let(:activation_warning_hack) { strip_whitespace(<<-RUBY) }
+ require #{spec_dir.join("support/hax").to_s.dump}
+ require "rubygems"
+
+ if Gem::Specification.instance_methods.map(&:to_sym).include?(:activate)
+ Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate)
+ Gem::Specification.send(:define_method, :activate) do
+ unless #{exemptions.inspect}.include?(name)
+ warn '-' * 80
+ warn "activating \#{full_name}"
+ warn *caller
+ warn '*' * 80
+ end
+ bundler_spec_activate
+ end
+ end
+ RUBY
+
+ let(:activation_warning_hack_rubyopt) do
+ create_file("activation_warning_hack.rb", activation_warning_hack)
+ "-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}"
+ end
+
+ let(:code) { strip_whitespace(<<-RUBY) }
+ require "bundler/setup"
+ require "pp"
+ loaded_specs = Gem.loaded_specs.dup
+ #{exemptions.inspect}.each {|s| loaded_specs.delete(s) }
+ pp loaded_specs
+
+ # not a default gem, but harmful to have loaded
+ open_uri = $LOADED_FEATURES.grep(/open.uri/)
+ unless open_uri.empty?
+ warn "open_uri: \#{open_uri}"
+ end
+ RUBY
+
+ it "activates no gems with -rbundler/setup" do
+ install_gemfile! ""
+ ruby! code, :env => { :RUBYOPT => activation_warning_hack_rubyopt }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ it "activates no gems with bundle exec" do
+ install_gemfile! ""
+ # ensure we clean out the default gems, bceause bundler's allowed to be activated
+ create_file("script.rb", code)
+ bundle! "exec ruby ./script.rb", :env => { :RUBYOPT => activation_warning_hack_rubyopt + " -rbundler/setup" }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ it "activates no gems with bundle exec that is loaded" do
+ # TODO: remove once https://github.com/erikhuda/thor/pull/539 is released
+ exemptions << "io-console"
+
+ install_gemfile! ""
+ create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}")
+ FileUtils.chmod(0o777, bundled_app("script.rb"))
+ bundle! "exec ./script.rb", :artifice => nil, :env => { :RUBYOPT => activation_warning_hack_rubyopt }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ let(:default_gems) do
+ ruby!(<<-RUBY).split("\n")
+ if Gem::Specification.is_a?(Enumerable)
+ puts Gem::Specification.select(&:default_gem?).map(&:name)
+ end
+ RUBY
+ end
+
+ it "activates newer versions of default gems" do
+ build_repo4 do
+ default_gems.each do |g|
+ build_gem g, "999999"
+ end
+ end
+
+ default_gems.reject! {|g| exemptions.include?(g) }
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ #{default_gems}.each do |g|
+ gem g, "999999"
+ end
+ G
+
+ expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 999999" })
+ end
+
+ it "activates older versions of default gems" do
+ build_repo4 do
+ default_gems.each do |g|
+ build_gem g, "0.0.0.a"
+ end
+ end
+
+ default_gems.reject! {|g| exemptions.include?(g) }
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ #{default_gems}.each do |g|
+ gem g, "0.0.0.a"
+ end
+ G
+
+ expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 0.0.0.a" })
+ end
+ end
+ end
+
+ describe "after setup" do
+ it "allows calling #gem on random objects", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby! <<-RUBY
+ require "bundler/setup"
+ Object.new.gem "rack"
+ puts Gem.loaded_specs["rack"].full_name
+ RUBY
+
+ expect(out).to eq("rack-1.0.0")
+ end
+
+ it "keeps Kernel#gem private", :bundler => "2" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ Object.new.gem "rack"
+ puts "FAIL"
+ RUBY
+
+ expect(last_command.stdboth).not_to include "FAIL"
+ expect(last_command.stderr).to include "private method `gem'"
+ end
+
+ it "keeps Kernel#require private" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ Object.new.require "rack"
+ puts "FAIL"
+ RUBY
+
+ expect(last_command.stdboth).not_to include "FAIL"
+ expect(last_command.stderr).to include "private method `require'"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/with_clean_env_spec.rb b/spec/bundler/runtime/with_clean_env_spec.rb
new file mode 100644
index 0000000000..321f5b6415
--- /dev/null
+++ b/spec/bundler/runtime/with_clean_env_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.with_env helpers" do
+ def bundle_exec_ruby!(code, *args)
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ env = opts[:env] ||= {}
+ env[:RUBYOPT] ||= "-r#{spec_dir.join("support/hax")}"
+ args.push opts
+ bundle! "exec '#{Gem.ruby}' -e #{code}", *args
+ end
+
+ describe "Bundler.original_env" do
+ before do
+ bundle "config path vendor/bundle"
+ gemfile ""
+ bundle "install"
+ end
+
+ it "should return the PATH present before bundle was activated" do
+ code = "print Bundler.original_env['PATH']"
+ path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo"
+ with_path_as(path) do
+ bundle_exec_ruby!(code.dump)
+ expect(last_command.stdboth).to eq(path)
+ end
+ end
+
+ it "should return the GEM_PATH present before bundle was activated" do
+ code = "print Bundler.original_env['GEM_PATH']"
+ gem_path = ENV["GEM_PATH"] + ":/foo"
+ with_gem_path_as(gem_path) do
+ bundle_exec_ruby!(code.dump)
+ expect(last_command.stdboth).to eq(gem_path)
+ end
+ end
+
+ it "works with nested bundle exec invocations", :ruby_repo do
+ create_file("exe.rb", <<-'RB')
+ count = ARGV.first.to_i
+ exit if count < 0
+ STDERR.puts "#{count} #{ENV["PATH"].end_with?(":/foo")}"
+ if count == 2
+ ENV["PATH"] = "#{ENV["PATH"]}:/foo"
+ end
+ exec(Gem.ruby, __FILE__, (count - 1).to_s)
+ RB
+ path = `getconf PATH`.strip + File::PATH_SEPARATOR + File.dirname(Gem.ruby)
+ with_path_as(path) do
+ bundle! "exec '#{Gem.ruby}' #{bundled_app("exe.rb")} 2", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ end
+ expect(err).to eq <<-EOS.strip
+2 false
+1 true
+0 true
+ EOS
+ end
+
+ it "removes variables that bundler added", :ruby_repo do
+ original = ruby!('puts ENV.to_a.map {|e| e.join("=") }.sort.join("\n")', :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" })
+ code = 'puts Bundler.original_env.to_a.map {|e| e.join("=") }.sort.join("\n")'
+ bundle! "exec '#{Gem.ruby}' -e #{code.dump}", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq original
+ end
+ end
+
+ describe "Bundler.clean_env", :bundler => "< 2" do
+ before do
+ bundle "config path vendor/bundle"
+ gemfile ""
+ bundle "install"
+ end
+
+ it "should delete BUNDLE_PATH" do
+ code = "print Bundler.clean_env.has_key?('BUNDLE_PATH')"
+ ENV["BUNDLE_PATH"] = "./foo"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq "false"
+ end
+
+ it "should remove '-rbundler/setup' from RUBYOPT" do
+ code = "print Bundler.clean_env['RUBYOPT']"
+ ENV["RUBYOPT"] = "-W2 -rbundler/setup"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).not_to include("-rbundler/setup")
+ end
+
+ it "should clean up RUBYLIB", :ruby_repo do
+ code = "print Bundler.clean_env['RUBYLIB']"
+ ENV["RUBYLIB"] = root.join("lib").to_s + File::PATH_SEPARATOR + "/foo"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq("/foo")
+ end
+
+ it "should restore the original MANPATH" do
+ code = "print Bundler.clean_env['MANPATH']"
+ ENV["MANPATH"] = "/foo"
+ ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq("/foo-original")
+ end
+ end
+
+ describe "Bundler.with_original_env" do
+ it "should set ENV to original_env in the block" do
+ expected = Bundler.original_env
+ actual = Bundler.with_original_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_original_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.with_clean_env", :bundler => "< 2" do
+ it "should set ENV to clean_env in the block" do
+ expected = Bundler.clean_env
+ actual = Bundler.with_clean_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_clean_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.clean_system", :ruby => ">= 1.9", :bundler => "< 2" do
+ it "runs system inside with_clean_env" do
+ Bundler.clean_system(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh))
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+
+ describe "Bundler.clean_exec", :ruby => ">= 1.9", :bundler => "< 2" do
+ it "runs exec inside with_clean_env" do
+ pid = Kernel.fork do
+ Bundler.clean_exec(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh))
+ end
+ Process.wait(pid)
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
new file mode 100644
index 0000000000..d24bd4505b
--- /dev/null
+++ b/spec/bundler/spec_helper.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+$:.unshift File.expand_path("..", __FILE__)
+$:.unshift File.expand_path("../../lib", __FILE__)
+
+require "rubygems"
+module Gem
+ if defined?(@path_to_default_spec_map)
+ @path_to_default_spec_map.delete_if do |_path, spec|
+ spec.name == "bundler"
+ end
+ end
+end
+
+begin
+ require File.expand_path("../support/path.rb", __FILE__)
+ spec = Gem::Specification.load(Spec::Path.gemspec.to_s)
+ rspec = spec.dependencies.find {|d| d.name == "rspec" }
+ gem "rspec", rspec.requirement.to_s
+ require "rspec"
+ require "diff/lcs"
+rescue LoadError
+ abort "Run rake spec:deps to install development dependencies"
+end
+
+require "bundler/psyched_yaml"
+require "bundler/vendored_fileutils"
+require "uri"
+require "digest"
+
+
+# Delete the default copy of Bundler that RVM installs for us when running in CI
+require "fileutils"
+if ENV.select {|k, _v| k =~ /TRAVIS/ }.any? && Gem::Version.new(Gem::VERSION) > Gem::Version.new("2.0")
+ Dir.glob(File.join(Gem::Specification.default_specifications_dir, "bundler*.gemspec")).each do |file|
+ FileUtils.rm_rf(file)
+ end
+
+ Dir.glob(File.join(RbConfig::CONFIG["sitelibdir"], "bundler*")).each do |file|
+ FileUtils.rm_rf(file)
+ end
+end
+
+if File.expand_path(__FILE__) =~ %r{([^\w/\.:\-])}
+ abort "The bundler specs cannot be run from a path that contains special characters (particularly #{$1.inspect})"
+end
+
+require "bundler"
+
+Dir["#{File.expand_path("../support", __FILE__)}/*.rb"].each do |file|
+ file = file.gsub(%r{\A#{Regexp.escape File.expand_path("..", __FILE__)}/}, "")
+ require file unless file.end_with?("hax.rb")
+end
+
+$debug = false
+
+Spec::Manpages.setup
+Spec::Rubygems.setup
+FileUtils.rm_rf(Spec::Path.gem_repo1)
+ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -r#{Spec::Path.spec_dir}/support/hax.rb"
+ENV["BUNDLE_SPEC_RUN"] = "true"
+
+# Don't wrap output in tests
+ENV["THOR_COLUMNS"] = "10000"
+
+Spec::CodeClimate.setup
+
+module Gem
+ def self.ruby=(ruby)
+ @ruby = ruby
+ end
+end
+
+RSpec.configure do |config|
+ config.include Spec::Builders
+ config.include Spec::Helpers
+ config.include Spec::Indexes
+ config.include Spec::Matchers
+ config.include Spec::Path
+ config.include Spec::Rubygems
+ config.include Spec::Platforms
+ config.include Spec::Sudo
+ config.include Spec::Permissions
+
+ # Enable flags like --only-failures and --next-failure
+ config.example_status_persistence_file_path = ".rspec_status"
+
+ config.disable_monkey_patching!
+
+ # Since failures cause us to keep a bunch of long strings in memory, stop
+ # once we have a large number of failures (indicative of core pieces of
+ # bundler being broken) so that running the full test suite doesn't take
+ # forever due to memory constraints
+ config.fail_fast ||= 25 if ENV["CI"]
+
+ if ENV["BUNDLER_SUDO_TESTS"] && Spec::Sudo.present?
+ config.filter_run :sudo => true
+ else
+ config.filter_run_excluding :sudo => true
+ end
+
+ if ENV["BUNDLER_REALWORLD_TESTS"]
+ config.filter_run :realworld => true
+ else
+ config.filter_run_excluding :realworld => true
+ end
+
+ git_version = Bundler::Source::Git::GitProxy.new(nil, nil, nil).version
+
+ config.filter_run_excluding :ruby => LessThanProc.with(RUBY_VERSION)
+ config.filter_run_excluding :rubygems => LessThanProc.with(Gem::VERSION)
+ config.filter_run_excluding :git => LessThanProc.with(git_version)
+ config.filter_run_excluding :rubygems_master => (ENV["RGV"] != "master")
+ config.filter_run_excluding :bundler => LessThanProc.with(Bundler::VERSION.split(".")[0, 2].join("."))
+ config.filter_run_excluding :ruby_repo => !(ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]).nil?
+
+ config.filter_run_when_matching :focus unless ENV["CI"]
+
+ original_wd = Dir.pwd
+ original_env = ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) }
+
+ config.expect_with :rspec do |c|
+ c.syntax = :expect
+ end
+
+ config.before :suite do
+ if ENV["BUNDLE_RUBY"]
+ @orig_ruby = Gem.ruby
+ Gem.ruby = ENV["BUNDLE_RUBY"]
+ end
+ end
+
+ config.before :all do
+ build_repo1
+ end
+
+ config.before :each do
+ reset!
+ system_gems []
+ in_app_root
+ @command_executions = []
+ end
+
+ config.after :each do |example|
+ all_output = @command_executions.map(&:to_s_verbose).join("\n\n")
+ if example.exception && !all_output.empty?
+ warn all_output unless config.formatters.grep(RSpec::Core::Formatters::DocumentationFormatter).empty?
+ message = example.exception.message + "\n\nCommands:\n#{all_output}"
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
+ end
+ end
+
+ Dir.chdir(original_wd)
+ ENV.replace(original_env)
+ end
+
+ config.after :suite do
+ Gem.ruby = @orig_ruby if ENV["BUNDLE_RUBY"]
+ end
+end
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
new file mode 100644
index 0000000000..01e8eb7837
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/compact_index*/lib")].first.to_s
+require "compact_index"
+
+class CompactIndexAPI < Endpoint
+ helpers do
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read))
+ end
+
+ def etag_response
+ response_body = yield
+ checksum = Digest(:MD5).hexdigest(response_body)
+ return if not_modified?(checksum)
+ headers "ETag" => quote(checksum)
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ rescue => e
+ puts e
+ puts e.backtrace
+ raise
+ end
+
+ def not_modified?(checksum)
+ etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
+
+ return unless etags.include?(checksum)
+ headers "ETag" => quote(checksum)
+ status 304
+ body ""
+ end
+
+ def requested_range_for(response_body)
+ ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
+
+ if ranges
+ status 206
+ body ranges.map! {|range| slice_body(response_body, range) }.join
+ else
+ status 200
+ body response_body
+ end
+ end
+
+ def quote(string)
+ %("#{string}")
+ end
+
+ def parse_etags(value)
+ value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
+ end
+
+ def slice_body(body, range)
+ if body.respond_to?(:byteslice)
+ body.byteslice(range)
+ else # pre-1.9.3
+ body.unpack("@#{range.first}a#{range.end + 1}").first
+ end
+ end
+
+ def gems(gem_repo = GEM_REPO)
+ @gems ||= {}
+ @gems[gem_repo] ||= begin
+ specs = Bundler::Deprecate.skip_during do
+ %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
+ load_spec(name, version, platform, gem_repo)
+ end
+ end.flatten
+ end
+
+ specs.group_by(&:name).map do |name, versions|
+ gem_versions = versions.map do |spec|
+ deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
+ reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
+ CompactIndex::Dependency.new(d.name, reqs)
+ end
+ checksum = begin
+ Digest::SHA256.file("#{GEM_REPO}/gems/#{spec.original_name}.gem").base64digest
+ rescue
+ nil
+ end
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version, spec.required_rubygems_version)
+ end
+ CompactIndex::Gem.new(name, gem_versions)
+ end
+ end
+ end
+ end
+
+ get "/names" do
+ etag_response do
+ CompactIndex.names(gems.map(&:name))
+ end
+ end
+
+ get "/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexAPI)
diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb
new file mode 100644
index 0000000000..d4e68c38e8
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_api_missing.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexApiMissing < CompactIndexAPI
+ get "/fetch/actual/gem/:id" do
+ $stderr.puts params[:id]
+ if params[:id] == "rack-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexApiMissing)
diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
new file mode 100644
index 0000000000..97aa6cbd84
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexBasicAuthentication < CompactIndexAPI
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
new file mode 100644
index 0000000000..62feb9f164
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexChecksumMismatch < CompactIndexAPI
+ get "/versions" do
+ headers "ETag" => quote("123")
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ body ""
+ end
+end
+
+Artifice.activate_with(CompactIndexChecksumMismatch)
diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
new file mode 100644
index 0000000000..972ecb88b7
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexConcurrentDownload < CompactIndexAPI
+ get "/versions" do
+ versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
+
+ # Verify the original (empty) content hasn't been deleted, e.g. on a retry
+ File.read(versions) == "" || raise("Original file should be present and empty")
+
+ # Verify this is only requested once for a partial download
+ env["HTTP_RANGE"] || raise("Missing Range header for expected partial download")
+
+ # Overwrite the file in parallel, which should be then overwritten
+ # after a successful download to prevent corruption
+ File.open(versions, "w") {|f| f.puts "another process" }
+
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexConcurrentDownload)
diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
new file mode 100644
index 0000000000..0d349bcc1e
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexCredsDiffHost < CompactIndexAPI
+ helpers do
+ def auth
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ end
+
+ def authorized?
+ auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w[user pass]
+ end
+
+ def protected!
+ return if authorized?
+ response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth")
+ throw(:halt, [401, "Not authorized\n"])
+ end
+ end
+
+ before do
+ protected! unless request.path_info.include?("/no/creds/")
+ end
+
+ get "/gems/:id" do
+ redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ end
+
+ get "/no/creds/:id" do
+ if request.host.include?("diffhost") && !auth.provided?
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexCredsDiffHost)
diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb
new file mode 100644
index 0000000000..84d1859235
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtra < CompactIndexAPI
+ get "/extra/versions" do
+ halt 404
+ end
+
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(CompactIndexExtra)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb
new file mode 100644
index 0000000000..903aa900fb
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_api.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtraApi < CompactIndexAPI
+ get "/extra/names" do
+ etag_response do
+ CompactIndex.names(gems(gem_repo4).map(&:name))
+ end
+ end
+
+ get "/extra/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems(gem_repo4))
+ file.contents
+ end
+ end
+
+ get "/extra/info/:name" do
+ etag_response do
+ gem = gems(gem_repo4).find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{gem_repo4}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(CompactIndexExtraApi)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
new file mode 100644
index 0000000000..e72040f604
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index_extra_api", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtraAPIMissing < CompactIndexExtraApi
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexExtraAPIMissing)
diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb
new file mode 100644
index 0000000000..67a9d23691
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index_extra", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtraMissing < CompactIndexExtra
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexExtraMissing)
diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb
new file mode 100644
index 0000000000..0a4dfdb2e8
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_forbidden.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexForbidden < CompactIndexAPI
+ get "/versions" do
+ halt 403
+ end
+end
+
+Artifice.activate_with(CompactIndexForbidden)
diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb
new file mode 100644
index 0000000000..ab371117de
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexHostRedirect < CompactIndexAPI
+ get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ redirect "http://bundler.localgemserver.test#{request.path_info}"
+ end
+
+ get "/versions" do
+ status 404
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(CompactIndexHostRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_no_gem.rb b/spec/bundler/support/artifice/compact_index_no_gem.rb
new file mode 100644
index 0000000000..01c5be1b3d
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_no_gem.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexNoGem < CompactIndexAPI
+ get "/gems/:id" do
+ halt 500
+ end
+end
+
+Artifice.activate_with(CompactIndexNoGem)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb
new file mode 100644
index 0000000000..eaedff5105
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexPartialUpdate < CompactIndexAPI
+ # Stub the server to never return 304s. This simulates the behaviour of
+ # Fastly / Rubygems ignoring ETag headers.
+ def not_modified?(_checksum)
+ false
+ end
+
+ get "/versions" do
+ cached_versions_path = File.join(
+ Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+
+ # Verify a cached copy of the versions file exists
+ unless File.read(cached_versions_path).start_with?("created_at: ")
+ raise("Cached versions file should be present and have content")
+ end
+
+ # Verify that a partial request is made, starting from the index of the
+ # final byte of the cached file.
+ unless env["HTTP_RANGE"] == "bytes=#{File.read(cached_versions_path).bytesize - 1}-"
+ raise("Range header should be present, and start from the index of the final byte of the cache.")
+ end
+
+ etag_response do
+ # Return the exact contents of the cache.
+ File.read(cached_versions_path)
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexPartialUpdate)
diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
new file mode 100644
index 0000000000..487be4771a
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexRangeNotSatisfiable < CompactIndexAPI
+ get "/versions" do
+ if env["HTTP_RANGE"]
+ status 416
+ else
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+ end
+
+ get "/info/:name" do
+ if env["HTTP_RANGE"]
+ status 416
+ else
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexRangeNotSatisfiable)
diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb
new file mode 100644
index 0000000000..e83451b5b6
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_redirects.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexRedirect < CompactIndexAPI
+ get "/fetch/actual/gem/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/versions" do
+ status 404
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(CompactIndexRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
new file mode 100644
index 0000000000..abbf3258e7
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexStrictBasicAuthentication < CompactIndexAPI
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+
+ # Only accepts password == "password"
+ unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
+ halt 403, "Authentication failed"
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
new file mode 100644
index 0000000000..7e1d3686e2
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexWrongDependencies < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ gem.versions.each {|gv| gv.dependencies.clear } if gem
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexWrongDependencies)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
new file mode 100644
index 0000000000..db4d8e3974
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexWrongGemChecksum < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ name = params[:name]
+ gem = gems.find {|g| g.name == name }
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 }
+ versions = gem ? gem.versions : []
+ versions.each {|v| v.checksum = checksum }
+ CompactIndex.info(versions)
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexWrongGemChecksum)
diff --git a/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
new file mode 100644
index 0000000000..12a6fa153f
--- /dev/null
+++ b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_marshal_fail", __FILE__)
+
+Artifice.deactivate
+
+class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
new file mode 100644
index 0000000000..9a0cfae8a2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+require Spec::Path.root.join("lib/bundler/deprecate")
+include Spec::Path
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
+require "artifice"
+require "sinatra/base"
+
+ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
+ALL_REQUESTS_MUTEX = Mutex.new
+
+at_exit do
+ if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
+ expected = expected.split("\n").sort
+ actual = ALL_REQUESTS.sort
+
+ unless expected == actual
+ raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
+ end
+ end
+end
+
+class Endpoint < Sinatra::Base
+ def self.all_requests
+ @all_requests ||= []
+ end
+
+ GEM_REPO = Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
+ set :raise_errors, true
+ set :show_exceptions, false
+
+ def call!(*)
+ super.tap do
+ ALL_REQUESTS_MUTEX.synchronize do
+ ALL_REQUESTS << @request.url
+ end
+ end
+ end
+
+ helpers do
+ def dependencies_for(gem_names, gem_repo = GEM_REPO)
+ return [] if gem_names.nil? || gem_names.empty?
+
+ require "rubygems"
+ require "bundler"
+ Bundler::Deprecate.skip_during do
+ all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read)
+ end.inject(:+)
+
+ all_specs.map do |name, version, platform|
+ spec = load_spec(name, version, platform, gem_repo)
+ next unless gem_names.include?(spec.name)
+ {
+ :name => spec.name,
+ :number => spec.version.version,
+ :platform => spec.platform.to_s,
+ :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
+ [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
+ end
+ }
+ end.compact
+ end
+ end
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read))
+ end
+ end
+
+ get "/quick/Marshal.4.8/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/fetch/actual/gem/:id" do
+ File.read("#{GEM_REPO}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/gems/:id" do
+ File.read("#{GEM_REPO}/gems/#{params[:id]}")
+ end
+
+ get "/api/v1/dependencies" do
+ Marshal.dump(dependencies_for(params[:gems]))
+ end
+
+ get "/specs.4.8.gz" do
+ File.read("#{GEM_REPO}/specs.4.8.gz")
+ end
+
+ get "/prerelease_specs.4.8.gz" do
+ File.read("#{GEM_REPO}/prerelease_specs.4.8.gz")
+ end
+end
+
+Artifice.activate_with(Endpoint)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
new file mode 100644
index 0000000000..202ccfc829
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+include Spec::Path
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
+
+require "artifice"
+require "sinatra/base"
+
+Artifice.deactivate
+
+class Endpoint500 < Sinatra::Base
+ before do
+ halt 500
+ end
+end
+
+Artifice.activate_with(Endpoint500)
diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
new file mode 100644
index 0000000000..bb89747adc
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointApiForbidden < Endpoint
+ get "/api/v1/dependencies" do
+ halt 403
+ end
+end
+
+Artifice.activate_with(EndpointApiForbidden)
diff --git a/spec/bundler/support/artifice/endpoint_api_missing.rb b/spec/bundler/support/artifice/endpoint_api_missing.rb
new file mode 100644
index 0000000000..95db8e2a7e
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_api_missing.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointApiMissing < Endpoint
+ get "/fetch/actual/gem/:id" do
+ $stderr.puts params[:id]
+ if params[:id] == "rack-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointApiMissing)
diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
new file mode 100644
index 0000000000..223671bc29
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointBasicAuthentication < Endpoint
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
new file mode 100644
index 0000000000..925954b12d
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointCredsDiffHost < Endpoint
+ helpers do
+ def auth
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ end
+
+ def authorized?
+ auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w[user pass]
+ end
+
+ def protected!
+ return if authorized?
+ response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth")
+ throw(:halt, [401, "Not authorized\n"])
+ end
+ end
+
+ before do
+ protected! unless request.path_info.include?("/no/creds/")
+ end
+
+ get "/gems/:id" do
+ redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ end
+
+ get "/no/creds/:id" do
+ if request.host.include?("diffhost") && !auth.provided?
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointCredsDiffHost)
diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb
new file mode 100644
index 0000000000..422f65401b
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtra < Endpoint
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(EndpointExtra)
diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb
new file mode 100644
index 0000000000..62e2c2bb93
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra_api.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtraApi < Endpoint
+ get "/extra/api/v1/dependencies" do
+ deps = dependencies_for(params[:gems], gem_repo4)
+ Marshal.dump(deps)
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{gem_repo4}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(EndpointExtraApi)
diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb
new file mode 100644
index 0000000000..038a12610a
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_extra", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtraMissing < EndpointExtra
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointExtraMissing)
diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb
new file mode 100644
index 0000000000..554c08f0a2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_fallback.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointFallback < Endpoint
+ DEPENDENCY_LIMIT = 60
+
+ get "/api/v1/dependencies" do
+ if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT
+ Marshal.dump(dependencies_for(params[:gems]))
+ else
+ halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointFallback)
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
new file mode 100644
index 0000000000..cda5664be2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointHostRedirect < Endpoint
+ get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ redirect "http://bundler.localgemserver.test#{request.path_info}"
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
new file mode 100644
index 0000000000..2a5dcdc2fd
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_fallback", __FILE__)
+
+Artifice.deactivate
+
+class EndpointMarshalFail < EndpointFallback
+ get "/api/v1/dependencies" do
+ "f0283y01hasf"
+ end
+end
+
+Artifice.activate_with(EndpointMarshalFail)
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
new file mode 100644
index 0000000000..64452f198d
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+class EndpointMirrorSource < Endpoint
+ get "/gems/:id" do
+ if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/"
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ else
+ halt 500
+ end
+ end
+end
+
+Artifice.activate_with(EndpointMirrorSource)
diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb
new file mode 100644
index 0000000000..ebf01458ba
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_redirect.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointRedirect < Endpoint
+ get "/fetch/actual/gem/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(EndpointRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
new file mode 100644
index 0000000000..905a519f3f
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointStrictBasicAuthentication < Endpoint
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+
+ # Only accepts password == "password"
+ unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
+ halt 403, "Authentication failed"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb
new file mode 100644
index 0000000000..3f60471c90
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_timeout.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_fallback", __FILE__)
+
+Artifice.deactivate
+
+class EndpointTimeout < EndpointFallback
+ SLEEP_TIMEOUT = 3
+
+ get "/api/v1/dependencies" do
+ sleep(SLEEP_TIMEOUT)
+ end
+end
+
+Artifice.activate_with(EndpointTimeout)
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
new file mode 100644
index 0000000000..1059c6df4e
--- /dev/null
+++ b/spec/bundler/support/artifice/fail.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "net/http"
+begin
+ require "net/https"
+rescue LoadError
+ nil # net/https or openssl
+end
+
+# We can't use artifice here because it uses rack
+
+module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse
+
+class Fail < Net::HTTP
+ # Net::HTTP uses a @newimpl instance variable to decide whether
+ # to use a legacy implementation. Since we are subclassing
+ # Net::HTTP, we must set it
+ @newimpl = true
+
+ def request(req, body = nil, &block)
+ raise(exception(req))
+ end
+
+ # Ensure we don't start a connect here
+ def connect
+ end
+
+ def exception(req)
+ name = ENV.fetch("BUNDLER_SPEC_EXCEPTION") { "Errno::ENETUNREACH" }
+ const = name.split("::").reduce(Object) {|mod, sym| mod.const_get(sym) }
+ const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
+ end
+end
+
+# Replace Net::HTTP with our failing subclass
+::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, ::Fail)
+end
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
new file mode 100644
index 0000000000..edd2f49a91
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require "net/http"
+if RUBY_VERSION < "1.9"
+ begin
+ require "net/https"
+ rescue LoadError
+ nil # net/https or openssl
+ end
+end # but only for 1.8
+
+CASSETTE_PATH = File.expand_path("../vcr_cassettes", __FILE__)
+CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
+
+class BundlerVCRHTTP < Net::HTTP
+ class RequestHandler
+ attr_reader :http, :request, :body, :response_block
+ def initialize(http, request, body = nil, &response_block)
+ @http = http
+ @request = request
+ @body = body
+ @response_block = response_block
+ end
+
+ def handle_request
+ handler = self
+ request.instance_eval do
+ @__vcr_request_handler = handler
+ end
+
+ if recorded_response?
+ recorded_response
+ else
+ record_response
+ end
+ end
+
+ def recorded_response?
+ return true if ENV["BUNDLER_SPEC_PRE_RECORDED"]
+ return false if ENV["BUNDLER_SPEC_FORCE_RECORD"]
+ request_pair_paths.all? {|f| File.exist?(f) }
+ end
+
+ def recorded_response
+ File.open(request_pair_paths.last, "rb:ASCII-8BIT") do |response_file|
+ response_io = ::Net::BufferedIO.new(response_file)
+ ::Net::HTTPResponse.read_new(response_io).tap do |response|
+ response.decode_content = request.decode_content if request.respond_to?(:decode_content)
+ response.uri = request.uri if request.respond_to?(:uri)
+
+ response.reading_body(response_io, request.response_body_permitted?) do
+ response_block.call(response) if response_block
+ end
+ end
+ end
+ end
+
+ def record_response
+ request_path, response_path = *request_pair_paths
+
+ @recording = true
+
+ response = http.request_without_vcr(request, body, &response_block)
+ @recording = false
+ unless @recording
+ FileUtils.mkdir_p(File.dirname(request_path))
+ binwrite(request_path, request_to_string(request))
+ binwrite(response_path, response_to_string(response))
+ end
+ response
+ end
+
+ def key
+ [request["host"] || http.address, request.path, request.method].compact
+ end
+
+ def file_name_for_key(key)
+ key.join("/").gsub(/[\:*?"<>|]/, "-")
+ end
+
+ def request_pair_paths
+ %w[request response].map do |kind|
+ File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key + [kind]))
+ end
+ end
+
+ def read_stored_request(path)
+ contents = File.read(path)
+ headers = {}
+ method = nil
+ path = nil
+ contents.lines.grep(/^> /).each do |line|
+ if line =~ /^> (GET|HEAD|POST|PATCH|PUT|DELETE) (.*)/
+ method = $1
+ path = $2.strip
+ elsif line =~ /^> (.*?): (.*)/
+ headers[$1] = $2
+ end
+ end
+ body = contents =~ /^([^>].*)/m && $1
+ Net::HTTP.const_get(method.capitalize).new(path, headers).tap {|r| r.body = body if body }
+ end
+
+ def request_to_string(request)
+ request_string = []
+ request_string << "> #{request.method.upcase} #{request.path}"
+ request.to_hash.each do |key, value|
+ request_string << "> #{key}: #{Array(value).first}"
+ end
+ request << "" << request.body if request.body
+ request_string.join("\n")
+ end
+
+ def response_to_string(response)
+ headers = response.to_hash
+ body = response.body
+
+ response_string = []
+ response_string << "HTTP/1.1 #{response.code} #{response.message}"
+
+ headers["content-length"] = [body.bytesize.to_s] if body
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value.join(", ")}"
+ end
+
+ response_string << "" << body
+
+ response_string = response_string.join("\n")
+ if response_string.respond_to?(:force_encoding)
+ response_string.force_encoding("ASCII-8BIT")
+ else
+ response_string
+ end
+ end
+
+ def binwrite(path, contents)
+ File.open(path, "wb:ASCII-8BIT") {|f| f.write(contents) }
+ end
+ end
+
+ def request_with_vcr(request, *args, &block)
+ handler = request.instance_eval do
+ remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)
+ end || RequestHandler.new(self, request, *args, &block)
+
+ handler.handle_request
+ end
+
+ alias_method :request_without_vcr, :request
+ alias_method :request, :request_with_vcr
+end
+
+# Replace Net::HTTP with our VCR subclass
+::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, BundlerVCRHTTP)
+end
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request
new file mode 100644
index 0000000000..00dcd51750
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request
@@ -0,0 +1,7 @@
+> GET /api/v1/dependencies?gems=bundler
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: api.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/request
new file mode 100644
index 0000000000..13b3c98dd2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/request
@@ -0,0 +1,6 @@
+> HEAD /api/v1/dependencies
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: api.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/response b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/response
new file mode 100644
index 0000000000..fa6cc543da
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies/HEAD/response
@@ -0,0 +1,24 @@
+HTTP/1.1 200 OK
+content-type: text/plain; charset=utf-8
+x-frame-options: SAMEORIGIN
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+content-security-policy: default-src 'self'; script-src 'self' https://secure.gaug.es; style-src 'self' https://fonts.googleapis.com; img-src 'self' https://secure.gaug.es https://gravatar.com https://secure.gravatar.com; font-src 'self' https://fonts.gstatic.com; connect-src https://s3-us-west-2.amazonaws.com/rubygems-dumps/; frame-src https://ghbtns.com
+cache-control: no-cache
+x-request-id: a7d87e66-6bb3-4b7a-9d3a-89ee68784d0e
+x-runtime: 0.003648
+x-ua-compatible: IE=Edge,chrome=1
+x-backend: F_Rails 54.186.104.15:443
+content-length: 0
+accept-ranges: bytes
+date: Fri, 28 Apr 2017 09:22:23 GMT
+via: 1.1 varnish
+age: 3103
+connection: keep-alive
+x-served-by: cache-fra1243-FRA
+x-cache: HIT
+x-cache-hits: 107
+x-timer: S1493371344.878545,VS0,VE0
+vary: Accept-Encoding,Fastly-SSL
+server: RubyGems.org
+
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/gems/bundler-1.12.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/gems/bundler-1.12.3.gem/GET/request
new file mode 100644
index 0000000000..230892854c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/gems/bundler-1.12.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/bundler-1.12.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: api.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request
new file mode 100644
index 0000000000..c9337e9a6b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request
@@ -0,0 +1,7 @@
+> GET /quick/Marshal.4.8/bundler-1.12.3.gemspec.rz
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: api.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/specs.4.8.gz/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/specs.4.8.gz/GET/request
new file mode 100644
index 0000000000..94f2b68688
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/api.rubygems.org/specs.4.8.gz/GET/request
@@ -0,0 +1,7 @@
+> GET /specs.4.8.gz
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: api.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
new file mode 100644
index 0000000000..a92b8bb55f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
@@ -0,0 +1,7 @@
+> GET /info/CFPropertyList
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
new file mode 100644
index 0000000000..278c2103b3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ParseTree
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RedCloth/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RedCloth/GET/request
new file mode 100644
index 0000000000..895b7dfd54
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RedCloth/GET/request
@@ -0,0 +1,7 @@
+> GET /info/RedCloth
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
new file mode 100644
index 0000000000..6bf0e6d3af
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
@@ -0,0 +1,7 @@
+> GET /info/RubyInline
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
new file mode 100644
index 0000000000..30a9943801
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
@@ -0,0 +1,7 @@
+> GET /info/SexpProcessor
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
new file mode 100644
index 0000000000..927fce847b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ZenTest
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
new file mode 100644
index 0000000000..33ba985fc9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
@@ -0,0 +1,7 @@
+> GET /info/abstract
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
new file mode 100644
index 0000000000..67f6acff19
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
@@ -0,0 +1,7 @@
+> GET /info/actioncable
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
new file mode 100644
index 0000000000..ff277f113a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
@@ -0,0 +1,7 @@
+> GET /info/actionmailer
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
new file mode 100644
index 0000000000..85c039b7eb
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
@@ -0,0 +1,7 @@
+> GET /info/actionpack
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
new file mode 100644
index 0000000000..8b11f69250
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
@@ -0,0 +1,7 @@
+> GET /info/actionview
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
new file mode 100644
index 0000000000..2ec89cf1b2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
@@ -0,0 +1,7 @@
+> GET /info/actionwebservice
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
new file mode 100644
index 0000000000..a215f0abf9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activejob
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
new file mode 100644
index 0000000000..ceae0f0950
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activemodel-globalid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
new file mode 100644
index 0000000000..e4b113569e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activemodel
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
new file mode 100644
index 0000000000..095307216d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activerecord-deprecated_finders
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
new file mode 100644
index 0000000000..72983afdb3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activerecord
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
new file mode 100644
index 0000000000..6da07e07c1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activeresource
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
new file mode 100644
index 0000000000..e850b19891
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
@@ -0,0 +1,7 @@
+> GET /info/activesupport
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
new file mode 100644
index 0000000000..b85f0d4abc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
@@ -0,0 +1,7 @@
+> GET /info/adamantium
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
new file mode 100644
index 0000000000..332d846d65
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
@@ -0,0 +1,7 @@
+> GET /info/addressable
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/allison/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/allison/GET/request
new file mode 100644
index 0000000000..8b0e9de941
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/allison/GET/request
@@ -0,0 +1,7 @@
+> GET /info/allison
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ansi/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ansi/GET/request
new file mode 100644
index 0000000000..35a798ad01
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ansi/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ansi
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/archive-tar-minitar/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/archive-tar-minitar/GET/request
new file mode 100644
index 0000000000..15113c6b35
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/archive-tar-minitar/GET/request
@@ -0,0 +1,7 @@
+> GET /info/archive-tar-minitar
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
new file mode 100644
index 0000000000..c8e60e5198
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
@@ -0,0 +1,7 @@
+> GET /info/arel
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
new file mode 100644
index 0000000000..83c157b83d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ast
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
new file mode 100644
index 0000000000..eb816b9425
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
@@ -0,0 +1,7 @@
+> GET /info/astrolabe
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
new file mode 100644
index 0000000000..95416a8856
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
@@ -0,0 +1,7 @@
+> GET /info/atomic
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
new file mode 100644
index 0000000000..9a7e6768c9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
@@ -0,0 +1,7 @@
+> GET /info/autoparse
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
new file mode 100644
index 0000000000..56d2f3f4be
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
@@ -0,0 +1,7 @@
+> GET /info/axiom-types
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
new file mode 100644
index 0000000000..d39a8eefed
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
@@ -0,0 +1,7 @@
+> GET /info/backports
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bacon/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bacon/GET/request
new file mode 100644
index 0000000000..99b3624b68
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bacon/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bacon
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
new file mode 100644
index 0000000000..da2d06bb39
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bcrypt-ruby
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
new file mode 100644
index 0000000000..6516d4f90d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bcrypt
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
new file mode 100644
index 0000000000..cba3167ca3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bcrypt_pbkdf
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-extras/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-extras/GET/request
new file mode 100644
index 0000000000..a42bb5fc39
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-extras/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-extras
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-git/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-git/GET/request
new file mode 100644
index 0000000000..f887a9d8b9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-git/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-git
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rcov/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rcov/GET/request
new file mode 100644
index 0000000000..123d71230b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rcov/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-rcov
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rspec/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rspec/GET/request
new file mode 100644
index 0000000000..11d8de5fd1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rspec/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-rspec
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rubyforge/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rubyforge/GET/request
new file mode 100644
index 0000000000..759c5dc5a7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-rubyforge/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-rubyforge
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-zentest/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-zentest/GET/request
new file mode 100644
index 0000000000..ae5a9b068f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones-zentest/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones-zentest
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones/GET/request
new file mode 100644
index 0000000000..3e6d44e52f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bones/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bones
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
new file mode 100644
index 0000000000..5fe5431bfa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
@@ -0,0 +1,7 @@
+> GET /info/builder
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
new file mode 100644
index 0000000000..d0af347d8a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
@@ -0,0 +1,7 @@
+> GET /info/bundler
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/camping/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/camping/GET/request
new file mode 100644
index 0000000000..d40975748c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/camping/GET/request
@@ -0,0 +1,7 @@
+> GET /info/camping
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
new file mode 100644
index 0000000000..4e852e84e4
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
@@ -0,0 +1,7 @@
+> GET /info/capybara
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
new file mode 100644
index 0000000000..8ae268566f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celerity
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
new file mode 100644
index 0000000000..caa2b64772
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-essentials
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
new file mode 100644
index 0000000000..3680210c06
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-extras
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
new file mode 100644
index 0000000000..9ab4a9f9a5
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-fsm
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
new file mode 100644
index 0000000000..48deb49201
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-io
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
new file mode 100644
index 0000000000..375710b600
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-pool
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
new file mode 100644
index 0000000000..62acec8401
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid-supervision
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
new file mode 100644
index 0000000000..ea4c6081c6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/celluloid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
new file mode 100644
index 0000000000..2087abff17
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
@@ -0,0 +1,7 @@
+> GET /info/cgi_multipart_eof_fix
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
new file mode 100644
index 0000000000..c9c4c8e1c3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
@@ -0,0 +1,7 @@
+> GET /info/childprocess
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
new file mode 100644
index 0000000000..627dfc2236
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
@@ -0,0 +1,7 @@
+> GET /info/climate_control
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
new file mode 100644
index 0000000000..0b58b496a2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
@@ -0,0 +1,7 @@
+> GET /info/cocaine
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
new file mode 100644
index 0000000000..26fb80abd9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
@@ -0,0 +1,7 @@
+> GET /info/coercible
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
new file mode 100644
index 0000000000..35f65b1c96
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
@@ -0,0 +1,7 @@
+> GET /info/coffee-rails
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
new file mode 100644
index 0000000000..c1e276d2f2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
@@ -0,0 +1,7 @@
+> GET /info/coffee-script-source
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
new file mode 100644
index 0000000000..6fb40106a3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
@@ -0,0 +1,7 @@
+> GET /info/coffee-script
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
new file mode 100644
index 0000000000..86a09b2b35
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
@@ -0,0 +1,7 @@
+> GET /info/colorize
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
new file mode 100644
index 0000000000..0069d6d724
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
@@ -0,0 +1,7 @@
+> GET /info/concurrent-ruby
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
new file mode 100644
index 0000000000..70e600db3d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
@@ -0,0 +1,7 @@
+> GET /info/configuration
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
new file mode 100644
index 0000000000..5692539d94
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
@@ -0,0 +1,7 @@
+> GET /info/coveralls
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
new file mode 100644
index 0000000000..8b9407ffc0
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
@@ -0,0 +1,7 @@
+> GET /info/crass
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
new file mode 100644
index 0000000000..2675af7847
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
@@ -0,0 +1,7 @@
+> GET /info/cucumber-core
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
new file mode 100644
index 0000000000..f37e871807
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
@@ -0,0 +1,7 @@
+> GET /info/cucumber-wire
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
new file mode 100644
index 0000000000..db80e44ac1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
@@ -0,0 +1,7 @@
+> GET /info/cucumber
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
new file mode 100644
index 0000000000..f92a2f7bc9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
@@ -0,0 +1,7 @@
+> GET /info/culerity
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
new file mode 100644
index 0000000000..1119c8e4fa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
@@ -0,0 +1,7 @@
+> GET /info/curses
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
new file mode 100644
index 0000000000..5080c98e6d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
@@ -0,0 +1,7 @@
+> GET /info/daemons
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
new file mode 100644
index 0000000000..d80f93551a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
@@ -0,0 +1,7 @@
+> GET /info/database_cleaner
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
new file mode 100644
index 0000000000..f2463924fc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
@@ -0,0 +1,7 @@
+> GET /info/declarative-option
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
new file mode 100644
index 0000000000..de13d40070
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
@@ -0,0 +1,7 @@
+> GET /info/declarative
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
new file mode 100644
index 0000000000..563a4f60c2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
@@ -0,0 +1,7 @@
+> GET /info/descendants_tracker
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
new file mode 100644
index 0000000000..3c60080b38
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
@@ -0,0 +1,7 @@
+> GET /info/diff-lcs
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
new file mode 100644
index 0000000000..de1e687189
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
@@ -0,0 +1,7 @@
+> GET /info/docile
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
new file mode 100644
index 0000000000..6157da6082
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
@@ -0,0 +1,7 @@
+> GET /info/domain_name
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
new file mode 100644
index 0000000000..fe63740e5b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
@@ -0,0 +1,7 @@
+> GET /info/dotenv-deployment
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
new file mode 100644
index 0000000000..234c682a25
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
@@ -0,0 +1,7 @@
+> GET /info/dotenv
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/echoe/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/echoe/GET/request
new file mode 100644
index 0000000000..98ea72af26
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/echoe/GET/request
@@ -0,0 +1,7 @@
+> GET /info/echoe
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
new file mode 100644
index 0000000000..12962c23bb
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
@@ -0,0 +1,7 @@
+> GET /info/em-hiredis
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
new file mode 100644
index 0000000000..e558748718
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
@@ -0,0 +1,7 @@
+> GET /info/english
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
new file mode 100644
index 0000000000..830c2ba57f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
@@ -0,0 +1,7 @@
+> GET /info/equalizer
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
new file mode 100644
index 0000000000..60045f3490
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
@@ -0,0 +1,7 @@
+> GET /info/erubi
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
new file mode 100644
index 0000000000..1f4a5d9599
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
@@ -0,0 +1,7 @@
+> GET /info/erubis
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
new file mode 100644
index 0000000000..27c56dc41b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
@@ -0,0 +1,7 @@
+> GET /info/escape_utils
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
new file mode 100644
index 0000000000..83753cb9de
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
@@ -0,0 +1,7 @@
+> GET /info/et-orbi
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
new file mode 100644
index 0000000000..aad129c8f2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
@@ -0,0 +1,7 @@
+> GET /info/event-bus
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine-le/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine-le/GET/request
new file mode 100644
index 0000000000..1d83bd53b7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine-le/GET/request
@@ -0,0 +1,7 @@
+> GET /info/eventmachine-le
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
new file mode 100644
index 0000000000..614a5bf2a8
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
@@ -0,0 +1,7 @@
+> GET /info/eventmachine
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
new file mode 100644
index 0000000000..531f5c9b31
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
@@ -0,0 +1,7 @@
+> GET /info/execjs
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
new file mode 100644
index 0000000000..3fa0dea3a5
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
@@ -0,0 +1,7 @@
+> GET /info/extlib
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
new file mode 100644
index 0000000000..6aae67744b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
@@ -0,0 +1,7 @@
+> GET /info/facets
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
new file mode 100644
index 0000000000..18bdd6e328
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
@@ -0,0 +1,7 @@
+> GET /info/facter
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
new file mode 100644
index 0000000000..7bf2fb9f5d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
@@ -0,0 +1,7 @@
+> GET /info/faker
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/jobs,spec_run,trampoline_disable,plugins ab677b2f2db78951
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
new file mode 100644
index 0000000000..b7ee2f317a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
@@ -0,0 +1,7 @@
+> GET /info/faraday
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
new file mode 100644
index 0000000000..7ea5079231
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
@@ -0,0 +1,7 @@
+> GET /info/fastthread
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
new file mode 100644
index 0000000000..e865b6d68f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
@@ -0,0 +1,7 @@
+> GET /info/faye-websocket
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fcgi/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fcgi/GET/request
new file mode 100644
index 0000000000..4389c71b0c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fcgi/GET/request
@@ -0,0 +1,7 @@
+> GET /info/fcgi
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
new file mode 100644
index 0000000000..c2ea7ed71b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ffi-win32-extensions
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
new file mode 100644
index 0000000000..337ccf260d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ffi
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
new file mode 100644
index 0000000000..85e3d59016
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
@@ -0,0 +1,7 @@
+> GET /info/flexmock
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
new file mode 100644
index 0000000000..2946076a0c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
@@ -0,0 +1,7 @@
+> GET /info/functional-ruby
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
new file mode 100644
index 0000000000..037082143d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
@@ -0,0 +1,7 @@
+> GET /info/gem_plugin
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
new file mode 100644
index 0000000000..48a190b596
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
@@ -0,0 +1,7 @@
+> GET /info/gemcutter
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
new file mode 100644
index 0000000000..e16c323f9f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
@@ -0,0 +1,7 @@
+> GET /info/gherkin
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
new file mode 100644
index 0000000000..51c1b8dbd1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
@@ -0,0 +1,7 @@
+> GET /info/gherkin3
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/git/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/git/GET/request
new file mode 100644
index 0000000000..a17b1c9fd9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/git/GET/request
@@ -0,0 +1,7 @@
+> GET /info/git
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
new file mode 100644
index 0000000000..0b5cc95383
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/globalid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
new file mode 100644
index 0000000000..6997e2f1b1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
@@ -0,0 +1,7 @@
+> GET /info/google-api-client
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
new file mode 100644
index 0000000000..a5af0919c9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
@@ -0,0 +1,7 @@
+> GET /info/googleauth
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
new file mode 100644
index 0000000000..35f5608ebe
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
@@ -0,0 +1,7 @@
+> GET /info/gxapi_rails
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
new file mode 100644
index 0000000000..385ec2b595
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hashie
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/highline/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/highline/GET/request
new file mode 100644
index 0000000000..4b571d66c1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/highline/GET/request
@@ -0,0 +1,7 @@
+> GET /info/highline
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
new file mode 100644
index 0000000000..f6c38deaaa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hike
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
new file mode 100644
index 0000000000..6113e85b8d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hiredis
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
new file mode 100644
index 0000000000..4a3c1bbdab
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hitimes
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
new file mode 100644
index 0000000000..e86bcea319
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hoe
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
new file mode 100644
index 0000000000..40972439c4
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hooks
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
new file mode 100644
index 0000000000..dc272ed3b8
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
@@ -0,0 +1,7 @@
+> GET /info/http-cookie
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http_parser.rb/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http_parser.rb/GET/request
new file mode 100644
index 0000000000..e67979aa78
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http_parser.rb/GET/request
@@ -0,0 +1,7 @@
+> GET /info/http_parser.rb
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
new file mode 100644
index 0000000000..c9d3ce3546
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
@@ -0,0 +1,7 @@
+> GET /info/httpadapter
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
new file mode 100644
index 0000000000..499c716b32
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
@@ -0,0 +1,7 @@
+> GET /info/httpclient
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
new file mode 100644
index 0000000000..218736d428
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
@@ -0,0 +1,7 @@
+> GET /info/hurley
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
new file mode 100644
index 0000000000..ad35d73f0b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
@@ -0,0 +1,7 @@
+> GET /info/i18n
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
new file mode 100644
index 0000000000..0c7e990bf7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ice_nine
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
new file mode 100644
index 0000000000..8f9b0132c2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
@@ -0,0 +1,7 @@
+> GET /info/journey
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
new file mode 100644
index 0000000000..6dd6f3f43b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
@@ -0,0 +1,7 @@
+> GET /info/jruby-pageant
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
new file mode 100644
index 0000000000..7ca746d23e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
@@ -0,0 +1,7 @@
+> GET /info/json
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
new file mode 100644
index 0000000000..aa2b18bd1d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
@@ -0,0 +1,7 @@
+> GET /info/json_pure
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
new file mode 100644
index 0000000000..c8669eac08
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
@@ -0,0 +1,7 @@
+> GET /info/jwt
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/language/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/language/GET/request
new file mode 100644
index 0000000000..4aeefbc7aa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/language/GET/request
@@ -0,0 +1,7 @@
+> GET /info/language
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
new file mode 100644
index 0000000000..60da8f9a26
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
@@ -0,0 +1,7 @@
+> GET /info/launchy
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
new file mode 100644
index 0000000000..3764e1e3b1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
@@ -0,0 +1,7 @@
+> GET /info/libwebsocket
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
new file mode 100644
index 0000000000..fad58483fe
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
@@ -0,0 +1,7 @@
+> GET /info/libxml-ruby
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
new file mode 100644
index 0000000000..9ac0105694
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/liquid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
new file mode 100644
index 0000000000..d8ac06f4ea
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
@@ -0,0 +1,7 @@
+> GET /info/listen
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
new file mode 100644
index 0000000000..34f643d772
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
@@ -0,0 +1,7 @@
+> GET /info/little-plugger
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
new file mode 100644
index 0000000000..21ccaf6be5
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
@@ -0,0 +1,7 @@
+> GET /info/lockfile
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
new file mode 100644
index 0000000000..8a7bd45f35
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
@@ -0,0 +1,7 @@
+> GET /info/logging
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
new file mode 100644
index 0000000000..2cb6d57679
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
@@ -0,0 +1,7 @@
+> GET /info/loofah
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loquacious/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loquacious/GET/request
new file mode 100644
index 0000000000..bda60b4dc3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loquacious/GET/request
@@ -0,0 +1,7 @@
+> GET /info/loquacious
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mab/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mab/GET/request
new file mode 100644
index 0000000000..115c6e6448
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mab/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mab
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
new file mode 100644
index 0000000000..ea11e0bd0c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mail
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/markaby/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/markaby/GET/request
new file mode 100644
index 0000000000..27db5d09cf
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/markaby/GET/request
@@ -0,0 +1,7 @@
+> GET /info/markaby
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
new file mode 100644
index 0000000000..4b0c275c6e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
@@ -0,0 +1,7 @@
+> GET /info/memcache-client
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
new file mode 100644
index 0000000000..fcda1676de
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
@@ -0,0 +1,7 @@
+> GET /info/memoist
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
new file mode 100644
index 0000000000..ae7e5e0371
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
@@ -0,0 +1,7 @@
+> GET /info/memoizable
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
new file mode 100644
index 0000000000..9f502c21cc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
@@ -0,0 +1,7 @@
+> GET /info/metaclass
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaid/GET/request
new file mode 100644
index 0000000000..872aea81e0
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/metaid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
new file mode 100644
index 0000000000..4d02bc1e42
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
@@ -0,0 +1,7 @@
+> GET /info/method_source
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
new file mode 100644
index 0000000000..8081aaa002
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mime-types-data
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
new file mode 100644
index 0000000000..eb07326eda
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mime-types
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
new file mode 100644
index 0000000000..cbb2e4f493
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mimemagic
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
new file mode 100644
index 0000000000..f536dc4d3e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mini_portile
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
new file mode 100644
index 0000000000..ab28a8089a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mini_portile2
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar-cli/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar-cli/GET/request
new file mode 100644
index 0000000000..d9d5968a5b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar-cli/GET/request
@@ -0,0 +1,7 @@
+> GET /info/minitar-cli
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar/GET/request
new file mode 100644
index 0000000000..c8b5697b43
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitar/GET/request
@@ -0,0 +1,7 @@
+> GET /info/minitar
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
new file mode 100644
index 0000000000..4d88e89a59
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
@@ -0,0 +1,7 @@
+> GET /info/minitest
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
new file mode 100644
index 0000000000..3333257711
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mkrf
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
new file mode 100644
index 0000000000..f215f1f8d6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mocha
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
new file mode 100644
index 0000000000..0181a370bc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mongrel
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
new file mode 100644
index 0000000000..088d7a648d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mono_logger
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
new file mode 100644
index 0000000000..56942172dc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
@@ -0,0 +1,7 @@
+> GET /info/multi_json
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
new file mode 100644
index 0000000000..8f4d50696c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
@@ -0,0 +1,7 @@
+> GET /info/multi_test
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
new file mode 100644
index 0000000000..1a64e6f47e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
@@ -0,0 +1,7 @@
+> GET /info/multimap
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
new file mode 100644
index 0000000000..63eec25ffd
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
@@ -0,0 +1,7 @@
+> GET /info/multipart-post
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
new file mode 100644
index 0000000000..710afe0829
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
@@ -0,0 +1,7 @@
+> GET /info/mustermann
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
new file mode 100644
index 0000000000..45d8ea2c39
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
@@ -0,0 +1,7 @@
+> GET /info/needle
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
new file mode 100644
index 0000000000..4ab184fc81
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
@@ -0,0 +1,7 @@
+> GET /info/nenv
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
new file mode 100644
index 0000000000..8910edf8e6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
@@ -0,0 +1,7 @@
+> GET /info/net-scp
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
new file mode 100644
index 0000000000..043751da3b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
@@ -0,0 +1,7 @@
+> GET /info/net-ssh
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
new file mode 100644
index 0000000000..912c799120
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
@@ -0,0 +1,7 @@
+> GET /info/netrc
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/newgem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/newgem/GET/request
new file mode 100644
index 0000000000..ef62899a0c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/newgem/GET/request
@@ -0,0 +1,7 @@
+> GET /info/newgem
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
new file mode 100644
index 0000000000..44337df0b7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
@@ -0,0 +1,7 @@
+> GET /info/nio4r
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
new file mode 100644
index 0000000000..09c5dd725f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
@@ -0,0 +1,7 @@
+> GET /info/nokogiri
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
new file mode 100644
index 0000000000..1d82322d8b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
@@ -0,0 +1,7 @@
+> GET /info/os
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
new file mode 100644
index 0000000000..c977514627
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
@@ -0,0 +1,7 @@
+> GET /info/paperclip
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
new file mode 100644
index 0000000000..5fc1b9ff8b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
@@ -0,0 +1,7 @@
+> GET /info/parser
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
new file mode 100644
index 0000000000..1636fe07b4
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
@@ -0,0 +1,7 @@
+> GET /info/pattern-match
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
new file mode 100644
index 0000000000..8f2f5ae398
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
@@ -0,0 +1,7 @@
+> GET /info/pkg-config
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
new file mode 100644
index 0000000000..8841563b63
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
@@ -0,0 +1,7 @@
+> GET /info/polyglot
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
new file mode 100644
index 0000000000..73ef4c0d07
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
@@ -0,0 +1,7 @@
+> GET /info/power_assert
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerbar/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerbar/GET/request
new file mode 100644
index 0000000000..0a69936bc6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerbar/GET/request
@@ -0,0 +1,7 @@
+> GET /info/powerbar
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
new file mode 100644
index 0000000000..20bf19b1af
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
@@ -0,0 +1,7 @@
+> GET /info/powerpack
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/preforker/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/preforker/GET/request
new file mode 100644
index 0000000000..2ba448e12d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/preforker/GET/request
@@ -0,0 +1,7 @@
+> GET /info/preforker
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
new file mode 100644
index 0000000000..9f13c8a79e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
@@ -0,0 +1,7 @@
+> GET /info/public_suffix
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
new file mode 100644
index 0000000000..dfc9adfa9d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
@@ -0,0 +1,7 @@
+> GET /info/racc
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
new file mode 100644
index 0000000000..27a90522f4
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack-cache
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
new file mode 100644
index 0000000000..59a7bd8b8d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack-mount
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
new file mode 100644
index 0000000000..4eb6557436
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack-protection
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
new file mode 100644
index 0000000000..6871bc0308
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack-ssl
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
new file mode 100644
index 0000000000..624dd07678
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack-test
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
new file mode 100644
index 0000000000..16a0feac8c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rack
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
new file mode 100644
index 0000000000..67514b94c8
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rails-deprecated_sanitizer
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
new file mode 100644
index 0000000000..2caa6d814e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rails-dom-testing
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
new file mode 100644
index 0000000000..54db344173
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rails-html-sanitizer
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
new file mode 100644
index 0000000000..3fb8b11adf
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rails-observers
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
new file mode 100644
index 0000000000..b156e7be25
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rails
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
new file mode 100644
index 0000000000..40f66c8a6f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
@@ -0,0 +1,7 @@
+> GET /info/railties
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
new file mode 100644
index 0000000000..ae1ee0cee9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rainbow
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
new file mode 100644
index 0000000000..2f777a05bf
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rake-compiler
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
new file mode 100644
index 0000000000..c161c917e1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rake
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
new file mode 100644
index 0000000000..101066914a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rb-fchange
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
new file mode 100644
index 0000000000..192253cb0c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rb-fsevent
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
new file mode 100644
index 0000000000..cf7287e37b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rb-inotify
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
new file mode 100644
index 0000000000..6d256b9e11
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rb-kqueue
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
new file mode 100644
index 0000000000..0a269f784f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rbnacl-libsodium
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
new file mode 100644
index 0000000000..d28de6c266
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rbnacl
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rcov/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rcov/GET/request
new file mode 100644
index 0000000000..611a286a4c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rcov/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rcov
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
new file mode 100644
index 0000000000..8402303257
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rdoc
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
new file mode 100644
index 0000000000..9b78830065
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
@@ -0,0 +1,7 @@
+> GET /info/redis-namespace
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
new file mode 100644
index 0000000000..9bfcfc80fd
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
@@ -0,0 +1,7 @@
+> GET /info/redis
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
new file mode 100644
index 0000000000..42ee151f68
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ref
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
new file mode 100644
index 0000000000..73a5ca084e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
@@ -0,0 +1,7 @@
+> GET /info/representable
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
new file mode 100644
index 0000000000..aa9132aedb
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
@@ -0,0 +1,7 @@
+> GET /info/resque-scheduler
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
new file mode 100644
index 0000000000..616a9e5b9f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
@@ -0,0 +1,7 @@
+> GET /info/resque
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
new file mode 100644
index 0000000000..5935658273
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rest-client
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
new file mode 100644
index 0000000000..a76f439668
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
@@ -0,0 +1,7 @@
+> GET /info/retriable
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexical/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexical/GET/request
new file mode 100644
index 0000000000..0390a17867
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexical/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rexical
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
new file mode 100644
index 0000000000..46fa433f4a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
@@ -0,0 +1,7 @@
+> GET /info/right_aws
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
new file mode 100644
index 0000000000..9afacd1726
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
@@ -0,0 +1,7 @@
+> GET /info/right_http_connection
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
new file mode 100644
index 0000000000..c9398deae9
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec-core
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
new file mode 100644
index 0000000000..660b24d100
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec-expectations
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
new file mode 100644
index 0000000000..5ff447f187
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec-logsplit
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
new file mode 100644
index 0000000000..0a961c6ffd
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec-mocks
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
new file mode 100644
index 0000000000..ab2d3175ec
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec-support
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
new file mode 100644
index 0000000000..3f327837cc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rspec
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubigen/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubigen/GET/request
new file mode 100644
index 0000000000..19c742edd3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubigen/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rubigen
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
new file mode 100644
index 0000000000..49899f0274
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rubocop
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-openid/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-openid/GET/request
new file mode 100644
index 0000000000..83ae0d71e7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-openid/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ruby-openid
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
new file mode 100644
index 0000000000..85bcdde042
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ruby-progressbar
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-yadis/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-yadis/GET/request
new file mode 100644
index 0000000000..e12117e5dc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-yadis/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ruby-yadis
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
new file mode 100644
index 0000000000..43d58ead74
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ruby_dep
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
new file mode 100644
index 0000000000..460ff43276
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
@@ -0,0 +1,7 @@
+> GET /info/ruby_parser
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
new file mode 100644
index 0000000000..92e612095d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rubyforge
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
new file mode 100644
index 0000000000..56ff5252ce
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rubyzip
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
new file mode 100644
index 0000000000..9c499d7320
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
@@ -0,0 +1,7 @@
+> GET /info/rufus-scheduler
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
new file mode 100644
index 0000000000..98051f0722
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sass-listen
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
new file mode 100644
index 0000000000..ab996f5ead
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sass-rails
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
new file mode 100644
index 0000000000..16f4a2d834
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sass
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
new file mode 100644
index 0000000000..3128daa2f7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
@@ -0,0 +1,7 @@
+> GET /info/selenium-webdriver
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
new file mode 100644
index 0000000000..e92bfe4e50
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sexp_processor
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/shotgun/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/shotgun/GET/request
new file mode 100644
index 0000000000..9d6b958cc5
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/shotgun/GET/request
@@ -0,0 +1,7 @@
+> GET /info/shotgun
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
new file mode 100644
index 0000000000..bd4257ed35
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
@@ -0,0 +1,7 @@
+> GET /info/signet
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
new file mode 100644
index 0000000000..daf148a4f8
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
@@ -0,0 +1,7 @@
+> GET /info/simplecov-html
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
new file mode 100644
index 0000000000..be9dec39be
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
@@ -0,0 +1,7 @@
+> GET /info/simplecov
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
new file mode 100644
index 0000000000..f442fa89f6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sinatra
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
new file mode 100644
index 0000000000..f4a1685ba2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
@@ -0,0 +1,7 @@
+> GET /info/slop
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spicycode-rcov/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spicycode-rcov/GET/request
new file mode 100644
index 0000000000..c9686cae49
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spicycode-rcov/GET/request
@@ -0,0 +1,7 @@
+> GET /info/spicycode-rcov
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
new file mode 100644
index 0000000000..d1f41c369a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
@@ -0,0 +1,7 @@
+> GET /info/spoon
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
new file mode 100644
index 0000000000..7b7ba15490
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sprockets-rails
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
new file mode 100644
index 0000000000..06767f04a6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sprockets
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
new file mode 100644
index 0000000000..a9995940f5
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
@@ -0,0 +1,7 @@
+> GET /info/spruz
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
new file mode 100644
index 0000000000..39542c09e8
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sqlite3
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/syntax/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/syntax/GET/request
new file mode 100644
index 0000000000..462e2bd29b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/syntax/GET/request
@@ -0,0 +1,7 @@
+> GET /info/syntax
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
new file mode 100644
index 0000000000..4ce082dcaa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
@@ -0,0 +1,7 @@
+> GET /info/sys-admin
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tenderlove-frex/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tenderlove-frex/GET/request
new file mode 100644
index 0000000000..39f37ebdf3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tenderlove-frex/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tenderlove-frex
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
new file mode 100644
index 0000000000..23c9c82fcf
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
@@ -0,0 +1,7 @@
+> GET /info/term-ansicolor
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/termios/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/termios/GET/request
new file mode 100644
index 0000000000..17087a36e1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/termios/GET/request
@@ -0,0 +1,7 @@
+> GET /info/termios
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-spec/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-spec/GET/request
new file mode 100644
index 0000000000..1d2d581583
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-spec/GET/request
@@ -0,0 +1,7 @@
+> GET /info/test-spec
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
new file mode 100644
index 0000000000..92b85c1751
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
@@ -0,0 +1,7 @@
+> GET /info/test-unit
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
new file mode 100644
index 0000000000..3a8cbe23e1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
@@ -0,0 +1,7 @@
+> GET /info/text-format
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
new file mode 100644
index 0000000000..5aa85a177c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
@@ -0,0 +1,7 @@
+> GET /info/text-hyphen
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
new file mode 100644
index 0000000000..ce6a7c8e8e
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
@@ -0,0 +1,7 @@
+> GET /info/thin
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
new file mode 100644
index 0000000000..413597fd9d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
@@ -0,0 +1,7 @@
+> GET /info/thor
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
new file mode 100644
index 0000000000..b420081022
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
@@ -0,0 +1,7 @@
+> GET /info/thoughtbot-shoulda
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 6d5397ce7f8b26e0
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
new file mode 100644
index 0000000000..f5f0fffab3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
@@ -0,0 +1,7 @@
+> GET /info/thread_safe
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
new file mode 100644
index 0000000000..039aa0341f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tilt
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
new file mode 100644
index 0000000000..2fc37f3a88
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
@@ -0,0 +1,7 @@
+> GET /info/timers
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
new file mode 100644
index 0000000000..be1d206beb
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tins
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
new file mode 100644
index 0000000000..56ebce0f3f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tlsmail
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
new file mode 100644
index 0000000000..a8492ade82
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tool
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
new file mode 100644
index 0000000000..52f2db382f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
@@ -0,0 +1,7 @@
+> GET /info/treetop
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
new file mode 100644
index 0000000000..224cab6988
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
@@ -0,0 +1,7 @@
+> GET /info/trollop
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
new file mode 100644
index 0000000000..0175216dd1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
@@ -0,0 +1,7 @@
+> GET /info/tzinfo
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
new file mode 100644
index 0000000000..9fe5b0c30a
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
@@ -0,0 +1,7 @@
+> GET /info/uber
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
new file mode 100644
index 0000000000..f67c99558f
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
@@ -0,0 +1,7 @@
+> GET /info/unf
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
new file mode 100644
index 0000000000..77598fe741
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
@@ -0,0 +1,7 @@
+> GET /info/unf_ext
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
new file mode 100644
index 0000000000..9d4f27056b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
@@ -0,0 +1,7 @@
+> GET /info/unicode-display_width
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
new file mode 100644
index 0000000000..5daed37793
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
@@ -0,0 +1,7 @@
+> GET /info/uuidtools
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
new file mode 100644
index 0000000000..f22bf57945
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
@@ -0,0 +1,7 @@
+> GET /info/vegas
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/install options/retry,path,disable_shared_gems,spec_run,plugins,trampoline_disable d59b382d069fc94f
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
new file mode 100644
index 0000000000..858ceea38d
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
@@ -0,0 +1,7 @@
+> GET /info/virtus
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 53c83f3e5793993c
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
new file mode 100644
index 0000000000..f6a7eb9098
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
@@ -0,0 +1,7 @@
+> GET /info/weakling
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
new file mode 100644
index 0000000000..06463439e0
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
@@ -0,0 +1,7 @@
+> GET /info/websocket-driver
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
new file mode 100644
index 0000000000..4757f24824
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
@@ -0,0 +1,7 @@
+> GET /info/websocket-extensions
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
new file mode 100644
index 0000000000..45eaa04e55
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
@@ -0,0 +1,7 @@
+> GET /info/websocket
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
new file mode 100644
index 0000000000..9d47c2709b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
@@ -0,0 +1,7 @@
+> GET /info/win32-api
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
new file mode 100644
index 0000000000..6a11013349
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
@@ -0,0 +1,7 @@
+> GET /info/win32-dir
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
new file mode 100644
index 0000000000..d89f956691
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
@@ -0,0 +1,7 @@
+> GET /info/win32-security
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
new file mode 100644
index 0000000000..37acaa6088
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
@@ -0,0 +1,7 @@
+> GET /info/win32console
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
new file mode 100644
index 0000000000..30402b17e0
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
@@ -0,0 +1,7 @@
+> GET /info/windows-api
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
new file mode 100644
index 0000000000..31c1180ac7
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
@@ -0,0 +1,7 @@
+> GET /info/windows-pr
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
new file mode 100644
index 0000000000..5cc409d8db
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
@@ -0,0 +1,7 @@
+> GET /info/xpath
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
new file mode 100644
index 0000000000..ec73805dff
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
@@ -0,0 +1,7 @@
+> GET /versions
+> accept-encoding: gzip
+> accept: */*
+> user-agent: bundler/1.15.0.pre.2 rubygems/2.6.11 ruby/2.4.1 (x86_64-apple-darwin16.5.0) command/lock options/spec_run,plugins,trampoline_disable 7edfb7e0938d0c35
+> connection: keep-alive
+> keep-alive: 30
+> host: index.rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
new file mode 100644
index 0000000000..c078ac79e1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/activesupport-3.2.12.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
new file mode 100644
index 0000000000..b87c281d96
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/activesupport-3.2.22.5.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-1.12.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-1.12.3.gem/GET/request
new file mode 100644
index 0000000000..af03dfd189
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-1.12.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/bundler-1.12.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.3.gem/GET/request
new file mode 100644
index 0000000000..6b0ccfc8c1
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/diff-lcs-1.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
new file mode 100644
index 0000000000..5226c62cd2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/faker-1.1.2.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
new file mode 100644
index 0000000000..6e27eac32c
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/i18n-0.6.11.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.8.1.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.8.1.gem/GET/request
new file mode 100644
index 0000000000..04e800510b
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.8.1.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/i18n-0.8.1.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.0.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.0.gem/GET/request
new file mode 100644
index 0000000000..8e82694d05
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.0.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/mono_logger-1.1.0.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.12.1.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.12.1.gem/GET/request
new file mode 100644
index 0000000000..fa8e059e91
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.12.1.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/multi_json-1.12.1.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.0.1.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.0.1.gem/GET/request
new file mode 100644
index 0000000000..9d96c013bc
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.0.1.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/rack-1.0.1.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.6.5.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.6.5.gem/GET/request
new file mode 100644
index 0000000000..4ae9745db3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-1.6.5.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/rack-1.6.5.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.1.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.1.gem/GET/request
new file mode 100644
index 0000000000..ed63e334fa
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.1.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/rack-2.0.1.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-1.5.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-1.5.3.gem/GET/request
new file mode 100644
index 0000000000..03a85b2308
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-1.5.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/rack-protection-1.5.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-3.3.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-3.3.3.gem/GET/request
new file mode 100644
index 0000000000..7d5ad5ad23
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-3.3.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/redis-3.3.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.5.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.5.3.gem/GET/request
new file mode 100644
index 0000000000..f928e29dd3
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.5.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/redis-namespace-1.5.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
new file mode 100644
index 0000000000..91c5562047
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/resque-1.24.1.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
new file mode 100644
index 0000000000..0237de2859
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/resque-scheduler-2.2.0.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
new file mode 100644
index 0000000000..a8428bc9f6
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/rufus-scheduler-2.0.24.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-1.4.8.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-1.4.8.gem/GET/request
new file mode 100644
index 0000000000..ee93e5d100
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-1.4.8.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/sinatra-1.4.8.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/thread_safe-0.3.6.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/thread_safe-0.3.6.gem/GET/request
new file mode 100644
index 0000000000..51ddc098ae
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/thread_safe-0.3.6.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/thread_safe-0.3.6.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.7.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.7.gem/GET/request
new file mode 100644
index 0000000000..c329c7b4c2
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.7.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/tilt-2.0.7.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-1.2.3.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-1.2.3.gem/GET/request
new file mode 100644
index 0000000000..df7b05b346
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-1.2.3.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/tzinfo-1.2.3.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
new file mode 100644
index 0000000000..562c01e339
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
@@ -0,0 +1,7 @@
+> GET /gems/vegas-0.1.11.gem
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request
new file mode 100644
index 0000000000..ef6eab21ce
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/quick/Marshal.4.8/bundler-1.12.3.gemspec.rz/GET/request
@@ -0,0 +1,7 @@
+> GET /quick/Marshal.4.8/bundler-1.12.3.gemspec.rz
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/specs.4.8.gz/GET/request b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/specs.4.8.gz/GET/request
new file mode 100644
index 0000000000..ca0f179471
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr_cassettes/realworld/rubygems.org/specs.4.8.gz/GET/request
@@ -0,0 +1,7 @@
+> GET /specs.4.8.gz
+> accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+> accept: */*
+> user-agent: Ruby
+> connection: keep-alive
+> keep-alive: 30
+> host: rubygems.org \ No newline at end of file
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
new file mode 100644
index 0000000000..f39b2c6d53
--- /dev/null
+++ b/spec/bundler/support/artifice/windows.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+include Spec::Path
+
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/artifice*/lib")].first.to_s
+$LOAD_PATH.unshift(*Dir[base_system_gems.join("gems/rack-*/lib")])
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/tilt*/lib")].first.to_s
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/sinatra*/lib")].first.to_s
+require "artifice"
+require "sinatra/base"
+
+Artifice.deactivate
+
+class Windows < Sinatra::Base
+ set :raise_errors, true
+ set :show_exceptions, false
+
+ helpers do
+ def gem_repo
+ Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
+ end
+ end
+
+ files = ["specs.4.8.gz",
+ "prerelease_specs.4.8.gz",
+ "quick/Marshal.4.8/rcov-1.0-mswin32.gemspec.rz",
+ "gems/rcov-1.0-mswin32.gem"]
+
+ files.each do |file|
+ get "/#{file}" do
+ File.read gem_repo.join(file)
+ end
+ end
+
+ get "/gems/rcov-1.0-x86-mswin32.gem" do
+ halt 404
+ end
+
+ get "/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/versions" do
+ halt 500
+ end
+end
+
+Artifice.activate_with(Windows)
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
new file mode 100644
index 0000000000..97134a045a
--- /dev/null
+++ b/spec/bundler/support/builders.rb
@@ -0,0 +1,819 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+require "shellwords"
+
+module Spec
+ module Builders
+ def self.constantize(name)
+ name.delete("-").upcase
+ end
+
+ def v(version)
+ Gem::Version.new(version)
+ end
+
+ def pl(platform)
+ Gem::Platform.new(platform)
+ end
+
+ def build_repo1
+ build_repo gem_repo1 do
+ build_gem "rack", %w[0.9.1 1.0.0] do |s|
+ s.executables = "rackup"
+ s.post_install_message = "Rack's post install message"
+ end
+
+ build_gem "thin" do |s|
+ s.add_dependency "rack"
+ s.post_install_message = "Thin's post install message"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ s.post_install_message = "Rack-obama's post install message"
+ end
+
+ build_gem "rack_middleware", "1.0" do |s|
+ s.add_dependency "rack", "0.9.1"
+ end
+
+ build_gem "rails", "2.3.2" do |s|
+ s.executables = "rails"
+ s.add_dependency "rake", "10.0.2"
+ s.add_dependency "actionpack", "2.3.2"
+ s.add_dependency "activerecord", "2.3.2"
+ s.add_dependency "actionmailer", "2.3.2"
+ s.add_dependency "activeresource", "2.3.2"
+ end
+ build_gem "actionpack", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activerecord", ["2.3.1", "2.3.2"] do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "actionmailer", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activeresource", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activesupport", %w[1.2.3 2.3.2 2.3.5]
+
+ build_gem "activemerchant" do |s|
+ s.add_dependency "activesupport", ">= 2.0.0"
+ end
+
+ build_gem "rails_fail" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+
+ build_gem "missing_dep" do |s|
+ s.add_dependency "not_here"
+ end
+
+ build_gem "rspec", "1.2.7", :no_default => true do |s|
+ s.write "lib/spec.rb", "SPEC = '1.2.7'"
+ end
+
+ build_gem "rack-test", :no_default => true do |s|
+ s.write "lib/rack/test.rb", "RACK_TEST = '1.0'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = Bundler.local_platform
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "java"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 JAVA'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "ruby"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-mswin32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 MSWIN'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-mingw32"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-darwin-100"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86-darwin-100'"
+ end
+
+ build_gem "only_java", "1.0" do |s|
+ s.platform = "java"
+ s.write "lib/only_java.rb", "ONLY_JAVA = '1.0.0 JAVA'"
+ end
+
+ build_gem "only_java", "1.1" do |s|
+ s.platform = "java"
+ s.write "lib/only_java.rb", "ONLY_JAVA = '1.1.0 JAVA'"
+ end
+
+ build_gem "nokogiri", "1.4.2"
+ build_gem "nokogiri", "1.4.2" do |s|
+ s.platform = "java"
+ s.write "lib/nokogiri.rb", "NOKOGIRI = '1.4.2 JAVA'"
+ s.add_dependency "weakling", ">= 0.0.3"
+ end
+
+ build_gem "laduradura", "5.15.2"
+ build_gem "laduradura", "5.15.2" do |s|
+ s.platform = "java"
+ s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
+ end
+ build_gem "laduradura", "5.15.3" do |s|
+ s.platform = "java"
+ s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
+ end
+
+ build_gem "weakling", "0.0.3"
+
+ build_gem "terranova", "8"
+
+ build_gem "duradura", "7.0"
+
+ build_gem "multiple_versioned_deps" do |s|
+ s.add_dependency "weakling", ">= 0.0.1", "< 0.1"
+ end
+
+ build_gem "not_released", "1.0.pre"
+
+ build_gem "has_prerelease", "1.0"
+ build_gem "has_prerelease", "1.1.pre"
+
+ build_gem "with_development_dependency" do |s|
+ s.add_development_dependency "activesupport", "= 2.3.5"
+ end
+
+ build_gem "with_license" do |s|
+ s.license = "MIT"
+ end
+
+ build_gem "with_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/implicit_rake_dep.rb", "w") do |f|
+ f.puts "IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "another_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
+ f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "very_simple_binary", &:add_c_extension
+ build_gem "simple_binary", &:add_c_extension
+
+ build_gem "bundler", "0.9" do |s|
+ s.executables = "bundle"
+ s.write "bin/bundle", "puts 'FAIL'"
+ end
+
+ # The bundler 0.8 gem has a rubygems plugin that always loads :(
+ build_gem "bundler", "0.8.1" do |s|
+ s.write "lib/bundler/omg.rb", ""
+ s.write "lib/rubygems_plugin.rb", "require 'bundler/omg' ; puts 'FAIL'"
+ end
+
+ build_gem "bundler_dep" do |s|
+ s.add_dependency "bundler"
+ end
+
+ # The yard gem iterates over Gem.source_index looking for plugins
+ build_gem "yard" do |s|
+ s.write "lib/yard.rb", <<-Y
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.10")
+ specs = Gem::Specification
+ else
+ specs = Gem.source_index.find_name('')
+ end
+ specs.sort_by(&:name).each do |gem|
+ puts gem.full_name
+ end
+ Y
+ end
+
+ # The rcov gem is platform mswin32, but has no arch
+ build_gem "rcov" do |s|
+ s.platform = Gem::Platform.new([nil, "mswin32", nil])
+ s.write "lib/rcov.rb", "RCOV = '1.0.0'"
+ end
+
+ build_gem "net-ssh"
+ build_gem "net-sftp", "1.1.1" do |s|
+ s.add_dependency "net-ssh", ">= 1.0.0", "< 1.99.0"
+ end
+
+ # Test complicated gem dependencies for install
+ build_gem "net_a" do |s|
+ s.add_dependency "net_b"
+ s.add_dependency "net_build_extensions"
+ end
+
+ build_gem "net_b"
+
+ build_gem "net_build_extensions" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/net_build_extensions.rb", "w") do |f|
+ f.puts "NET_BUILD_EXTENSIONS = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "net_c" do |s|
+ s.add_dependency "net_a"
+ s.add_dependency "net_d"
+ end
+
+ build_gem "net_d"
+
+ build_gem "net_e" do |s|
+ s.add_dependency "net_d"
+ end
+
+ # Capistrano did this (at least until version 2.5.10)
+ # RubyGems 2.2 doesn't allow the specifying of a dependency twice
+ # See https://github.com/rubygems/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
+ build_gem "double_deps", "1.0", :skip_validation => true do |s|
+ s.add_dependency "net-ssh", ">= 1.0.0"
+ s.add_dependency "net-ssh"
+ end
+
+ build_gem "foo"
+
+ # A minimal fake pry console
+ build_gem "pry" do |s|
+ s.write "lib/pry.rb", <<-RUBY
+ class Pry
+ class << self
+ def toplevel_binding
+ unless defined?(@toplevel_binding) && @toplevel_binding
+ TOPLEVEL_BINDING.eval %{
+ def self.__pry__; binding; end
+ Pry.instance_variable_set(:@toplevel_binding, __pry__)
+ class << self; undef __pry__; end
+ }
+ end
+ @toplevel_binding.eval('private')
+ @toplevel_binding
+ end
+
+ def __pry__
+ while line = gets
+ begin
+ puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
+ rescue Exception => e
+ puts "\#{e.class}: \#{e.message}"
+ puts e.backtrace.first
+ end
+ end
+ end
+ alias start __pry__
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ def build_repo2(&blk)
+ FileUtils.rm_rf gem_repo2
+ FileUtils.cp_r gem_repo1, gem_repo2
+ update_repo2(&blk) if block_given?
+ end
+
+ def build_repo3
+ build_repo gem_repo3 do
+ build_gem "rack"
+ end
+ FileUtils.rm_rf Dir[gem_repo3("prerelease*")]
+ end
+
+ # A repo that has no pre-installed gems included. (The caller completely
+ # determines the contents with the block.)
+ def build_repo4(&blk)
+ FileUtils.rm_rf gem_repo4
+ build_repo(gem_repo4, &blk)
+ end
+
+ def update_repo4(&blk)
+ update_repo(gem_repo4, &blk)
+ end
+
+ def update_repo2
+ update_repo gem_repo2 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ yield if block_given?
+ end
+ end
+
+ def build_security_repo
+ build_repo security_repo do
+ build_gem "rack"
+
+ build_gem "signed_gem" do |s|
+ cert = "signing-cert.pem"
+ pkey = "signing-pkey.pem"
+ s.write cert, TEST_CERT
+ s.write pkey, TEST_PKEY
+ s.signing_key = pkey
+ s.cert_chain = [cert]
+ end
+ end
+ end
+
+ def build_repo(path, &blk)
+ return if File.directory?(path)
+ rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+
+ if rake_path.nil?
+ Spec::Path.base_system_gems.rmtree
+ Spec::Rubygems.setup
+ rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+ end
+
+ if rake_path
+ FileUtils.mkdir_p("#{path}/gems")
+ FileUtils.cp rake_path, "#{path}/gems/"
+ else
+ abort "Your test gems are missing! Run `rm -rf #{tmp}` and try again."
+ end
+
+ update_repo(path, &blk)
+ end
+
+ def update_repo(path)
+ if path == gem_repo1 && caller.first.split(" ").last == "`build_repo`"
+ raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead"
+ end
+ return unless block_given?
+ @_build_path = "#{path}/gems"
+ @_build_repo = File.basename(path)
+ yield
+ with_gem_path_as Path.base_system_gems do
+ Dir.chdir(path) { gem_command! :generate_index }
+ end
+ ensure
+ @_build_path = nil
+ @_build_repo = nil
+ end
+
+ def build_index(&block)
+ index = Bundler::Index.new
+ IndexBuilder.run(index, &block) if block_given?
+ index
+ end
+
+ def build_spec(name, version = "0.0.1", platform = nil, &block)
+ Array(version).map do |v|
+ Gem::Specification.new do |s|
+ s.name = name
+ s.version = Gem::Version.new(v)
+ s.platform = platform
+ s.authors = ["no one in particular"]
+ s.summary = "a gemspec used only for testing"
+ DepBuilder.run(s, &block) if block_given?
+ end
+ end
+ end
+
+ def build_dep(name, requirements = Gem::Requirement.default, type = :runtime)
+ Bundler::Dependency.new(name, :version => requirements)
+ end
+
+ def build_lib(name, *args, &blk)
+ build_with(LibBuilder, name, args, &blk)
+ end
+
+ def build_gem(name, *args, &blk)
+ build_with(GemBuilder, name, args, &blk)
+ end
+
+ def build_git(name, *args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ builder = opts[:bare] ? GitBareBuilder : GitBuilder
+ spec = build_with(builder, name, args, &block)
+ GitReader.new(opts[:path] || lib_path(spec.full_name))
+ end
+
+ def update_git(name, *args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ spec = build_with(GitUpdater, name, args, &block)
+ GitReader.new(opts[:path] || lib_path(spec.full_name))
+ end
+
+ def build_plugin(name, *args, &blk)
+ build_with(PluginBuilder, name, args, &blk)
+ end
+
+ private
+
+ def build_with(builder, name, args, &blk)
+ @_build_path ||= nil
+ @_build_repo ||= nil
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ versions = args.last || "1.0"
+ spec = nil
+
+ options[:path] ||= @_build_path
+ options[:source] ||= @_build_repo
+
+ Array(versions).each do |version|
+ spec = builder.new(self, name, version)
+ spec.authors = ["no one"] if !spec.authors || spec.authors.empty?
+ yield spec if block_given?
+ spec._build(options)
+ end
+
+ spec
+ end
+
+ class IndexBuilder
+ include Builders
+
+ def self.run(index, &block)
+ new(index).run(&block)
+ end
+
+ def initialize(index)
+ @index = index
+ end
+
+ def run(&block)
+ instance_eval(&block)
+ end
+
+ def gem(*args, &block)
+ build_spec(*args, &block).each do |s|
+ @index << s
+ end
+ end
+
+ def platforms(platforms)
+ platforms.split(/\s+/).each do |platform|
+ platform.gsub!(/^(mswin32)$/, 'x86-\1')
+ yield Gem::Platform.new(platform)
+ end
+ end
+
+ def versions(versions)
+ versions.split(/\s+/).each {|version| yield v(version) }
+ end
+ end
+
+ class DepBuilder
+ include Builders
+
+ def self.run(spec, &block)
+ new(spec).run(&block)
+ end
+
+ def initialize(spec)
+ @spec = spec
+ end
+
+ def run(&block)
+ instance_eval(&block)
+ end
+
+ def runtime(name, requirements)
+ @spec.add_runtime_dependency(name, requirements)
+ end
+
+ def development(name, requirements)
+ @spec.add_development_dependency(name, requirements)
+ end
+
+ def required_ruby_version=(*reqs)
+ @spec.required_ruby_version = *reqs
+ end
+
+ alias_method :dep, :runtime
+ end
+
+ class LibBuilder
+ def initialize(context, name, version)
+ @context = context
+ @name = name
+ @spec = Gem::Specification.new do |s|
+ s.name = name
+ s.version = version
+ s.summary = "This is just a fake gem for testing"
+ s.description = "This is a completely fake gem, for testing purposes."
+ s.author = "no one"
+ s.email = "foo@bar.baz"
+ s.homepage = "http://example.com"
+ s.license = "MIT"
+ end
+ @files = {}
+ end
+
+ def method_missing(*args, &blk)
+ @spec.send(*args, &blk)
+ end
+
+ def write(file, source = "")
+ @files[file] = source
+ end
+
+ def executables=(val)
+ @spec.executables = Array(val)
+ @spec.executables.each do |file|
+ executable = "#{@spec.bindir}/#{file}"
+ shebang = if Bundler.current_ruby.jruby?
+ "#!/usr/bin/env jruby\n"
+ else
+ "#!/usr/bin/env ruby\n"
+ end
+ @spec.files << executable
+ write executable, "#{shebang}require '#{@name}' ; puts #{Builders.constantize(@name)}"
+ end
+ end
+
+ def add_c_extension
+ require_paths << "ext"
+ extensions << "ext/extconf.rb"
+ write "ext/extconf.rb", <<-RUBY
+ require "mkmf"
+
+
+ # exit 1 unless with_config("simple")
+
+ extension_name = "#{name}_c"
+ if extra_lib_dir = with_config("ext-lib")
+ # add extra libpath if --with-ext-lib is
+ # passed in as a build_arg
+ dir_config extension_name, nil, extra_lib_dir
+ else
+ dir_config extension_name
+ end
+ create_makefile extension_name
+ RUBY
+ write "ext/#{name}.c", <<-C
+ #include "ruby.h"
+
+ void Init_#{name}_c() {
+ rb_define_module("#{Builders.constantize(name)}_IN_C");
+ }
+ C
+ end
+
+ def _build(options)
+ path = options[:path] || _default_path
+
+ if options[:rubygems_version]
+ @spec.rubygems_version = options[:rubygems_version]
+ def @spec.mark_version; end
+
+ def @spec.validate(*); end
+ end
+
+ case options[:gemspec]
+ when false
+ # do nothing
+ when :yaml
+ @files["#{name}.gemspec"] = @spec.to_yaml
+ else
+ @files["#{name}.gemspec"] = @spec.to_ruby
+ end
+
+ unless options[:no_default]
+ gem_source = options[:source] || "path@#{path}"
+ @files = _default_files.
+ merge("lib/#{name}/source.rb" => "#{Builders.constantize(name)}_SOURCE = #{gem_source.to_s.dump}").
+ merge(@files)
+ end
+
+ @spec.authors = ["no one"]
+
+ @files.each do |file, source|
+ file = Pathname.new(path).join(file)
+ FileUtils.mkdir_p(file.dirname)
+ File.open(file, "w") {|f| f.puts source }
+ end
+ @spec.files = @files.keys
+ path
+ end
+
+ def _default_files
+ @_default_files ||= begin
+ platform_string = " #{@spec.platform}" unless @spec.platform == Gem::Platform::RUBY
+ { "lib/#{name}.rb" => "#{Builders.constantize(name)} = '#{version}#{platform_string}'" }
+ end
+ end
+
+ def _default_path
+ @context.tmp("libs", @spec.full_name)
+ end
+ end
+
+ class GitBuilder < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ source = options[:source] || "git@#{path}"
+ super(options.merge(:path => path, :source => source))
+ Dir.chdir(path) do
+ `git init`
+ `git add *`
+ `git config user.email "lol@wut.com"`
+ `git config user.name "lolwut"`
+ `git commit -m 'OMG INITIAL COMMIT'`
+ end
+ end
+ end
+
+ class GitBareBuilder < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ super(options.merge(:path => path))
+ Dir.chdir(path) do
+ `git init --bare`
+ end
+ end
+ end
+
+ class GitUpdater < LibBuilder
+ def silently(str)
+ `#{str} 2>#{Bundler::NULL}`
+ end
+
+ def _build(options)
+ libpath = options[:path] || _default_path
+ update_gemspec = options[:gemspec] || false
+ source = options[:source] || "git@#{libpath}"
+
+ Dir.chdir(libpath) do
+ silently "git checkout master"
+
+ if branch = options[:branch]
+ raise "You can't specify `master` as the branch" if branch == "master"
+ escaped_branch = Shellwords.shellescape(branch)
+
+ if `git branch | grep #{escaped_branch}`.empty?
+ silently("git branch #{escaped_branch}")
+ end
+
+ silently("git checkout #{escaped_branch}")
+ elsif tag = options[:tag]
+ `git tag #{Shellwords.shellescape(tag)}`
+ elsif options[:remote]
+ silently("git remote add origin file://#{options[:remote]}")
+ elsif options[:push]
+ silently("git push origin #{options[:push]}")
+ end
+
+ current_ref = `git rev-parse HEAD`.strip
+ _default_files.keys.each do |path|
+ _default_files[path] += "\n#{Builders.constantize(name)}_PREV_REF = '#{current_ref}'"
+ end
+ super(options.merge(:path => libpath, :gemspec => update_gemspec, :source => source))
+ `git add *`
+ `git commit -m "BUMP"`
+ end
+ end
+ end
+
+ class GitReader
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def ref_for(ref, len = nil)
+ ref = git "rev-parse #{ref}"
+ ref = ref[0..len] if len
+ ref
+ end
+
+ private
+
+ def git(cmd)
+ Bundler::SharedHelpers.with_clean_git_env do
+ Dir.chdir(@path) { `git #{cmd}`.strip }
+ end
+ end
+ end
+
+ class GemBuilder < LibBuilder
+ def _build(opts)
+ lib_path = super(opts.merge(:path => @context.tmp(".tmp/#{@spec.full_name}"), :no_default => opts[:no_default]))
+ destination = opts[:path] || _default_path
+ Dir.chdir(lib_path) do
+ FileUtils.mkdir_p(destination)
+
+ @spec.authors = ["that guy"] if !@spec.authors || @spec.authors.empty?
+
+ Bundler.rubygems.build(@spec, opts[:skip_validation])
+ end
+ gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path)
+ if opts[:to_system]
+ @context.system_gems gem_path, :keep_path => true
+ elsif opts[:to_bundle]
+ @context.system_gems gem_path, :path => :bundle_path, :keep_path => true
+ else
+ FileUtils.mv(gem_path, destination)
+ end
+ end
+
+ def _default_path
+ @context.gem_repo1("gems")
+ end
+ end
+
+ class PluginBuilder < GemBuilder
+ def _default_files
+ @_default_files ||= super.merge("plugins.rb" => "")
+ end
+ end
+
+ TEST_CERT = <<-CERT.gsub(/^\s*/, "")
+ -----BEGIN CERTIFICATE-----
+ MIIDMjCCAhqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAnMQwwCgYDVQQDDAN5b3Ux
+ FzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTE1MDIwODAwMTIyM1oXDTQyMDYy
+ NTAwMTIyM1owJzEMMAoGA1UEAwwDeW91MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBs
+ ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANlvFdpN43c4DMS9Jo06
+ m0a7k3bQ3HWQ1yrYhZMi77F1F73NpBknYHIzDktQpGn6hs/4QFJT4m4zNEBF47UL
+ jHU5nTK5rjkS3niGYUjvh3ZEzVeo9zHUlD/UwflDo4ALl3TSo2KY/KdPS/UTdLXL
+ ajkQvaVJtEDgBPE3DPhlj5whp+Ik3mDHej7qpV6F502leAwYaFyOtlEG/ZGNG+nZ
+ L0clH0j77HpP42AylHDi+vakEM3xcjo9BeWQ6Vkboic93c9RTt6CWBWxMQP7Nol1
+ MOebz9XOSQclxpxWteXNfPRtMdAhmRl76SMI8ywzThNPpa4EH/yz34ftebVOgKyM
+ nd0CAwEAAaNpMGcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFA7D
+ n9qo0np23qi3aOYuAAPn/5IdMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYGA1Ud
+ EgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBBQUAA4IBAQA7Gyk62sWOUX/N
+ vk4tJrgKESph6Ns8+E36A7n3jt8zCep8ldzMvwTWquf9iqhsC68FilEoaDnUlWw7
+ d6oNuaFkv7zfrWGLlvqQJC+cu2X5EpcCksg5oRp8VNbwJysJ6JgwosxzROII8eXc
+ R+j1j6mDvQYqig2QOnzf480pjaqbP+tspfDFZbhKPrgM3Blrb3ZYuFpv4zkqI7aB
+ 6fuk2DUhNO1CuwrJA84TqC+jGo73bDKaT5hrIDiaJRrN5+zcWja2uEWrj5jSbep4
+ oXdEdyH73hOHMBP40uds3PqnUsxEJhzjB2sCCe1geV24kw9J4m7EQXPVkUKDgKrt
+ LlpDmOoo
+ -----END CERTIFICATE-----
+ CERT
+
+ TEST_PKEY = <<-PKEY.gsub(/^\s*/, "")
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEA2W8V2k3jdzgMxL0mjTqbRruTdtDcdZDXKtiFkyLvsXUXvc2k
+ GSdgcjMOS1CkafqGz/hAUlPibjM0QEXjtQuMdTmdMrmuORLeeIZhSO+HdkTNV6j3
+ MdSUP9TB+UOjgAuXdNKjYpj8p09L9RN0tctqORC9pUm0QOAE8TcM+GWPnCGn4iTe
+ YMd6PuqlXoXnTaV4DBhoXI62UQb9kY0b6dkvRyUfSPvsek/jYDKUcOL69qQQzfFy
+ Oj0F5ZDpWRuiJz3dz1FO3oJYFbExA/s2iXUw55vP1c5JByXGnFa15c189G0x0CGZ
+ GXvpIwjzLDNOE0+lrgQf/LPfh+15tU6ArIyd3QIDAQABAoIBACbDqz20TS1gDMa2
+ gj0DidNedbflHKjJHdNBru7Ad8NHgOgR1YO2hXdWquG6itVqGMbTF4SV9/R1pIcg
+ 7qvEV1I+50u31tvOBWOvcYCzU48+TO2n7gowQA3xPHPYHzog1uu48fAOHl0lwgD7
+ av9OOK3b0jO5pC08wyTOD73pPWU0NrkTh2+N364leIi1pNuI1z4V+nEuIIm7XpVd
+ 5V4sXidMTiEMJwE6baEDfTjHKaoRndXrrPo3ryIXmcX7Ag1SwAQwF5fBCRToCgIx
+ dszEZB1bJD5gA6r+eGnJLB/F60nK607az5o3EdguoB2LKa6q6krpaRCmZU5svvoF
+ J7xgBPECgYEA8RIzHAQ3zbaibKdnllBLIgsqGdSzebTLKheFuigRotEV3Or/z5Lg
+ k/nVnThWVkTOSRqXTNpJAME6a4KTdcVSxYP+SdZVO1esazHrGb7xPVb7MWSE1cqp
+ WEk3Yy8OUOPoPQMc4dyGzd30Mi8IBB6gnFIYOTrpUo0XtkBv8rGGhfsCgYEA5uYn
+ 6QgL4NqNT84IXylmMb5ia3iBt6lhxI/A28CDtQvfScl4eYK0IjBwdfG6E1vJgyzg
+ nJzv3xEVo9bz+Kq7CcThWpK5JQaPnsV0Q74Wjk0ShHet15txOdJuKImnh5F6lylC
+ GTLR9gnptytfMH/uuw4ws0Q2kcg4l5NHKOWOnAcCgYEAvAwIVkhsB0n59Wu4gCZu
+ FUZENxYWUk/XUyQ6KnZrG2ih90xQ8+iMyqFOIm/52R2fFKNrdoWoALC6E3ct8+ZS
+ pMRLrelFXx8K3it4SwMJR2H8XBEfFW4bH0UtsW7Zafv+AunUs9LETP5gKG1LgXsq
+ qgXX43yy2LQ61O365YPZfdUCgYBVbTvA3MhARbvYldrFEnUL3GtfZbNgdxuD9Mee
+ xig0eJMBIrgfBLuOlqtVB70XYnM4xAbKCso4loKSHnofO1N99siFkRlM2JOUY2tz
+ kMWZmmxKdFjuF0WZ5f/5oYxI/QsFGC+rUQEbbWl56mMKd5qkvEhKWudxoklF0yiV
+ ufC8SwKBgDWb8iWqWN5a/kfvKoxFcDM74UHk/SeKMGAL+ujKLf58F+CbweM5pX9C
+ EUsxeoUEraVWTiyFVNqD81rCdceus9TdBj0ZIK1vUttaRZyrMAwF0uQSfjtxsOpd
+ l69BkyvzjgDPkmOHVGiSZDLi3YDvypbUpo6LOy4v5rVg5U2F/A0v
+ -----END RSA PRIVATE KEY-----
+ PKEY
+ end
+end
diff --git a/spec/bundler/support/code_climate.rb b/spec/bundler/support/code_climate.rb
new file mode 100644
index 0000000000..a15442cabe
--- /dev/null
+++ b/spec/bundler/support/code_climate.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Spec
+ module CodeClimate
+ def self.setup
+ require "codeclimate-test-reporter"
+ ::CodeClimate::TestReporter.start
+ configure_exclusions
+ rescue LoadError
+ # it's fine if CodeClimate isn't set up
+ nil
+ end
+
+ def self.configure_exclusions
+ SimpleCov.start do
+ add_filter "/bin/"
+ add_filter "/lib/bundler/man/"
+ add_filter "/lib/bundler/vendor/"
+ add_filter "/man/"
+ add_filter "/pkg/"
+ add_filter "/spec/"
+ add_filter "/tmp/"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb
new file mode 100644
index 0000000000..556285ac52
--- /dev/null
+++ b/spec/bundler/support/command_execution.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "support/helpers"
+require "support/path"
+
+module Spec
+ CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :stdout, :stderr) do
+ include RSpec::Matchers::Composable
+
+ def to_s
+ c = Shellwords.shellsplit(command.strip).map {|s| s.include?("\n") ? " \\\n <<EOS\n#{s.gsub(/^/, " ").chomp}\nEOS" : Shellwords.shellescape(s) }
+ c = c.reduce("") do |acc, elem|
+ concat = acc + " " + elem
+
+ last_line = concat.match(/.*\z/)[0]
+ if last_line.size >= 100
+ acc + " \\\n " + elem
+ else
+ concat
+ end
+ end
+ "$ #{c.strip}"
+ end
+ alias_method :inspect, :to_s
+
+ def stdboth
+ @stdboth ||= [stderr, stdout].join("\n").strip
+ end
+
+ def bundler_err
+ if Bundler::VERSION.start_with?("1.")
+ stdout
+ else
+ stderr
+ end
+ end
+
+ def to_s_verbose
+ [
+ to_s,
+ stdout,
+ stderr,
+ exitstatus ? "# $? => #{exitstatus}" : "",
+ ].reject(&:empty?).join("\n")
+ end
+
+ def success?
+ return true unless exitstatus
+ exitstatus == 0
+ end
+
+ def failure?
+ return true unless exitstatus
+ exitstatus > 0
+ end
+ end
+end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
new file mode 100644
index 0000000000..b14e4a5943
--- /dev/null
+++ b/spec/bundler/support/hax.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require "rubygems"
+
+module Gem
+ if version = ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"]
+ remove_const(:VERSION) if const_defined?(:VERSION)
+ VERSION = version
+ end
+
+ class Platform
+ @local = new(ENV["BUNDLER_SPEC_PLATFORM"]) if ENV["BUNDLER_SPEC_PLATFORM"]
+ end
+ @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
+
+ if defined?(@path_to_default_spec_map) && !ENV["BUNDLER_SPEC_KEEP_DEFAULT_BUNDLER_GEM"]
+ @path_to_default_spec_map.delete_if do |_path, spec|
+ spec.name == "bundler"
+ end
+ end
+end
+
+if ENV["BUNDLER_SPEC_VERSION"]
+ module Bundler
+ remove_const(:VERSION) if const_defined?(:VERSION)
+ VERSION = ENV["BUNDLER_SPEC_VERSION"].dup
+ end
+end
+
+if ENV["BUNDLER_SPEC_WINDOWS"] == "true"
+ require "bundler/constants"
+
+ module Bundler
+ remove_const :WINDOWS if defined?(WINDOWS)
+ WINDOWS = true
+ end
+end
+
+class Object
+ if ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE != "jruby" && ENV["BUNDLER_SPEC_RUBY_ENGINE"] == "jruby"
+ begin
+ # this has to be done up front because psych will try to load a .jar
+ # if it thinks its on jruby
+ require "psych"
+ rescue LoadError
+ nil
+ end
+ end
+
+ remove_const :RUBY_ENGINE if defined?(RUBY_ENGINE)
+ RUBY_ENGINE = ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+
+ if RUBY_ENGINE == "jruby"
+ remove_const :JRUBY_VERSION if defined?(JRUBY_VERSION)
+ JRUBY_VERSION = ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+ end
+ end
+end
+
+if ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"]
+ $LOADED_FEATURES << File.expand_path("../../../bundler/compatibility_guard.rb", __FILE__)
+ $LOADED_FEATURES << File.expand_path("../../../bundler/compatibility_guard", __FILE__)
+ $LOADED_FEATURES << "bundler/compatibility_guard.rb"
+ $LOADED_FEATURES << "bundler/compatibility_guard"
+ require "bundler/compatibility_guard"
+end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
new file mode 100644
index 0000000000..b027e7a922
--- /dev/null
+++ b/spec/bundler/support/helpers.rb
@@ -0,0 +1,600 @@
+# frozen_string_literal: true
+
+require "open3"
+
+module Spec
+ module Helpers
+ def reset!
+ Dir.glob("#{tmp}/{gems/*,*}", File::FNM_DOTMATCH).each do |dir|
+ next if %w[base remote1 gems rubygems . ..].include?(File.basename(dir))
+ if ENV["BUNDLER_SUDO_TESTS"]
+ `sudo rm -rf "#{dir}"`
+ else
+ FileUtils.rm_rf(dir)
+ end
+ end
+ FileUtils.mkdir_p(home)
+ FileUtils.mkdir_p(tmpdir)
+ Bundler.reset!
+ Bundler.ui = nil
+ Bundler.ui # force it to initialize
+ end
+
+ def self.bang(method)
+ define_method("#{method}!") do |*args, &blk|
+ send(method, *args, &blk).tap do
+ unless last_command.success?
+ raise RuntimeError,
+ "Invoking #{method}!(#{args.map(&:inspect).join(", ")}) failed:\n#{last_command.stdboth}",
+ caller.drop_while {|bt| bt.start_with?(__FILE__) }
+ end
+ end
+ end
+ end
+
+ def the_bundle(*args)
+ TheBundle.new(*args)
+ end
+
+ def last_command
+ @command_executions.last || raise("There is no last command")
+ end
+
+ def out
+ last_command.stdboth
+ end
+
+ def err
+ last_command.stderr
+ end
+
+ def exitstatus
+ last_command.exitstatus
+ end
+
+ def bundle_update_requires_all?
+ Bundler::VERSION.start_with?("1.") ? nil : true
+ end
+
+ def in_app_root(&blk)
+ Dir.chdir(bundled_app, &blk)
+ end
+
+ def in_app_root2(&blk)
+ Dir.chdir(bundled_app2, &blk)
+ end
+
+ def in_app_root_custom(root, &blk)
+ Dir.chdir(root, &blk)
+ end
+
+ def run(cmd, *args)
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ groups = args.map(&:inspect).join(", ")
+ setup = "require 'rubygems' ; require 'bundler' ; Bundler.setup(#{groups})\n"
+ ruby(setup + cmd, opts)
+ end
+ bang :run
+
+ def load_error_run(ruby, name, *args)
+ cmd = <<-RUBY
+ begin
+ #{ruby}
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ end
+ RUBY
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ args += [opts]
+ run(cmd, *args)
+ end
+
+ def lib
+ root.join("lib")
+ end
+
+ def spec
+ spec_dir.to_s
+ end
+
+ def bundle(cmd, options = {})
+ with_sudo = options.delete(:sudo)
+ sudo = with_sudo == :preserve_env ? "sudo -E" : "sudo" if with_sudo
+
+ bundle_bin = options.delete("bundle_bin") || bindir.join("bundle")
+
+ if system_bundler = options.delete(:system_bundler)
+ bundle_bin = "-S bundle"
+ end
+
+ env = options.delete(:env) || {}
+ env["PATH"].gsub!("#{Path.root}/exe", "") if env["PATH"] && system_bundler
+
+ requires = options.delete(:requires) || []
+ requires << "support/hax"
+
+ artifice = options.delete(:artifice) do
+ if RSpec.current_example.metadata[:realworld]
+ "vcr"
+ else
+ "fail"
+ end
+ end
+ if artifice
+ requires << File.expand_path("../artifice/#{artifice}", __FILE__)
+ end
+
+ requires_str = requires.map {|r| "-r#{r}" }.join(" ")
+
+ load_path = []
+ load_path << lib unless system_bundler
+ load_path << spec
+ load_path_str = "-I#{load_path.join(File::PATH_SEPARATOR)}"
+
+ env = env.map {|k, v| "#{k}='#{v}'" }.join(" ")
+
+ args = options.map do |k, v|
+ case v
+ when nil
+ next
+ when true
+ " --#{k}"
+ when false
+ " --no-#{k}"
+ else
+ " --#{k} #{v}"
+ end
+ end.join
+
+ cmd = "#{env} #{sudo} #{Gem.ruby} #{load_path_str} #{requires_str} #{bundle_bin} #{cmd}#{args}"
+ sys_exec(cmd) {|i, o, thr| yield i, o, thr if block_given? }
+ end
+ bang :bundle
+
+ def forgotten_command_line_options(options)
+ remembered = Bundler::VERSION.split(".", 2).first == "1"
+ options = options.map do |k, v|
+ k = Array(k)[remembered ? 0 : -1]
+ v = '""' if v && v.to_s.empty?
+ [k, v]
+ end
+ return Hash[options] if remembered
+ options.each do |k, v|
+ if v.nil?
+ bundle! "config --delete #{k}"
+ else
+ bundle! "config --local #{k} #{v}"
+ end
+ end
+ {}
+ end
+
+ def bundler(cmd, options = {})
+ options["bundle_bin"] = bindir.join("bundler")
+ bundle(cmd, options)
+ end
+
+ def bundle_ruby(options = {})
+ options["bundle_bin"] = bindir.join("bundle_ruby")
+ bundle("", options)
+ end
+
+ def ruby(ruby, options = {})
+ env = (options.delete(:env) || {}).map {|k, v| "#{k}='#{v}' " }.join
+ ruby = ruby.gsub(/["`\$]/) {|m| "\\#{m}" }
+ lib_option = options[:no_lib] ? "" : " -I#{lib}"
+ sys_exec(%(#{env}#{Gem.ruby}#{lib_option} -e "#{ruby}"))
+ end
+ bang :ruby
+
+ def load_error_ruby(ruby, name, opts = {})
+ ruby(<<-R)
+ begin
+ #{ruby}
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR"# if e.message.include?("-- #{name}")
+ end
+ R
+ end
+
+ def gembin(cmd)
+ lib = File.expand_path("../../../lib", __FILE__)
+ old = ENV["RUBYOPT"]
+ ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -I#{lib}"
+ cmd = bundled_app("bin/#{cmd}") unless cmd.to_s.include?("/")
+ sys_exec(cmd.to_s)
+ ensure
+ ENV["RUBYOPT"] = old
+ end
+
+ def gem_command(command, args = "", options = {})
+ if command == :exec && !options[:no_quote]
+ args = args.gsub(/(?=")/, "\\")
+ args = %("#{args}")
+ end
+ gem = ENV["BUNDLE_GEM"] || "#{Gem.ruby} -rrubygems -S gem --backtrace"
+ sys_exec("#{gem} #{command} #{args}")
+ end
+ bang :gem_command
+
+ def rake
+ "#{Gem.ruby} -S #{ENV["GEM_PATH"]}/bin/rake"
+ end
+
+ def sys_exec(cmd)
+ command_execution = CommandExecution.new(cmd.to_s, Dir.pwd)
+
+ Open3.popen3(cmd.to_s) do |stdin, stdout, stderr, wait_thr|
+ yield stdin, stdout, wait_thr if block_given?
+ stdin.close
+
+ command_execution.exitstatus = wait_thr && wait_thr.value.exitstatus
+ command_execution.stdout = Thread.new { stdout.read }.value.strip
+ command_execution.stderr = Thread.new { stderr.read }.value.strip
+ end
+
+ (@command_executions ||= []) << command_execution
+
+ command_execution.stdout
+ end
+ bang :sys_exec
+
+ def config(config = nil, path = bundled_app(".bundle/config"))
+ return YAML.load_file(path) unless config
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path, "w") do |f|
+ f.puts config.to_yaml
+ end
+ config
+ end
+
+ def global_config(config = nil)
+ config(config, home(".bundle/config"))
+ end
+
+ def create_file(*args)
+ path = bundled_app(args.shift)
+ path = args.shift if args.first.is_a?(Pathname)
+ str = args.shift || ""
+ path.dirname.mkpath
+ File.open(path.to_s, "w") do |f|
+ f.puts strip_whitespace(str)
+ end
+ end
+
+ def gemfile(*args)
+ if args.empty?
+ File.open("Gemfile", "r", &:read)
+ else
+ create_file("Gemfile", *args)
+ end
+ end
+
+ def lockfile(*args)
+ if args.empty?
+ File.open("Gemfile.lock", "r", &:read)
+ else
+ create_file("Gemfile.lock", *args)
+ end
+ end
+
+ def strip_whitespace(str)
+ # Trim the leading spaces
+ spaces = str[/\A\s+/, 0] || ""
+ str.gsub(/^#{spaces}/, "")
+ end
+
+ def normalize_uri_file(str)
+ # URI::File of Ruby 2.6 normalize localhost variable with file protocol.
+ if defined?(URI::File)
+ str.gsub(%r{file:\/\/localhost}, "file://")
+ else
+ str
+ end
+ end
+
+ def install_gemfile(*args)
+ gemfile(*args)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ opts[:retry] ||= 0
+ bundle :install, opts
+ end
+ bang :install_gemfile
+
+ def lock_gemfile(*args)
+ gemfile(*args)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ opts[:retry] ||= 0
+ bundle :lock, opts
+ end
+
+ def install_gems(*gems)
+ options = gems.last.is_a?(Hash) ? gems.pop : {}
+ gem_repo = options.fetch(:gem_repo) { gem_repo1 }
+ gems.each do |g|
+ path = if g == :bundler
+ Dir.chdir(root) { gem_command! :build, gemspec.to_s }
+ bundler_path = if ruby_core?
+ root + "lib/bundler-#{Bundler::VERSION}.gem"
+ else
+ root + "bundler-#{Bundler::VERSION}.gem"
+ end
+ elsif g.to_s =~ %r{\A/.*\.gem\z}
+ g
+ else
+ "#{gem_repo}/gems/#{g}.gem"
+ end
+
+ raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+
+ if Gem::VERSION < "2.0.0"
+ gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies '#{path}'"
+ else
+ gem_command! :install, "--no-document --ignore-dependencies '#{path}'"
+ end
+ bundler_path && bundler_path.rmtree
+ end
+ end
+
+ alias_method :install_gem, :install_gems
+
+ def with_gem_path_as(path)
+ backup = ENV.to_hash
+ ENV["GEM_HOME"] = path.to_s
+ ENV["GEM_PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_GEM_PATH"] = nil
+ yield
+ ensure
+ ENV.replace(backup)
+ end
+
+ def with_path_as(path)
+ backup = ENV.to_hash
+ ENV["PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_PATH"] = nil
+ yield
+ ensure
+ ENV.replace(backup)
+ end
+
+ def with_path_added(path)
+ with_path_as(path.to_s + ":" + ENV["PATH"]) do
+ yield
+ end
+ end
+
+ def break_git!
+ FileUtils.mkdir_p(tmp("broken_path"))
+ File.open(tmp("broken_path/git"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nSTDERR.puts 'This is not the git you are looking for'\nexit 1"
+ end
+
+ ENV["PATH"] = "#{tmp("broken_path")}:#{ENV["PATH"]}"
+ end
+
+ def with_fake_man
+ FileUtils.mkdir_p(tmp("fake_man"))
+ File.open(tmp("fake_man/man"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nputs ARGV.inspect\n"
+ end
+ with_path_added(tmp("fake_man")) { yield }
+ end
+
+ def system_gems(*gems)
+ opts = gems.last.is_a?(Hash) ? gems.last : {}
+ path = opts.fetch(:path, system_gem_path)
+ if path == :bundle_path
+ path = ruby!(<<-RUBY)
+ require "bundler"
+ begin
+ puts Bundler.bundle_path
+ rescue Bundler::GemfileNotFound
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ retry
+ end
+
+ RUBY
+ end
+ gems = gems.flatten
+
+ unless opts[:keep_path]
+ FileUtils.rm_rf(path)
+ FileUtils.mkdir_p(path)
+ end
+
+ Gem.clear_paths
+
+ env_backup = ENV.to_hash
+ ENV["GEM_HOME"] = path.to_s
+ ENV["GEM_PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_GEM_PATH"] = nil
+
+ install_gems(*gems)
+ return unless block_given?
+ begin
+ yield
+ ensure
+ ENV.replace(env_backup)
+ end
+ end
+
+ def realworld_system_gems(*gems)
+ gems = gems.flatten
+
+ FileUtils.rm_rf(system_gem_path)
+ FileUtils.mkdir_p(system_gem_path)
+
+ Gem.clear_paths
+
+ gem_home = ENV["GEM_HOME"]
+ gem_path = ENV["GEM_PATH"]
+ path = ENV["PATH"]
+ ENV["GEM_HOME"] = system_gem_path.to_s
+ ENV["GEM_PATH"] = system_gem_path.to_s
+
+ gems.each do |gem|
+ gem_command :install, "--no-rdoc --no-ri #{gem}"
+ end
+ return unless block_given?
+ begin
+ yield
+ ensure
+ ENV["GEM_HOME"] = gem_home
+ ENV["GEM_PATH"] = gem_path
+ ENV["PATH"] = path
+ end
+ end
+
+ def cache_gems(*gems)
+ gems = gems.flatten
+
+ FileUtils.rm_rf("#{bundled_app}/vendor/cache")
+ FileUtils.mkdir_p("#{bundled_app}/vendor/cache")
+
+ gems.each do |g|
+ path = "#{gem_repo1}/gems/#{g}.gem"
+ raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ FileUtils.cp(path, "#{bundled_app}/vendor/cache")
+ end
+ end
+
+ def simulate_new_machine
+ system_gems []
+ FileUtils.rm_rf system_gem_path
+ FileUtils.rm_rf bundled_app(".bundle")
+ end
+
+ def simulate_platform(platform)
+ old = ENV["BUNDLER_SPEC_PLATFORM"]
+ ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
+ end
+
+ def simulate_ruby_version(version)
+ return if version == RUBY_VERSION
+ old = ENV["BUNDLER_SPEC_RUBY_VERSION"]
+ ENV["BUNDLER_SPEC_RUBY_VERSION"] = version
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_VERSION"] = old if block_given?
+ end
+
+ def simulate_ruby_engine(engine, version = "1.6.0")
+ return if engine == local_ruby_engine
+
+ old = ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] = engine
+ old_version = ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+ ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] = version
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] = old if block_given?
+ ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] = old_version if block_given?
+ end
+
+ def simulate_bundler_version(version)
+ old = ENV["BUNDLER_SPEC_VERSION"]
+ ENV["BUNDLER_SPEC_VERSION"] = version.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_VERSION"] = old if block_given?
+ end
+
+ def simulate_rubygems_version(version)
+ old = ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"]
+ ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"] = version.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"] = old if block_given?
+ end
+
+ def simulate_windows(platform = mswin)
+ old = ENV["BUNDLER_SPEC_WINDOWS"]
+ ENV["BUNDLER_SPEC_WINDOWS"] = "true"
+ simulate_platform platform do
+ yield
+ end
+ ensure
+ ENV["BUNDLER_SPEC_WINDOWS"] = old
+ end
+
+ def revision_for(path)
+ Dir.chdir(path) { `git rev-parse HEAD`.strip }
+ end
+
+ def capture_output
+ capture(:stdout)
+ end
+
+ def with_read_only(pattern)
+ chmod = lambda do |dirmode, filemode|
+ lambda do |f|
+ mode = File.directory?(f) ? dirmode : filemode
+ File.chmod(mode, f)
+ end
+ end
+
+ Dir[pattern].each(&chmod[0o555, 0o444])
+ yield
+ ensure
+ Dir[pattern].each(&chmod[0o755, 0o644])
+ end
+
+ def process_file(pathname)
+ changed_lines = pathname.readlines.map do |line|
+ yield line
+ end
+ File.open(pathname, "w") {|file| file.puts(changed_lines.join) }
+ end
+
+ def with_env_vars(env_hash, &block)
+ current_values = {}
+ env_hash.each do |k, v|
+ current_values[k] = ENV[k]
+ ENV[k] = v
+ end
+ block.call if block_given?
+ env_hash.each do |k, _|
+ ENV[k] = current_values[k]
+ end
+ end
+
+ def require_rack
+ # need to hack, so we can require rack
+ old_gem_home = ENV["GEM_HOME"]
+ ENV["GEM_HOME"] = Spec::Path.base_system_gems.to_s
+ require "rack"
+ ENV["GEM_HOME"] = old_gem_home
+ end
+
+ def wait_for_server(host, port, seconds = 15)
+ tries = 0
+ sleep 0.5
+ TCPSocket.new(host, port)
+ rescue => e
+ raise(e) if tries > (seconds * 2)
+ tries += 1
+ retry
+ end
+
+ def find_unused_port
+ port = 21_453
+ begin
+ port += 1 while TCPSocket.new("127.0.0.1", port)
+ rescue
+ false
+ end
+ port
+ end
+
+ def bundler_fileutils
+ if RUBY_VERSION >= "2.4"
+ ::Bundler::FileUtils
+ else
+ ::FileUtils
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
new file mode 100644
index 0000000000..69f8d9f679
--- /dev/null
+++ b/spec/bundler/support/indexes.rb
@@ -0,0 +1,421 @@
+# frozen_string_literal: true
+
+module Spec
+ module Indexes
+ def dep(name, reqs = nil)
+ @deps ||= []
+ @deps << Bundler::Dependency.new(name, reqs)
+ end
+
+ def platform(*args)
+ @platforms ||= []
+ @platforms.concat args.map {|p| Gem::Platform.new(p) }
+ end
+
+ alias_method :platforms, :platform
+
+ def resolve(args = [])
+ @platforms ||= ["ruby"]
+ deps = []
+ default_source = instance_double("Bundler::Source::Rubygems", :specs => @index)
+ source_requirements = { :default => default_source }
+ @deps.each do |d|
+ @platforms.each do |p|
+ source_requirements[d.name] = d.source = default_source
+ deps << Bundler::DepProxy.new(d, p)
+ end
+ end
+ source_requirements ||= {}
+ Bundler::Resolver.resolve(deps, @index, source_requirements, *args)
+ end
+
+ def should_resolve_as(specs)
+ got = resolve
+ got = got.map(&:full_name).sort
+ expect(got).to eq(specs.sort)
+ end
+
+ def should_resolve_and_include(specs, args = [])
+ got = resolve(args)
+ got = got.map(&:full_name).sort
+ specs.each do |s|
+ expect(got).to include(s)
+ end
+ end
+
+ def should_conflict_on(names)
+ got = resolve
+ flunk "The resolve succeeded with: #{got.map(&:full_name).sort.inspect}"
+ rescue Bundler::VersionConflict => e
+ expect(Array(names).sort).to eq(e.conflicts.sort)
+ end
+
+ def gem(*args, &blk)
+ build_spec(*args, &blk).first
+ end
+
+ def locked(*args)
+ Bundler::SpecSet.new(args.map do |name, version|
+ gem(name, version)
+ end)
+ end
+
+ def should_conservative_resolve_and_include(opts, unlock, specs)
+ # empty unlock means unlock all
+ opts = Array(opts)
+ search = Bundler::GemVersionPromoter.new(@locked, unlock).tap do |s|
+ s.level = opts.first
+ s.strict = opts.include?(:strict)
+ s.prerelease_specified = Hash[@deps.map {|d| [d.name, d.requirement.prerelease?] }]
+ end
+ should_resolve_and_include specs, [@base, search]
+ end
+
+ def an_awesome_index
+ build_index do
+ gem "rack", %w[0.8 0.9 0.9.1 0.9.2 1.0 1.1]
+ gem "rack-mount", %w[0.4 0.5 0.5.1 0.5.2 0.6]
+
+ # --- Pre-release support
+ gem "rubygems\0", ["1.3.2"]
+
+ # --- Rails
+ versions "1.2.3 2.2.3 2.3.5 3.0.0.beta 3.0.0.beta1" do |version|
+ gem "activesupport", version
+ gem "actionpack", version do
+ dep "activesupport", version
+ if version >= v("3.0.0.beta")
+ dep "rack", "~> 1.1"
+ dep "rack-mount", ">= 0.5"
+ elsif version > v("2.3") then dep "rack", "~> 1.0.0"
+ elsif version > v("2.0.0") then dep "rack", "~> 0.9.0"
+ end
+ end
+ gem "activerecord", version do
+ dep "activesupport", version
+ dep "arel", ">= 0.2" if version >= v("3.0.0.beta")
+ end
+ gem "actionmailer", version do
+ dep "activesupport", version
+ dep "actionmailer", version
+ end
+ if version < v("3.0.0.beta")
+ gem "railties", version do
+ dep "activerecord", version
+ dep "actionpack", version
+ dep "actionmailer", version
+ dep "activesupport", version
+ end
+ else
+ gem "railties", version
+ gem "rails", version do
+ dep "activerecord", version
+ dep "actionpack", version
+ dep "actionmailer", version
+ dep "activesupport", version
+ dep "railties", version
+ end
+ end
+ end
+
+ versions "1.0 1.2 1.2.1 1.2.2 1.3 1.3.0.1 1.3.5 1.4.0 1.4.2 1.4.2.1" do |version|
+ platforms "ruby java mswin32 mingw32 x64-mingw32" do |platform|
+ next if version == v("1.4.2.1") && platform != pl("x86-mswin32")
+ next if version == v("1.4.2") && platform == pl("x86-mswin32")
+ gem "nokogiri", version, platform do
+ dep "weakling", ">= 0.0.3" if platform =~ pl("java")
+ end
+ end
+ end
+
+ versions "0.0.1 0.0.2 0.0.3" do |version|
+ gem "weakling", version
+ end
+
+ # --- Rails related
+ versions "1.2.3 2.2.3 2.3.5" do |version|
+ gem "activemerchant", version do
+ dep "activesupport", ">= #{version}"
+ end
+ end
+
+ gem "reform", ["1.0.0"] do
+ dep "activesupport", ">= 1.0.0.beta1"
+ end
+
+ gem "need-pre", ["1.0.0"] do
+ dep "activesupport", "~> 3.0.0.beta1"
+ end
+ end
+ end
+
+ # Builder 3.1.4 will activate first, but if all
+ # goes well, it should resolve to 3.0.4
+ def a_conflict_index
+ build_index do
+ gem "builder", %w[3.0.4 3.1.4]
+ gem("grape", "0.2.6") do
+ dep "builder", ">= 0"
+ end
+
+ versions "3.2.8 3.2.9 3.2.10 3.2.11" do |version|
+ gem("activemodel", version) do
+ dep "builder", "~> 3.0.0"
+ end
+ end
+
+ gem("my_app", "1.0.0") do
+ dep "activemodel", ">= 0"
+ dep "grape", ">= 0"
+ end
+ end
+ end
+
+ def a_complex_conflict_index
+ build_index do
+ gem("a", %w[1.0.2 1.1.4 1.2.0 1.4.0]) do
+ dep "d", ">= 0"
+ end
+
+ gem("d", %w[1.3.0 1.4.1]) do
+ dep "x", ">= 0"
+ end
+
+ gem "d", "0.9.8"
+
+ gem("b", "0.3.4") do
+ dep "a", ">= 1.5.0"
+ end
+
+ gem("b", "0.3.5") do
+ dep "a", ">= 1.2"
+ end
+
+ gem("b", "0.3.3") do
+ dep "a", "> 1.0"
+ end
+
+ versions "3.2 3.3" do |version|
+ gem("c", version) do
+ dep "a", "~> 1.0"
+ end
+ end
+
+ gem("my_app", "1.3.0") do
+ dep "c", ">= 4.0"
+ dep "b", ">= 0"
+ end
+
+ gem("my_app", "1.2.0") do
+ dep "c", "~> 3.3.0"
+ dep "b", "0.3.4"
+ end
+
+ gem("my_app", "1.1.0") do
+ dep "c", "~> 3.2.0"
+ dep "b", "0.3.5"
+ end
+ end
+ end
+
+ def index_with_conflict_on_child
+ build_index do
+ gem "json", %w[1.6.5 1.7.7 1.8.0]
+
+ gem("chef", "10.26") do
+ dep "json", [">= 1.4.4", "<= 1.7.7"]
+ end
+
+ gem("berkshelf", "2.0.7") do
+ dep "json", ">= 1.7.7"
+ end
+
+ gem("chef_app", "1.0.0") do
+ dep "berkshelf", "~> 2.0"
+ dep "chef", "~> 10.26"
+ end
+ end
+ end
+
+ # Issue #3459
+ def a_complicated_index
+ build_index do
+ gem "foo", %w[3.0.0 3.0.5] do
+ dep "qux", ["~> 3.1"]
+ dep "baz", ["< 9.0", ">= 5.0"]
+ dep "bar", ["~> 1.0"]
+ dep "grault", ["~> 3.1"]
+ end
+
+ gem "foo", "1.2.1" do
+ dep "baz", ["~> 4.2"]
+ dep "bar", ["~> 1.0"]
+ dep "qux", ["~> 3.1"]
+ dep "grault", ["~> 2.0"]
+ end
+
+ gem "bar", "1.0.5" do
+ dep "grault", ["~> 3.1"]
+ dep "baz", ["< 9", ">= 4.2"]
+ end
+
+ gem "bar", "1.0.3" do
+ dep "baz", ["< 9", ">= 4.2"]
+ dep "grault", ["~> 2.0"]
+ end
+
+ gem "baz", "8.2.10" do
+ dep "grault", ["~> 3.0"]
+ dep "garply", [">= 0.5.1", "~> 0.5"]
+ end
+
+ gem "baz", "5.0.2" do
+ dep "grault", ["~> 2.0"]
+ dep "garply", [">= 0.3.1"]
+ end
+
+ gem "baz", "4.2.0" do
+ dep "grault", ["~> 2.0"]
+ dep "garply", [">= 0.3.1"]
+ end
+
+ gem "grault", %w[2.6.3 3.1.1]
+
+ gem "garply", "0.5.1" do
+ dep "waldo", ["~> 0.1.3"]
+ end
+
+ gem "waldo", "0.1.5" do
+ dep "plugh", ["~> 0.6.0"]
+ end
+
+ gem "plugh", %w[0.6.3 0.6.11 0.7.0]
+
+ gem "qux", "3.2.21" do
+ dep "plugh", [">= 0.6.4", "~> 0.6"]
+ dep "corge", ["~> 1.0"]
+ end
+
+ gem "corge", "1.10.1"
+ end
+ end
+
+ def a_unresovable_child_index
+ build_index do
+ gem "json", %w[1.8.0]
+
+ gem("chef", "10.26") do
+ dep "json", [">= 1.4.4", "<= 1.7.7"]
+ end
+
+ gem("berkshelf", "2.0.7") do
+ dep "json", ">= 1.7.7"
+ end
+
+ gem("chef_app_error", "1.0.0") do
+ dep "berkshelf", "~> 2.0"
+ dep "chef", "~> 10.26"
+ end
+ end
+ end
+
+ def a_index_with_root_conflict_on_child
+ build_index do
+ gem "builder", %w[2.1.2 3.0.1 3.1.3]
+ gem "i18n", %w[0.4.1 0.4.2]
+
+ gem "activesupport", %w[3.0.0 3.0.1 3.0.5 3.1.7]
+
+ gem("activemodel", "3.0.5") do
+ dep "activesupport", "= 3.0.5"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.4"
+ end
+
+ gem("activemodel", "3.0.0") do
+ dep "activesupport", "= 3.0.0"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.4.1"
+ end
+
+ gem("activemodel", "3.1.3") do
+ dep "activesupport", "= 3.1.3"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.5"
+ end
+
+ gem("activerecord", "3.0.0") do
+ dep "activesupport", "= 3.0.0"
+ dep "activemodel", "= 3.0.0"
+ end
+
+ gem("activerecord", "3.0.5") do
+ dep "activesupport", "= 3.0.5"
+ dep "activemodel", "= 3.0.5"
+ end
+
+ gem("activerecord", "3.0.9") do
+ dep "activesupport", "= 3.1.5"
+ dep "activemodel", "= 3.1.5"
+ end
+ end
+ end
+
+ def a_circular_index
+ build_index do
+ gem "rack", "1.0.1"
+ gem("foo", "0.2.6") do
+ dep "bar", ">= 0"
+ end
+
+ gem("bar", "1.0.0") do
+ dep "foo", ">= 0"
+ end
+
+ gem("circular_app", "1.0.0") do
+ dep "foo", ">= 0"
+ dep "bar", ">= 0"
+ end
+ end
+ end
+
+ def an_ambiguous_index
+ build_index do
+ gem("a", "1.0.0") do
+ dep "c", ">= 0"
+ end
+
+ gem("b", %w[0.5.0 1.0.0])
+
+ gem("b", "2.0.0") do
+ dep "c", "< 2.0.0"
+ end
+
+ gem("c", "1.0.0") do
+ dep "d", "1.0.0"
+ end
+
+ gem("c", "2.0.0") do
+ dep "d", "2.0.0"
+ end
+
+ gem("d", %w[1.0.0 2.0.0])
+ end
+ end
+
+ def optional_prereleases_index
+ build_index do
+ gem("a", %w[1.0.0])
+
+ gem("a", "2.0.0") do
+ dep "b", ">= 2.0.0.pre"
+ end
+
+ gem("b", %w[0.9.0 1.5.0 2.0.0.pre])
+
+ # --- Pre-release support
+ gem "rubygems\0", ["1.3.2"]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/less_than_proc.rb b/spec/bundler/support/less_than_proc.rb
new file mode 100644
index 0000000000..ddac5458b7
--- /dev/null
+++ b/spec/bundler/support/less_than_proc.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class LessThanProc < Proc
+ attr_accessor :present
+
+ def self.with(present)
+ provided = Gem::Version.new(present.dup)
+ new do |required|
+ if required =~ /[=><~]/
+ !Gem::Requirement.new(required).satisfied_by?(provided)
+ else
+ provided < Gem::Version.new(required)
+ end
+ end.tap {|l| l.present = present }
+ end
+
+ def inspect
+ "\"=< #{present}\""
+ end
+end
diff --git a/spec/bundler/support/manpages.rb b/spec/bundler/support/manpages.rb
new file mode 100644
index 0000000000..ce1f72cc49
--- /dev/null
+++ b/spec/bundler/support/manpages.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Spec
+ module Manpages
+ def self.setup
+ man_path = Spec::Path.root.join("man")
+ return if man_path.children(false).select {|file| file.extname == ".ronn" }.all? do |man|
+ Dir[man_path.join("#{man.to_s[0..-6]}*.txt").to_s].any?
+ end
+
+ system(Spec::Path.root.join("bin", "rake").to_s, "man:build") || raise("Failed building man pages")
+ end
+ end
+end
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
new file mode 100644
index 0000000000..8e17be3a02
--- /dev/null
+++ b/spec/bundler/support/matchers.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+
+require "forwardable"
+require "support/the_bundle"
+module Spec
+ module Matchers
+ extend RSpec::Matchers
+
+ class Precondition
+ include RSpec::Matchers::Composable
+ extend Forwardable
+ def_delegators :failing_matcher,
+ :failure_message,
+ :actual,
+ :description,
+ :diffable?,
+ :expected,
+ :failure_message_when_negated
+
+ def initialize(matcher, preconditions)
+ @matcher = with_matchers_cloned(matcher)
+ @preconditions = with_matchers_cloned(preconditions)
+ @failure_index = nil
+ end
+
+ def matches?(target, &blk)
+ return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
+ @matcher.matches?(target, &blk)
+ end
+
+ def does_not_match?(target, &blk)
+ return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
+ if @matcher.respond_to?(:does_not_match?)
+ @matcher.does_not_match?(target, &blk)
+ else
+ !@matcher.matches?(target, &blk)
+ end
+ end
+
+ def expects_call_stack_jump?
+ @matcher.expects_call_stack_jump? || @preconditions.any?(&:expects_call_stack_jump)
+ end
+
+ def supports_block_expectations?
+ @matcher.supports_block_expectations? || @preconditions.any?(&:supports_block_expectations)
+ end
+
+ def failing_matcher
+ @failure_index ? @preconditions[@failure_index] : @matcher
+ end
+ end
+
+ def self.define_compound_matcher(matcher, preconditions, &declarations)
+ raise "Must have preconditions to define a compound matcher" if preconditions.empty?
+ define_method(matcher) do |*expected, &block_arg|
+ Precondition.new(
+ RSpec::Matchers::DSL::Matcher.new(matcher, declarations, self, *expected, &block_arg),
+ preconditions
+ )
+ end
+ end
+
+ MAJOR_DEPRECATION = /^\[DEPRECATED FOR 2\.0\]\s*/
+
+ RSpec::Matchers.define :lack_errors do
+ diffable
+ match do |actual|
+ actual.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "") == ""
+ end
+ end
+
+ RSpec::Matchers.define :eq_err do |expected|
+ diffable
+ match do |actual|
+ actual.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "") == expected
+ end
+ end
+
+ RSpec::Matchers.define :have_major_deprecation do |expected|
+ diffable
+ match do |actual|
+ deprecations = actual.split(MAJOR_DEPRECATION)
+
+ return !expected.nil? if deprecations.size <= 1
+ return true if expected.nil?
+
+ deprecations.any? do |d|
+ !d.empty? && values_match?(expected, d.strip)
+ end
+ end
+ end
+
+ RSpec::Matchers.define :have_dep do |*args|
+ dep = Bundler::Dependency.new(*args)
+
+ match do |actual|
+ actual.length == 1 && actual.all? {|d| d == dep }
+ end
+ end
+
+ RSpec::Matchers.define :have_gem do |*args|
+ match do |actual|
+ actual.length == args.length && actual.all? {|a| args.include?(a.full_name) }
+ end
+ end
+
+ RSpec::Matchers.define :have_rubyopts do |*args|
+ args = args.flatten
+ args = args.first.split(/\s+/) if args.size == 1
+
+ match do |actual|
+ actual = actual.split(/\s+/) if actual.is_a?(String)
+ args.all? {|arg| actual.include?(arg) } && actual.uniq.size == actual.size
+ end
+ end
+
+ RSpec::Matchers.define :be_sorted do
+ diffable
+ attr_reader :expected
+ match do |actual|
+ expected = block_arg ? actual.sort_by(&block_arg) : actual.sort
+ actual.==(expected).tap do
+ # HACK: since rspec won't show a diff when everything is a string
+ differ = RSpec::Support::Differ.new
+ @actual = differ.send(:object_to_string, actual)
+ @expected = differ.send(:object_to_string, expected)
+ end
+ end
+ end
+
+ define_compound_matcher :read_as, [exist] do |file_contents|
+ diffable
+
+ match do |actual|
+ @actual = Bundler.read_file(actual)
+ values_match?(file_contents, @actual)
+ end
+ end
+
+ def indent(string, padding = 4, indent_character = " ")
+ string.to_s.gsub(/^/, indent_character * padding).gsub("\t", " ")
+ end
+
+ define_compound_matcher :include_gems, [be_an_instance_of(Spec::TheBundle)] do |*names|
+ match do
+ opts = names.last.is_a?(Hash) ? names.pop : {}
+ source = opts.delete(:source)
+ groups = Array(opts[:groups])
+ groups << opts
+ @errors = names.map do |name|
+ name, version, platform = name.split(/\s+/)
+ version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
+ begin
+ run! "require '#{name}.rb'; puts #{version_const}", *groups
+ rescue => e
+ next "#{name} is not installed:\n#{indent(e)}"
+ end
+ last_command.stdout.gsub!(/#{MAJOR_DEPRECATION}.*$/, "")
+ actual_version, actual_platform = last_command.stdout.strip.split(/\s+/, 2)
+ unless Gem::Version.new(actual_version) == Gem::Version.new(version)
+ next "#{name} was expected to be at version #{version} but was #{actual_version}"
+ end
+ unless actual_platform == platform
+ next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
+ end
+ next unless source
+ begin
+ source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
+ run! "require '#{name}/source'; puts #{source_const}", *groups
+ rescue
+ next "#{name} does not have a source defined:\n#{indent(e)}"
+ end
+ last_command.stdout.gsub!(/#{MAJOR_DEPRECATION}.*$/, "")
+ unless last_command.stdout.strip == source
+ next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{out}`"
+ end
+ end.compact
+
+ @errors.empty?
+ end
+
+ match_when_negated do
+ opts = names.last.is_a?(Hash) ? names.pop : {}
+ groups = Array(opts[:groups]) || []
+ @errors = names.map do |name|
+ name, version = name.split(/\s+/, 2)
+ begin
+ run <<-R, *(groups + [opts])
+ begin
+ require '#{name}'
+ puts #{Spec::Builders.constantize(name)}
+ rescue LoadError, NameError
+ puts "WIN"
+ end
+ R
+ rescue => e
+ next "checking for #{name} failed:\n#{e}"
+ end
+ next if last_command.stdout == "WIN"
+ next "expected #{name} to not be installed, but it was" if version.nil?
+ if Gem::Version.new(last_command.stdout) == Gem::Version.new(version)
+ next "expected #{name} (#{version}) not to be installed, but it was"
+ end
+ end.compact
+
+ @errors.empty?
+ end
+
+ failure_message do
+ super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
+ end
+
+ failure_message_when_negated do
+ super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
+ end
+ end
+ RSpec::Matchers.define_negated_matcher :not_include_gems, :include_gems
+ RSpec::Matchers.alias_matcher :include_gem, :include_gems
+
+ def have_lockfile(expected)
+ read_as(strip_whitespace(expected))
+ end
+
+ def plugin_should_be_installed(*names)
+ names.each do |name|
+ expect(Bundler::Plugin).to be_installed(name)
+ path = Pathname.new(Bundler::Plugin.installed?(name))
+ expect(path + "plugins.rb").to exist
+ end
+ end
+
+ def plugin_should_not_be_installed(*names)
+ names.each do |name|
+ expect(Bundler::Plugin).not_to be_installed(name)
+ end
+ end
+
+ def lockfile_should_be(expected)
+ expect(bundled_app("Gemfile.lock")).to read_as(normalize_uri_file(strip_whitespace(expected)))
+ end
+
+ def gemfile_should_be(expected)
+ expect(bundled_app("Gemfile")).to read_as(strip_whitespace(expected))
+ end
+ end
+end
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
new file mode 100644
index 0000000000..2eea088eea
--- /dev/null
+++ b/spec/bundler/support/path.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+module Spec
+ module Path
+ def root
+ @root ||= Pathname.new(ruby_core? ? "../../../.." : "../../..").expand_path(__FILE__)
+ end
+
+ def gemspec
+ @gemspec ||= root.join(ruby_core? ? "lib/bundler.gemspec" : "bundler.gemspec")
+ end
+
+ def bindir
+ @bindir ||= root.join(ruby_core? ? "bin" : "exe")
+ end
+
+ def spec_dir
+ @spec_dir ||= root.join(ruby_core? ? "spec/bundler" : "spec")
+ end
+
+ def tmp(*path)
+ root.join("tmp", *path)
+ end
+
+ def home(*path)
+ tmp.join("home", *path)
+ end
+
+ def default_bundle_path(*path)
+ if Bundler::VERSION.split(".").first.to_i < 2
+ system_gem_path(*path)
+ else
+ bundled_app(*[".bundle", ENV.fetch("BUNDLER_SPEC_RUBY_ENGINE", Gem.ruby_engine), Gem::ConfigMap[:ruby_version], *path].compact)
+ end
+ end
+
+ def bundled_app(*path)
+ root = tmp.join("bundled_app")
+ FileUtils.mkdir_p(root)
+ root.join(*path)
+ end
+
+ alias_method :bundled_app1, :bundled_app
+
+ def bundled_app2(*path)
+ root = tmp.join("bundled_app2")
+ FileUtils.mkdir_p(root)
+ root.join(*path)
+ end
+
+ def vendored_gems(path = nil)
+ bundled_app(*["vendor/bundle", Gem.ruby_engine, Gem::ConfigMap[:ruby_version], path].compact)
+ end
+
+ def cached_gem(path)
+ bundled_app("vendor/cache/#{path}.gem")
+ end
+
+ def base_system_gems
+ tmp.join("gems/base")
+ end
+
+ def gem_repo1(*args)
+ tmp("gems/remote1", *args)
+ end
+
+ def gem_repo_missing(*args)
+ tmp("gems/missing", *args)
+ end
+
+ def gem_repo2(*args)
+ tmp("gems/remote2", *args)
+ end
+
+ def gem_repo3(*args)
+ tmp("gems/remote3", *args)
+ end
+
+ def gem_repo4(*args)
+ tmp("gems/remote4", *args)
+ end
+
+ def security_repo(*args)
+ tmp("gems/security_repo", *args)
+ end
+
+ def system_gem_path(*path)
+ tmp("gems/system", *path)
+ end
+
+ def lib_path(*args)
+ tmp("libs", *args)
+ end
+
+ def bundler_path
+ Pathname.new(File.expand_path(root.join("lib"), __FILE__))
+ end
+
+ def global_plugin_gem(*args)
+ home ".bundle", "plugin", "gems", *args
+ end
+
+ def local_plugin_gem(*args)
+ bundled_app ".bundle", "plugin", "gems", *args
+ end
+
+ def tmpdir(*args)
+ tmp "tmpdir", *args
+ end
+
+ def ruby_core?
+ # avoid to wornings
+ @ruby_core ||= nil
+
+ if @ruby_core.nil?
+ @ruby_core = true & (ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"])
+ else
+ @ruby_core
+ end
+ end
+
+ extend self
+ end
+end
diff --git a/spec/bundler/support/permissions.rb b/spec/bundler/support/permissions.rb
new file mode 100644
index 0000000000..b21ce3848d
--- /dev/null
+++ b/spec/bundler/support/permissions.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Spec
+ module Permissions
+ def with_umask(new_umask)
+ old_umask = File.umask(new_umask)
+ yield if block_given?
+ ensure
+ File.umask(old_umask)
+ end
+ end
+end
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
new file mode 100644
index 0000000000..39040a61bd
--- /dev/null
+++ b/spec/bundler/support/platforms.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+module Spec
+ module Platforms
+ include Bundler::GemHelpers
+
+ def rb
+ Gem::Platform::RUBY
+ end
+
+ def mac
+ Gem::Platform.new("x86-darwin-10")
+ end
+
+ def x64_mac
+ Gem::Platform.new("x86_64-darwin-15")
+ end
+
+ def java
+ Gem::Platform.new([nil, "java", nil])
+ end
+
+ def linux
+ Gem::Platform.new(["x86", "linux", nil])
+ end
+
+ def mswin
+ Gem::Platform.new(["x86", "mswin32", nil])
+ end
+
+ def mingw
+ Gem::Platform.new(["x86", "mingw32", nil])
+ end
+
+ def x64_mingw
+ Gem::Platform.new(["x64", "mingw32", nil])
+ end
+
+ def all_platforms
+ [rb, java, linux, mswin, mingw, x64_mingw]
+ end
+
+ def local
+ generic_local_platform
+ end
+
+ def specific_local_platform
+ Bundler.local_platform
+ end
+
+ def not_local
+ all_platforms.find {|p| p != generic_local_platform }
+ end
+
+ def local_tag
+ if RUBY_PLATFORM == "java"
+ :jruby
+ else
+ :ruby
+ end
+ end
+
+ def not_local_tag
+ [:ruby, :jruby].find {|tag| tag != local_tag }
+ end
+
+ def local_ruby_engine
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] || (defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby")
+ end
+
+ def local_engine_version
+ return ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] if ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+
+ case local_ruby_engine
+ when "ruby"
+ RUBY_VERSION
+ when "rbx"
+ Rubinius::VERSION
+ when "jruby"
+ JRUBY_VERSION
+ else
+ RUBY_ENGINE_VERSION
+ end
+ end
+
+ def not_local_engine_version
+ case not_local_tag
+ when :ruby
+ not_local_ruby_version
+ when :jruby
+ "1.6.1"
+ end
+ end
+
+ def not_local_ruby_version
+ "1.12"
+ end
+
+ def not_local_patchlevel
+ 9999
+ end
+
+ def lockfile_platforms(*platforms)
+ platforms = local_platforms if platforms.empty?
+ platforms.map(&:to_s).sort.join("\n ")
+ end
+
+ def local_platforms
+ if Bundler::VERSION.split(".").first.to_i > 1
+ [local, specific_local_platform]
+ else
+ [local]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
new file mode 100644
index 0000000000..806933fe2f
--- /dev/null
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require "rubygems/user_interaction"
+require "support/path" unless defined?(Spec::Path)
+
+module Spec
+ module Rubygems
+ DEPS = begin
+ deps = {
+ # rack 2.x requires Ruby version >= 2.2.2.
+ # artifice doesn't support rack 2.x now.
+ # TODO: revert to `< 2` once https://github.com/rack/rack/issues/1168 is
+ # addressed
+ "rack" => "1.6.6",
+ # rack-test 0.7.0 dropped 1.8.7 support
+ # https://github.com/rack-test/rack-test/issues/193#issuecomment-314230318
+ "rack-test" => "< 0.7.0",
+ "artifice" => "~> 0.6.0",
+ "compact_index" => "~> 0.11.0",
+ "sinatra" => "~> 1.4.7",
+ # Rake version has to be consistent for tests to pass
+ "rake" => "10.0.2",
+ # 3.0.0 breaks 1.9.2 specs
+ "builder" => "2.1.2",
+ }
+ # ruby-graphviz is used by the viz tests
+ deps["ruby-graphviz"] = nil if RUBY_VERSION >= "1.9.3"
+ deps
+ end
+
+ def self.setup
+ Gem.clear_paths
+
+ ENV["BUNDLE_PATH"] = nil
+ ENV["GEM_HOME"] = ENV["GEM_PATH"] = Path.base_system_gems.to_s
+ ENV["PATH"] = ["#{Path.root}/exe", "#{Path.system_gem_path}/bin", ENV["PATH"]].join(File::PATH_SEPARATOR)
+
+ manifest = DEPS.to_a.sort_by(&:first).map {|k, v| "#{k} => #{v}\n" }
+ manifest_path = "#{Path.base_system_gems}/manifest.txt"
+ # it's OK if there are extra gems
+ if !File.exist?(manifest_path) || !(manifest - File.readlines(manifest_path)).empty?
+ FileUtils.rm_rf(Path.base_system_gems)
+ FileUtils.mkdir_p(Path.base_system_gems)
+ puts "installing gems for the tests to use..."
+ install_gems(DEPS)
+ File.open(manifest_path, "w") {|f| f << manifest.join }
+ end
+
+ ENV["HOME"] = Path.home.to_s
+ ENV["TMPDIR"] = Path.tmpdir.to_s
+
+ Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
+ end
+
+ def self.install_gems(gems)
+ reqs, no_reqs = gems.partition {|_, req| !req.nil? && !req.split(" ").empty? }
+ # TODO: remove when we drop ruby 1.8.7-2.2.2 support
+ reqs = reqs.sort_by {|name, _| name == "rack" ? 0 : 1 }.sort_by {|name, _| name =~ /rack/ ? 0 : 1 }
+ no_reqs.map!(&:first)
+ reqs.map! {|name, req| "'#{name}:#{req}'" }
+ deps = reqs.concat(no_reqs).join(" ")
+ cmd = if Gem::VERSION < "2.0.0"
+ "gem install #{deps} --no-rdoc --no-ri --conservative"
+ else
+ "gem install #{deps} --no-document --conservative"
+ end
+ puts cmd
+ system(cmd) || raise("Installing gems #{deps} for the tests to use failed!")
+ end
+ end
+end
diff --git a/spec/bundler/support/silent_logger.rb b/spec/bundler/support/silent_logger.rb
new file mode 100644
index 0000000000..8665beb2c9
--- /dev/null
+++ b/spec/bundler/support/silent_logger.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "logger"
+module Spec
+ class SilentLogger
+ (::Logger.instance_methods - Object.instance_methods).each do |logger_instance_method|
+ define_method(logger_instance_method) {|*args, &blk| }
+ end
+ end
+end
diff --git a/spec/bundler/support/sometimes.rb b/spec/bundler/support/sometimes.rb
new file mode 100644
index 0000000000..65a95ed59c
--- /dev/null
+++ b/spec/bundler/support/sometimes.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Sometimes
+ def run_with_retries(example_to_run, retries)
+ example = RSpec.current_example
+ example.metadata[:retries] ||= retries
+
+ retries.times do |t|
+ example.metadata[:retried] = t + 1
+ example.instance_variable_set(:@exception, nil)
+ example_to_run.run
+ break unless example.exception
+ end
+
+ if e = example.exception
+ new_exception = e.exception(e.message + "[Retried #{retries} times]")
+ new_exception.set_backtrace e.backtrace
+ example.instance_variable_set(:@exception, new_exception)
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.include Sometimes
+ config.alias_example_to :sometimes, :sometimes => true
+ config.add_setting :sometimes_retry_count, :default => 5
+
+ config.around(:each, :sometimes => true) do |example|
+ retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count
+ run_with_retries(example, retries)
+ end
+
+ config.after(:suite) do
+ message = proc do |color, text|
+ colored = RSpec::Core::Formatters::ConsoleCodes.wrap(text, color)
+ notification = RSpec::Core::Notifications::MessageNotification.new(colored)
+ formatter = RSpec.configuration.formatters.first
+ formatter.message(notification) if formatter.respond_to?(:message)
+ end
+
+ retried_examples = RSpec.world.example_groups.map do |g|
+ g.descendants.map do |d|
+ d.filtered_examples.select do |e|
+ e.metadata[:sometimes] && e.metadata.fetch(:retried, 1) > 1
+ end
+ end
+ end.flatten
+
+ message.call(retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}")
+
+ retried_examples.each do |e|
+ message.call(:cyan, " #{e.full_description}")
+ path = RSpec::Core::Metadata.relative_path(e.location)
+ message.call(:cyan, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] " + path)
+ end
+ end
+end
diff --git a/spec/bundler/support/streams.rb b/spec/bundler/support/streams.rb
new file mode 100644
index 0000000000..a947eebf6f
--- /dev/null
+++ b/spec/bundler/support/streams.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require "stringio"
+
+def capture(*args)
+ opts = args.pop if args.last.is_a?(Hash)
+ opts ||= {}
+
+ args.map!(&:to_s)
+ begin
+ result = StringIO.new
+ result.close if opts[:closed]
+ args.each {|stream| eval "$#{stream} = result" }
+ yield
+ ensure
+ args.each {|stream| eval("$#{stream} = #{stream.upcase}") }
+ end
+ result.string
+end
diff --git a/spec/bundler/support/sudo.rb b/spec/bundler/support/sudo.rb
new file mode 100644
index 0000000000..04e9443945
--- /dev/null
+++ b/spec/bundler/support/sudo.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Spec
+ module Sudo
+ def self.present?
+ @which_sudo ||= Bundler.which("sudo")
+ end
+
+ def sudo(cmd)
+ raise "sudo not present" unless Sudo.present?
+ sys_exec("sudo #{cmd}")
+ end
+
+ def chown_system_gems_to_root
+ sudo "chown -R root #{system_gem_path}"
+ end
+ end
+end
diff --git a/spec/bundler/support/the_bundle.rb b/spec/bundler/support/the_bundle.rb
new file mode 100644
index 0000000000..c994eaae78
--- /dev/null
+++ b/spec/bundler/support/the_bundle.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "support/helpers"
+require "support/path"
+
+module Spec
+ class TheBundle
+ include Spec::Helpers
+ include Spec::Path
+
+ attr_accessor :bundle_dir
+
+ def initialize(opts = {})
+ opts = opts.dup
+ @bundle_dir = Pathname.new(opts.delete(:bundle_dir) { bundled_app })
+ raise "Too many options! #{opts}" unless opts.empty?
+ end
+
+ def to_s
+ "the bundle"
+ end
+ alias_method :inspect, :to_s
+
+ def locked?
+ lockfile.file?
+ end
+
+ def lockfile
+ bundle_dir.join("Gemfile.lock")
+ end
+
+ def locked_gems
+ raise "Cannot read lockfile if it doesn't exist" unless locked?
+ Bundler::LockfileParser.new(lockfile.read)
+ end
+ end
+end
diff --git a/spec/bundler/update/gemfile_spec.rb b/spec/bundler/update/gemfile_spec.rb
new file mode 100644
index 0000000000..f59f3a2d32
--- /dev/null
+++ b/spec/bundler/update/gemfile_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle! :install, :gemfile => bundled_app("NotGemfile")
+ bundle! :update, :gemfile => bundled_app("NotGemfile"), :all => bundle_update_requires_all?
+
+ # Specify BUNDLE_GEMFILE for `the_bundle`
+ # to retrieve the proper Gemfile
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with gemfile set via config" do
+ before do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ bundle! :install
+ end
+
+ it "uses the gemfile to update" do
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+
+ it "uses the gemfile while in a subdirectory" do
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+ end
+
+ context "with prefer_gems_rb set" do
+ before { bundle! "config prefer_gems_rb true" }
+
+ it "prefers gems.rb to Gemfile" do
+ create_file("gems.rb", "gem 'bundler'")
+ create_file("Gemfile", "raise 'wrong Gemfile!'")
+
+ bundle! :install
+ bundle! :update, :all => bundle_update_requires_all?
+
+ expect(bundled_app("gems.rb")).to be_file
+ expect(bundled_app("Gemfile.lock")).not_to be_file
+
+ expect(the_bundle).to include_gem "bundler #{Bundler::VERSION}"
+ end
+ end
+end
diff --git a/spec/bundler/update/gems/post_install_spec.rb b/spec/bundler/update/gems/post_install_spec.rb
new file mode 100644
index 0000000000..2fb3547806
--- /dev/null
+++ b/spec/bundler/update/gems/post_install_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ let(:config) {}
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "< 1.0"
+ gem 'thin'
+ G
+
+ bundle! "config #{config}" if config
+
+ bundle! :install
+ end
+
+ shared_examples "a config observer" do
+ context "when ignore post-install messages for gem is set" do
+ let(:config) { "ignore_messages.rack true" }
+
+ it "doesn't display gem's post-install message" do
+ expect(out).not_to include("Rack's post install message")
+ end
+ end
+
+ context "when ignore post-install messages for all gems" do
+ let(:config) { "ignore_messages true" }
+
+ it "doesn't display any post-install messages" do
+ expect(out).not_to include("Post-install message")
+ end
+ end
+ end
+
+ shared_examples "a post-install message outputter" do
+ it "should display post-install messages for updated gems" do
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ it "should not display the post-install message for non-updated gems" do
+ expect(out).not_to include("Thin's post install message")
+ end
+ end
+
+ context "when listed gem is updated" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ G
+
+ bundle! :update, :all => bundle_update_requires_all?
+ end
+
+ it_behaves_like "a post-install message outputter"
+ it_behaves_like "a config observer"
+ end
+
+ context "when dependency triggers update" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama'
+ gem 'thin'
+ G
+
+ bundle! :update, :all => bundle_update_requires_all?
+ end
+
+ it_behaves_like "a post-install message outputter"
+ it_behaves_like "a config observer"
+ end
+end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
new file mode 100644
index 0000000000..b4cbb79434
--- /dev/null
+++ b/spec/bundler/update/git_spec.rb
@@ -0,0 +1,374 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ describe "git sources" do
+ it "floats on a branch when :branch is used" do
+ build_git "foo", "1.0"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :branch => "omg" do
+ gem 'foo'
+ end
+ G
+
+ update_git "foo", :branch => "omg" do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "updates correctly when you have like craziness" do
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 3.0"
+ end
+
+ install_gemfile! <<-G
+ gem "rails", :git => "#{lib_path("rails")}"
+ G
+
+ bundle! "update rails"
+ expect(the_bundle).to include_gems "rails 3.0", "activesupport 3.0"
+ end
+
+ it "floats on a branch when :branch is used and the source is specified in the update" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+ update_git "foo", :branch => "omg", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ git "#{lib_path("foo")}", :branch => "omg" do
+ gem 'foo'
+ end
+ G
+
+ update_git "foo", :branch => "omg", :path => lib_path("foo") do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update --source foo"
+
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "floats on master when updating all gems that are pinned to the source even if you have child dependencies" do
+ build_git "foo", :path => lib_path("foo")
+ build_gem "bar", :to_bundle => true do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "bar"
+ G
+
+ update_git "foo", :path => lib_path("foo") do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update foo"
+
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "notices when you change the repo url in the Gemfile" do
+ build_git "foo", :path => lib_path("foo_one")
+ build_git "foo", :path => lib_path("foo_two")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo_one")}"
+ G
+
+ FileUtils.rm_rf lib_path("foo_one")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo_two")}"
+ G
+
+ expect(err).to lack_errors
+ expect(out).to include("Fetching #{lib_path}/foo_two")
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "fetches tags from the remote" do
+ build_git "foo"
+ @remote = build_git("bar", :bare => true)
+ update_git "foo", :remote => @remote.path
+ update_git "foo", :push => "master"
+
+ install_gemfile <<-G
+ gem 'foo', :git => "#{@remote.path}"
+ G
+
+ # Create a new tag on the remote that needs fetching
+ update_git "foo", :tag => "fubar"
+ update_git "foo", :push => "fubar"
+
+ gemfile <<-G
+ gem 'foo', :git => "#{@remote.path}", :tag => "fubar"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ describe "with submodules" do
+ before :each do
+ build_repo4 do
+ build_gem "submodule" do |s|
+ s.write "lib/submodule.rb", "puts 'GEM'"
+ end
+ end
+
+ build_git "submodule", "1.0" do |s|
+ s.write "lib/submodule.rb", "puts 'GIT'"
+ end
+
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+ end
+
+ it "it unlocks the source when submodules are added to a git source" do
+ install_gemfile <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+
+ run "require 'submodule'"
+ expect(out).to eq("GEM")
+
+ install_gemfile <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ run "require 'submodule'"
+ expect(out).to eq("GIT")
+ end
+
+ it "unlocks the source when submodules are removed from git source", :git => ">= 2.9.0" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ run! "require 'submodule'"
+ expect(out).to eq("GIT")
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+
+ run! "require 'submodule'"
+ expect(out).to eq("GEM")
+ end
+ end
+
+ it "errors with a message when the .git repo is gone" do
+ build_git "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lib_path("foo-1.0").join(".git").rmtree
+
+ bundle :update, :all => bundle_update_requires_all?
+ expect(last_command.bundler_err).to include(lib_path("foo-1.0").to_s).
+ and match(/Git error: command `git fetch.+has failed/)
+ end
+
+ it "should not explode on invalid revision on update of gem by name" do
+ build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle "update rack"
+ expect(out).to include("Bundle updated!")
+ end
+
+ it "shows the previous version of the gem" do
+ build_git "rails", "3.0", :path => lib_path("rails")
+
+ install_gemfile <<-G
+ gem "rails", :git => "#{lib_path("rails")}"
+ G
+
+ lockfile <<-G
+ GIT
+ remote: #{lib_path("rails")}
+ specs:
+ rails (2.3.2)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rails!
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Using rails 3.0 (was 2.3.2) from #{lib_path("rails")} (at master@#{revision_for(lib_path("rails"))[0..6]})")
+ end
+ end
+
+ describe "with --source flag" do
+ before :each do
+ build_repo2
+ @git = build_git "foo", :path => lib_path("foo") do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ git "#{lib_path("foo")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "updates the source" do
+ update_git "foo", :path => @git.path
+
+ bundle "update --source foo"
+
+ in_app_root do
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+ end
+
+ it "unlocks gems that were originally pulled in by the source" do
+ update_git "foo", "2.0", :path => @git.path
+
+ bundle "update --source foo"
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+
+ it "leaves all other gems frozen" do
+ update_repo2
+ update_git "foo", :path => @git.path
+
+ bundle "update --source foo"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ context "when the gem and the repository have different names" do
+ before :each do
+ build_repo2
+ @git = build_git "foo", :path => lib_path("bar")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ git "#{lib_path("bar")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "< 2" do
+ spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
+ spec_lines[5] = "s.version = '2.0'"
+
+ update_git "foo", "2.0", :path => @git.path do |s|
+ s.write "foo.gemspec", spec_lines.join("\n")
+ end
+
+ ref = @git.ref_for "master"
+
+ bundle "update --source bar"
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{@git.path}
+ revision: #{ref}
+ specs:
+ foo (2.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "2" do
+ spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
+ spec_lines[5] = "s.version = '2.0'"
+
+ update_git "foo", "2.0", :path => @git.path do |s|
+ s.write "foo.gemspec", spec_lines.join("\n")
+ end
+
+ ref = @git.ref_for "master"
+
+ bundle "update --source bar"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ GIT
+ remote: #{@git.path}
+ revision: #{ref}
+ specs:
+ foo (2.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+ end
+end
diff --git a/spec/bundler/update/path_spec.rb b/spec/bundler/update/path_spec.rb
new file mode 100644
index 0000000000..38c125e04b
--- /dev/null
+++ b/spec/bundler/update/path_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.describe "path sources" do
+ describe "bundle update --source" do
+ it "shows the previous version of the gem when updated from path source" do
+ build_lib "activesupport", "2.3.5", :path => lib_path("rails/activesupport")
+
+ install_gemfile <<-G
+ gem "activesupport", :path => "#{lib_path("rails/activesupport")}"
+ G
+
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+
+ bundle "update --source activesupport"
+ expect(out).to include("Using activesupport 3.0 (was 2.3.5) from source at `#{lib_path("rails/activesupport")}`")
+ end
+ end
+end
diff --git a/spec/bundler/update/redownload_spec.rb b/spec/bundler/update/redownload_spec.rb
new file mode 100644
index 0000000000..018d3ed2e9
--- /dev/null
+++ b/spec/bundler/update/redownload_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update", :bundler => "< 2", :ruby => ">= 2.0" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ before { bundle "config major_deprecations yes" }
+
+ describe "with --force" do
+ it "shows a deprecation when single flag passed" do
+ bundle! "update rack --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "shows a deprecation when multiple flags passed" do
+ bundle! "update rack --no-color --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+
+ describe "with --redownload" do
+ it "does not show a deprecation when single flag passed" do
+ bundle! "update rack --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "does not show a deprecation when single multiple flags passed" do
+ bundle! "update rack --no-color --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+end