summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColby Swandale <colby@taplaboratories.com>2017-03-29 18:52:18 +1100
committerColby Swandale <colby@taplaboratories.com>2017-03-29 18:52:18 +1100
commitfe3046b0b422d0ff453149f4445026072648d6d6 (patch)
tree12a0923433fbe5d880abe23fd0c089c072031213
parent9f358b82190dee44567726c5352c24109f3550ad (diff)
parent22db10fe2c1ff89468de2b3b2ce85a21902bf576 (diff)
downloadbundler-fe3046b0b422d0ff453149f4445026072648d6d6.tar.gz
Merge remote-tracking branch 'upstream/master' into bundler-config-parseable-flag
* upstream/master: (522 commits) add tests fix EOL Require socket in the file that needs it use Gem::Version.correct? instead of catching exceptions remove rails 2.3 from ISSUES guide quote empty PATH turns out linux needs env vars to be quoted (even when empty) run specs without git in the env add specs to test gem command when git is not installed (note: this won't actually work at the moment) dont create .gitignore as well if git is not installed in the gem command dont init new git repo unless git is installed on gem command fix spacing between paragraphs in generated READMEs for new gems Account for differing behavior in hash#select between 1.8.7 and 1.9.3+ .map -> .each, as we are not using return value Only uninstall plugins that we attempted to install. Documentation at the correct place Double quotes and small documentation --key parameter can be set via bundle config gem.push_key Add a spec for nested bundle exec with a gem that shadows a default gem Allow installing a gem from an arbitrary repo in the specs ...
-rw-r--r--.gitignore2
-rw-r--r--.rubocop.yml2
-rw-r--r--.rubocop_todo.yml183
-rw-r--r--.travis.yml44
-rw-r--r--CHANGELOG.md211
-rw-r--r--CONTRIBUTING.md39
-rw-r--r--DEVELOPMENT.md148
-rw-r--r--README.md22
-rw-r--r--Rakefile59
-rwxr-xr-xbin/rubocop2
-rw-r--r--bundler.gemspec10
-rw-r--r--doc/README.md30
-rw-r--r--doc/TROUBLESHOOTING.md64
-rw-r--r--doc/contributing/BUG_TRIAGE.md36
-rw-r--r--doc/contributing/COMMUNITY.md13
-rw-r--r--doc/contributing/GETTING_HELP.md11
-rw-r--r--doc/contributing/HOW_YOU_CAN_HELP.md27
-rw-r--r--doc/contributing/ISSUES.md (renamed from ISSUES.md)61
-rw-r--r--doc/contributing/README.md38
-rw-r--r--doc/development/NEW_FEATURES.md10
-rw-r--r--doc/development/PULL_REQUESTS.md40
-rw-r--r--doc/development/README.md19
-rw-r--r--doc/development/RELEASING.md9
-rw-r--r--doc/development/SETUP.md29
-rw-r--r--doc/documentation/README.md29
-rw-r--r--doc/documentation/VISION.md26
-rw-r--r--doc/documentation/WRITING.md54
-rwxr-xr-xexe/bundle5
-rw-r--r--lib/bundler.rb77
-rw-r--r--lib/bundler/cli.rb153
-rw-r--r--lib/bundler/cli/check.rb2
-rw-r--r--lib/bundler/cli/clean.rb2
-rw-r--r--lib/bundler/cli/common.rb30
-rw-r--r--lib/bundler/cli/doctor.rb4
-rw-r--r--lib/bundler/cli/exec.rb6
-rw-r--r--lib/bundler/cli/gem.rb50
-rw-r--r--lib/bundler/cli/info.rb51
-rw-r--r--lib/bundler/cli/inject.rb6
-rw-r--r--lib/bundler/cli/install.rb32
-rw-r--r--lib/bundler/cli/lock.rb15
-rw-r--r--lib/bundler/cli/open.rb3
-rw-r--r--lib/bundler/cli/outdated.rb216
-rw-r--r--lib/bundler/cli/show.rb1
-rw-r--r--lib/bundler/cli/update.rb29
-rw-r--r--lib/bundler/cli/viz.rb2
-rw-r--r--lib/bundler/compact_index_client.rb108
-rw-r--r--lib/bundler/compact_index_client/cache.rb119
-rw-r--r--lib/bundler/compact_index_client/updater.rb88
-rw-r--r--lib/bundler/current_ruby.rb7
-rw-r--r--lib/bundler/definition.rb83
-rw-r--r--lib/bundler/dependency.rb12
-rw-r--r--lib/bundler/dsl.rb5
-rw-r--r--lib/bundler/endpoint_specification.rb9
-rw-r--r--lib/bundler/env.rb46
-rw-r--r--lib/bundler/errors.rb23
-rw-r--r--lib/bundler/feature_flag.rb13
-rw-r--r--lib/bundler/fetcher.rb4
-rw-r--r--lib/bundler/fetcher/compact_index.rb46
-rw-r--r--lib/bundler/fetcher/dependency.rb2
-rw-r--r--lib/bundler/fetcher/downloader.rb11
-rw-r--r--lib/bundler/friendly_errors.rb35
-rw-r--r--lib/bundler/gem_helper.rb7
-rw-r--r--lib/bundler/gem_helpers.rb2
-rw-r--r--lib/bundler/gemdeps.rb28
-rw-r--r--lib/bundler/index.rb18
-rw-r--r--lib/bundler/inline.rb4
-rw-r--r--lib/bundler/installer.rb5
-rw-r--r--lib/bundler/installer/gem_installer.rb6
-rw-r--r--lib/bundler/installer/parallel_installer.rb69
-rw-r--r--lib/bundler/lazy_specification.rb16
-rw-r--r--lib/bundler/lockfile_parser.rb1
-rw-r--r--lib/bundler/match_platform.rb16
-rw-r--r--lib/bundler/mirror.rb2
-rw-r--r--lib/bundler/plugin.rb8
-rw-r--r--lib/bundler/plugin/api.rb1
-rw-r--r--lib/bundler/plugin/api/source.rb10
-rw-r--r--lib/bundler/plugin/index.rb2
-rw-r--r--lib/bundler/postit_trampoline.rb6
-rw-r--r--lib/bundler/remote_specification.rb13
-rw-r--r--lib/bundler/resolver.rb38
-rw-r--r--lib/bundler/retry.rb5
-rw-r--r--lib/bundler/ruby_version.rb10
-rw-r--r--lib/bundler/rubygems_ext.rb16
-rw-r--r--lib/bundler/rubygems_gem_installer.rb38
-rw-r--r--lib/bundler/rubygems_integration.rb59
-rw-r--r--lib/bundler/settings.rb45
-rw-r--r--lib/bundler/setup.rb2
-rw-r--r--lib/bundler/shared_helpers.rb46
-rw-r--r--lib/bundler/source.rb24
-rw-r--r--lib/bundler/source/git.rb7
-rw-r--r--lib/bundler/source/git/git_proxy.rb3
-rw-r--r--lib/bundler/source/path.rb28
-rw-r--r--lib/bundler/source/path/installer.rb4
-rw-r--r--lib/bundler/source/rubygems.rb11
-rw-r--r--lib/bundler/source/rubygems/remote.rb4
-rw-r--r--lib/bundler/spec_set.rb20
-rw-r--r--lib/bundler/stub_specification.rb7
-rwxr-xr-xlib/bundler/templates/Executable2
-rw-r--r--lib/bundler/templates/Executable.standalone10
-rw-r--r--lib/bundler/templates/newgem/Gemfile.tt4
-rw-r--r--lib/bundler/templates/newgem/LICENSE.txt.tt2
-rw-r--r--lib/bundler/templates/newgem/README.md.tt20
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt10
-rw-r--r--lib/bundler/templates/newgem/bin/console.tt2
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.c.tt8
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.h.tt6
-rw-r--r--lib/bundler/templates/newgem/gitignore.tt5
-rw-r--r--lib/bundler/templates/newgem/lib/newgem.rb.tt12
-rw-r--r--lib/bundler/templates/newgem/lib/newgem/version.rb.tt8
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt18
-rw-r--r--lib/bundler/templates/newgem/spec/newgem_spec.rb.tt2
-rw-r--r--lib/bundler/templates/newgem/spec/spec_helper.rb.tt14
-rw-r--r--lib/bundler/templates/newgem/test/newgem_test.rb.tt2
-rw-r--r--lib/bundler/templates/newgem/test/test_helper.rb.tt6
-rw-r--r--lib/bundler/ui/shell.rb4
-rw-r--r--lib/bundler/ui/silent.rb21
-rw-r--r--lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb78
-rw-r--r--lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb112
-rw-r--r--lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb80
-rw-r--r--lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb27
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb2
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb15
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb62
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb13
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb13
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb2
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb2
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb92
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb (renamed from lib/bundler/vendor/net/http/faster.rb)1
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb (renamed from lib/bundler/vendor/net/http/persistent.rb)47
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb (renamed from lib/bundler/vendor/net/http/persistent/ssl_reuse.rb)3
-rw-r--r--lib/bundler/vendor/thor/lib/thor.rb54
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions.rb43
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/create_file.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/create_link.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/directory.rb4
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb16
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb35
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb24
-rw-r--r--lib/bundler/vendor/thor/lib/thor/base.rb60
-rw-r--r--lib/bundler/vendor/thor/lib/thor/command.rb18
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb10
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb12
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb157
-rw-r--r--lib/bundler/vendor/thor/lib/thor/error.rb6
-rw-r--r--lib/bundler/vendor/thor/lib/thor/group.rb24
-rw-r--r--lib/bundler/vendor/thor/lib/thor/invocation.rb9
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/argument.rb11
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/arguments.rb32
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/option.rb59
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/options.rb12
-rw-r--r--lib/bundler/vendor/thor/lib/thor/runner.rb50
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/basic.rb67
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/color.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/html.rb8
-rw-r--r--lib/bundler/vendor/thor/lib/thor/util.rb15
-rw-r--r--lib/bundler/vendor/thor/lib/thor/version.rb2
-rw-r--r--lib/bundler/vendored_persistent.rb13
-rw-r--r--lib/bundler/version.rb15
-rw-r--r--lib/bundler/worker.rb33
-rw-r--r--man/bundle-check.ronn26
-rw-r--r--man/bundle-clean.ronn18
-rw-r--r--man/bundle-config.ronn99
-rw-r--r--man/bundle-exec.ronn9
-rw-r--r--man/bundle-info.ronn17
-rw-r--r--man/bundle-init.ronn18
-rw-r--r--man/bundle-inject.ronn22
-rw-r--r--man/bundle-install.ronn2
-rw-r--r--man/bundle-lock.ronn47
-rw-r--r--man/bundle-open.ronn19
-rw-r--r--man/bundle-outdated.ronn107
-rw-r--r--man/bundle-show.ronn20
-rw-r--r--man/bundle-update.ronn155
-rw-r--r--man/bundle-viz.ronn30
-rw-r--r--man/bundle.ronn36
-rw-r--r--man/gemfile.5.ronn72
-rw-r--r--man/index.txt7
-rw-r--r--spec/bundler/bundler_spec.rb47
-rw-r--r--spec/bundler/cli_spec.rb67
-rw-r--r--spec/bundler/compact_index_client/updater_spec.rb30
-rw-r--r--spec/bundler/definition_spec.rb128
-rw-r--r--spec/bundler/dsl_spec.rb6
-rw-r--r--spec/bundler/endpoint_specification_spec.rb15
-rw-r--r--spec/bundler/env_spec.rb14
-rw-r--r--spec/bundler/environment_preserver_spec.rb2
-rw-r--r--spec/bundler/fetcher/base_spec.rb2
-rw-r--r--spec/bundler/fetcher/compact_index_spec.rb46
-rw-r--r--spec/bundler/fetcher/dependency_spec.rb6
-rw-r--r--spec/bundler/fetcher/downloader_spec.rb2
-rw-r--r--spec/bundler/fetcher/index_spec.rb2
-rw-r--r--spec/bundler/fetcher_spec.rb2
-rw-r--r--spec/bundler/friendly_errors_spec.rb186
-rw-r--r--spec/bundler/gem_helper_spec.rb7
-rw-r--r--spec/bundler/gem_version_promoter_spec.rb2
-rw-r--r--spec/bundler/index_spec.rb10
-rw-r--r--spec/bundler/installer/gem_installer_spec.rb27
-rw-r--r--spec/bundler/installer/parallel_installer_spec.rb47
-rw-r--r--spec/bundler/installer/spec_installation_spec.rb (renamed from spec/install/parallel/spec_installation_spec.rb)17
-rw-r--r--spec/bundler/lockfile_parser_spec.rb2
-rw-r--r--spec/bundler/mirror_spec.rb16
-rw-r--r--spec/bundler/plugin/api/source_spec.rb2
-rw-r--r--spec/bundler/plugin/api_spec.rb2
-rw-r--r--spec/bundler/plugin/dsl_spec.rb2
-rw-r--r--spec/bundler/plugin/index_spec.rb6
-rw-r--r--spec/bundler/plugin/installer_spec.rb2
-rw-r--r--spec/bundler/plugin/source_list_spec.rb2
-rw-r--r--spec/bundler/plugin_spec.rb2
-rw-r--r--spec/bundler/psyched_yaml_spec.rb2
-rw-r--r--spec/bundler/remote_specification_spec.rb20
-rw-r--r--spec/bundler/retry_spec.rb6
-rw-r--r--spec/bundler/ruby_dsl_spec.rb2
-rw-r--r--spec/bundler/ruby_version_spec.rb10
-rw-r--r--spec/bundler/rubygems_integration_spec.rb2
-rw-r--r--spec/bundler/settings_spec.rb46
-rw-r--r--spec/bundler/shared_helpers_spec.rb28
-rw-r--r--spec/bundler/source/git/git_proxy_spec.rb2
-rw-r--r--spec/bundler/source/rubygems/remote_spec.rb2
-rw-r--r--spec/bundler/source/rubygems_spec.rb2
-rw-r--r--spec/bundler/source_list_spec.rb2
-rw-r--r--spec/bundler/source_spec.rb30
-rw-r--r--spec/bundler/spec_set_spec.rb2
-rw-r--r--spec/bundler/ssl_certs/certificate_manager_spec.rb2
-rw-r--r--spec/bundler/ui_spec.rb28
-rw-r--r--spec/bundler/uri_credentials_filter_spec.rb2
-rw-r--r--spec/bundler/worker_spec.rb22
-rw-r--r--spec/bundler/yaml_serializer_spec.rb2
-rw-r--r--spec/cache/cache_path_spec.rb2
-rw-r--r--spec/cache/gems_spec.rb4
-rw-r--r--spec/cache/git_spec.rb4
-rw-r--r--spec/cache/path_spec.rb2
-rw-r--r--spec/cache/platform_spec.rb2
-rw-r--r--spec/commands/binstubs_spec.rb4
-rw-r--r--spec/commands/check_spec.rb2
-rw-r--r--spec/commands/clean_spec.rb34
-rw-r--r--spec/commands/config_spec.rb4
-rw-r--r--spec/commands/console_spec.rb2
-rw-r--r--spec/commands/doctor_spec.rb2
-rw-r--r--spec/commands/exec_spec.rb96
-rw-r--r--spec/commands/help_spec.rb25
-rw-r--r--spec/commands/info_spec.rb58
-rw-r--r--spec/commands/init_spec.rb2
-rw-r--r--spec/commands/inject_spec.rb12
-rw-r--r--spec/commands/install_spec.rb8
-rw-r--r--spec/commands/licenses_spec.rb2
-rw-r--r--spec/commands/lock_spec.rb90
-rw-r--r--spec/commands/newgem_spec.rb117
-rw-r--r--spec/commands/open_spec.rb9
-rw-r--r--spec/commands/outdated_spec.rb348
-rw-r--r--spec/commands/package_spec.rb4
-rw-r--r--spec/commands/show_spec.rb29
-rw-r--r--spec/commands/update_spec.rb210
-rw-r--r--spec/commands/viz_spec.rb2
-rw-r--r--spec/install/allow_offline_install_spec.rb2
-rw-r--r--spec/install/binstubs_spec.rb2
-rw-r--r--spec/install/bundler_spec.rb9
-rw-r--r--spec/install/deploy_spec.rb29
-rw-r--r--spec/install/failure_spec.rb33
-rw-r--r--spec/install/force_spec.rb2
-rw-r--r--spec/install/gemfile/eval_gemfile_spec.rb20
-rw-r--r--spec/install/gemfile/gemspec_spec.rb129
-rw-r--r--spec/install/gemfile/git_spec.rb103
-rw-r--r--spec/install/gemfile/groups_spec.rb2
-rw-r--r--spec/install/gemfile/path_spec.rb2
-rw-r--r--spec/install/gemfile/platform_spec.rb53
-rw-r--r--spec/install/gemfile/ruby_spec.rb22
-rw-r--r--spec/install/gemfile/sources_spec.rb68
-rw-r--r--spec/install/gemfile/specific_platform_spec.rb22
-rw-r--r--spec/install/gemfile_spec.rb31
-rw-r--r--spec/install/gems/compact_index_spec.rb54
-rw-r--r--spec/install/gems/dependency_api_spec.rb4
-rw-r--r--spec/install/gems/env_spec.rb2
-rw-r--r--spec/install/gems/flex_spec.rb2
-rw-r--r--spec/install/gems/mirror_spec.rb2
-rw-r--r--spec/install/gems/native_extensions_spec.rb44
-rw-r--r--spec/install/gems/post_install_spec.rb2
-rw-r--r--spec/install/gems/resolving_spec.rb2
-rw-r--r--spec/install/gems/standalone_spec.rb8
-rw-r--r--spec/install/gems/sudo_spec.rb2
-rw-r--r--spec/install/gems/win32_spec.rb2
-rw-r--r--spec/install/gemspecs_spec.rb2
-rw-r--r--spec/install/git_spec.rb2
-rw-r--r--spec/install/path_spec.rb32
-rw-r--r--spec/install/post_bundle_message_spec.rb21
-rw-r--r--spec/install/prereleases_spec.rb2
-rw-r--r--spec/install/security_policy_spec.rb4
-rw-r--r--spec/install/yanked_spec.rb31
-rw-r--r--spec/lock/git_spec.rb2
-rw-r--r--spec/lock/lockfile_spec.rb32
-rw-r--r--spec/other/bundle_ruby_spec.rb2
-rw-r--r--spec/other/cli_dispatch_spec.rb2
-rw-r--r--spec/other/ext_spec.rb12
-rw-r--r--spec/other/major_deprecation_spec.rb8
-rw-r--r--spec/other/platform_spec.rb2
-rw-r--r--spec/other/ssl_cert_spec.rb2
-rw-r--r--spec/other/trampoline_spec.rb10
-rw-r--r--spec/plugins/command_spec.rb2
-rw-r--r--spec/plugins/hook_spec.rb2
-rw-r--r--spec/plugins/install_spec.rb14
-rw-r--r--spec/plugins/source/example_spec.rb2
-rw-r--r--spec/plugins/source_spec.rb2
-rw-r--r--spec/quality_spec.rb34
-rw-r--r--spec/realworld/dependency_api_spec.rb3
-rw-r--r--spec/realworld/edgecases_spec.rb166
-rw-r--r--spec/realworld/gemfile_source_header_spec.rb3
-rw-r--r--spec/realworld/mirror_probe_spec.rb19
-rw-r--r--spec/realworld/parallel_spec.rb2
-rw-r--r--spec/resolver/basic_spec.rb18
-rw-r--r--spec/resolver/platform_spec.rb2
-rw-r--r--spec/runtime/executable_spec.rb2
-rw-r--r--spec/runtime/gem_tasks_spec.rb2
-rw-r--r--spec/runtime/inline_spec.rb85
-rw-r--r--spec/runtime/load_spec.rb9
-rw-r--r--spec/runtime/platform_spec.rb34
-rw-r--r--spec/runtime/require_spec.rb6
-rw-r--r--spec/runtime/setup_spec.rb60
-rw-r--r--spec/runtime/with_clean_env_spec.rb6
-rw-r--r--spec/spec_helper.rb22
-rw-r--r--spec/support/artifice/compact_index.rb12
-rw-r--r--spec/support/artifice/compact_index_concurrent_download.rb4
-rw-r--r--spec/support/artifice/compact_index_extra_api.rb4
-rw-r--r--spec/support/artifice/compact_index_wrong_dependencies.rb16
-rw-r--r--spec/support/artifice/endpoint.rb13
-rw-r--r--spec/support/artifice/endpoint_500.rb6
-rw-r--r--spec/support/artifice/fail.rb46
-rw-r--r--spec/support/artifice/windows.rb48
-rw-r--r--spec/support/builders.rb18
-rw-r--r--spec/support/fakeweb/rack-1.0.0.marshal2
-rw-r--r--spec/support/fakeweb/windows.rb27
-rw-r--r--spec/support/hax.rb8
-rw-r--r--spec/support/helpers.rb62
-rw-r--r--spec/support/path.rb4
-rw-r--r--spec/support/rubygems_ext.rb21
-rw-r--r--spec/update/gems/post_install_spec.rb77
-rw-r--r--spec/update/git_spec.rb2
-rw-r--r--spec/update/path_spec.rb2
-rw-r--r--task/release.rake115
342 files changed, 7103 insertions, 2005 deletions
diff --git a/.gitignore b/.gitignore
index bd5d29dc7b..88730f82cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,8 @@
# output from ronn
/lib/bundler/man/
+man/*
+!man/*.ronn
# rspec failure tracking
.rspec_status
diff --git a/.rubocop.yml b/.rubocop.yml
index b027d4cf54..ded01cbb21 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -15,7 +15,7 @@ Lint/AssignmentInCondition:
Enabled: false
Lint/EndAlignment:
- AlignWith: variable
+ EnforcedStyleAlignWith: variable
AutoCorrect: true
Lint/UnusedMethodArgument:
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 3a23005c2b..4917f5af50 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,17 +1,16 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
-# on 2016-07-27 12:41:39 -0500 using RuboCop version 0.41.2.
+# on 2017-03-11 17:10:16 -0500 using RuboCop version 0.47.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 4
-Lint/Eval:
+# Offense count: 2
+Lint/EmptyWhen:
Exclude:
- - 'lib/bundler.rb'
- - 'lib/bundler/endpoint_specification.rb'
- - 'spec/support/streams.rb'
+ - 'lib/bundler/friendly_errors.rb'
+ - 'spec/support/builders.rb'
# Offense count: 4
Lint/HandleExceptions:
@@ -41,6 +40,7 @@ Lint/RescueException:
- 'lib/bundler/worker.rb'
# Offense count: 1
+# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
Lint/UselessAccessModifier:
Exclude:
- 'lib/bundler/fetcher.rb'
@@ -51,18 +51,18 @@ Lint/UselessAssignment:
- 'lib/bundler/index.rb'
- 'lib/bundler/installer.rb'
-# Offense count: 1686
-# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
+# Offense count: 442
+# Configuration parameters: CountComments, ExcludedMethods.
+Metrics/BlockLength:
+ Max: 980
+
+# Offense count: 1966
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 207
-# Offense count: 3
-# Configuration parameters: CountKeywordArgs.
-Metrics/ParameterLists:
- Max: 6
-
-# Offense count: 6
+# Offense count: 7
# Cop supports --auto-correct.
Performance/RedundantBlockCall:
Exclude:
@@ -79,41 +79,61 @@ Performance/RedundantMatch:
- 'lib/bundler/definition.rb'
- 'lib/bundler/lockfile_parser.rb'
-# Offense count: 6
+# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: MaxKeyValuePairs.
Performance/RedundantMerge:
Exclude:
- 'lib/bundler/cli/gem.rb'
- - 'spec/support/helpers.rb'
+
+# Offense count: 4
+Security/Eval:
+ Exclude:
+ - 'lib/bundler.rb'
+ - 'lib/bundler/endpoint_specification.rb'
+ - 'spec/support/streams.rb'
+
+# Offense count: 6
+Security/MarshalLoad:
+ Exclude:
+ - 'lib/bundler.rb'
+ - 'spec/install/gems/resolving_spec.rb'
+ - 'spec/support/artifice/compact_index.rb'
+ - 'spec/support/artifice/endpoint.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Security/YAMLLoad:
+ Exclude:
+ - 'spec/bundler/yaml_serializer_spec.rb'
+ - 'spec/commands/inject_spec.rb'
# Offense count: 1
Style/AccessorMethodName:
Exclude:
- 'lib/bundler/source/git.rb'
-# Offense count: 3
+# Offense count: 4
Style/CaseEquality:
Exclude:
- 'lib/bundler/dsl.rb'
- 'lib/bundler/match_platform.rb'
- 'lib/bundler/rubygems_ext.rb'
-# Offense count: 23
+# Offense count: 24
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Enabled: false
-# Offense count: 10
+# Offense count: 9
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
+# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'lib/bundler/cli.rb'
- 'lib/bundler/cli/gem.rb'
- - 'lib/bundler/cli/lock.rb'
- 'lib/bundler/cli/platform.rb'
- 'lib/bundler/dsl.rb'
- 'lib/bundler/lazy_specification.rb'
@@ -122,10 +142,23 @@ Style/ConditionalAssignment:
- 'lib/bundler/source/git.rb'
- 'lib/bundler/source/rubygems.rb'
-# Offense count: 138
+# Offense count: 154
Style/Documentation:
Enabled: false
+# Offense count: 17
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: compact, expanded
+Style/EmptyMethod:
+ Exclude:
+ - 'exe/bundle_ruby'
+ - 'lib/bundler/cli.rb'
+ - 'lib/bundler/plugin/api/source.rb'
+ - 'lib/bundler/rubygems_integration.rb'
+ - 'lib/bundler/ui/silent.rb'
+ - 'spec/support/artifice/fail.rb'
+
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
@@ -133,13 +166,31 @@ Style/ExtraSpacing:
Exclude:
- 'lib/bundler/cli.rb'
-# Offense count: 4
+# Offense count: 2
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
Exclude:
- 'lib/bundler/cli.rb'
- 'spec/spec_helper.rb'
- - 'spec/support/helpers.rb'
+
+# Offense count: 18
+# Configuration parameters: MinBodyLength.
+Style/GuardClause:
+ Exclude:
+ - 'lib/bundler/cli/cache.rb'
+ - 'lib/bundler/cli/clean.rb'
+ - 'lib/bundler/cli/install.rb'
+ - 'lib/bundler/cli/outdated.rb'
+ - 'lib/bundler/cli/package.rb'
+ - 'lib/bundler/definition.rb'
+ - 'lib/bundler/dsl.rb'
+ - 'lib/bundler/installer.rb'
+ - 'lib/bundler/lockfile_parser.rb'
+ - 'lib/bundler/runtime.rb'
+ - 'lib/bundler/source/path/installer.rb'
+ - 'lib/bundler/source_list.rb'
+ - 'spec/commands/newgem_spec.rb'
+ - 'spec/support/sometimes.rb'
# Offense count: 1
Style/IfInsideElse:
@@ -151,13 +202,23 @@ Style/IfUnlessModifierOfIfUnless:
Exclude:
- 'spec/support/helpers.rb'
-# Offense count: 4
+# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: SupportedStyles, IndentationWidth.
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
Style/IndentArray:
EnforcedStyle: consistent
+# Offense count: 6
+Style/MethodMissing:
+ Exclude:
+ - 'lib/bundler/dep_proxy.rb'
+ - 'lib/bundler/dsl.rb'
+ - 'lib/bundler/lazy_specification.rb'
+ - 'lib/bundler/plugin/dsl.rb'
+ - 'lib/bundler/remote_specification.rb'
+ - 'spec/support/builders.rb'
+
# Offense count: 2
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: module_function, extend_self
@@ -166,6 +227,17 @@ Style/ModuleFunction:
- 'lib/bundler/shared_helpers.rb'
- 'spec/support/path.rb'
+# Offense count: 11
+# Cop supports --auto-correct.
+Style/MultilineIfModifier:
+ Exclude:
+ - 'bin/with_rubygems'
+ - 'lib/bundler/installer.rb'
+ - 'lib/bundler/psyched_yaml.rb'
+ - 'lib/bundler/rubygems_ext.rb'
+ - 'lib/bundler/runtime.rb'
+ - 'lib/bundler/source/rubygems.rb'
+
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
@@ -183,6 +255,18 @@ Style/NestedParenthesizedCalls:
- 'spec/commands/lock_spec.rb'
- 'spec/runtime/setup_spec.rb'
+# Offense count: 6
+# Cop supports --auto-correct.
+# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+ Exclude:
+ - 'spec/**/*'
+ - 'lib/bundler/gem_helper.rb'
+ - 'lib/bundler/mirror.rb'
+ - 'lib/bundler/source/git/git_proxy.rb'
+ - 'lib/bundler/source/path.rb'
+
# Offense count: 9
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
# NamePrefix: is_, has_, have_
@@ -198,12 +282,27 @@ Style/PredicateName:
- 'lib/bundler/source/git/git_proxy.rb'
- 'lib/bundler/source/path.rb'
-# Offense count: 25
+# Offense count: 24
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
- Enabled: false
+ Exclude:
+ - 'lib/bundler/cli/install.rb'
+ - 'lib/bundler/deployment.rb'
+ - 'lib/bundler/fetcher/downloader.rb'
+ - 'lib/bundler/fetcher/index.rb'
+ - 'lib/bundler/installer/standalone.rb'
+ - 'lib/bundler/rubygems_integration.rb'
+ - 'lib/bundler/shared_helpers.rb'
+ - 'lib/bundler/source/git/git_proxy.rb'
+ - 'lib/bundler/source/rubygems/remote.rb'
+ - 'spec/bundler/endpoint_specification_spec.rb'
+ - 'spec/bundler/fetcher/dependency_spec.rb'
+ - 'spec/bundler/fetcher/downloader_spec.rb'
+ - 'spec/bundler/fetcher/index_spec.rb'
+ - 'spec/bundler/rubygems_integration_spec.rb'
+ - 'spec/bundler/shared_helpers_spec.rb'
# Offense count: 2
# Cop supports --auto-correct.
@@ -219,14 +318,31 @@ Style/SpaceAroundOperators:
Exclude:
- 'lib/bundler/retry.rb'
-# Offense count: 10
+# Offense count: 2
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
-# SupportedStyles: comma, consistent_comma, no_comma
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Style/SpaceInsideBlockBraces:
+ Exclude:
+ - 'lib/bundler/installer/parallel_installer.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
+# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
+Style/TernaryParentheses:
+ Exclude:
+ - 'lib/bundler/cli/common.rb'
+ - 'lib/bundler/gem_version_promoter.rb'
+
+# Offense count: 9
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInLiteral:
Exclude:
- 'lib/bundler/cli/gem.rb'
- - 'lib/bundler/dependency.rb'
- 'lib/bundler/fetcher.rb'
- 'lib/bundler/gem_helpers.rb'
- 'lib/bundler/graph.rb'
@@ -234,7 +350,7 @@ Style/TrailingCommaInLiteral:
- 'lib/bundler/similarity_detector.rb'
- 'spec/support/artifice/endpoint.rb'
-# Offense count: 18
+# Offense count: 7
# Cop supports --auto-correct.
Style/UnneededInterpolation:
Exclude:
@@ -243,6 +359,3 @@ Style/UnneededInterpolation:
- 'spec/bundler/shared_helpers_spec.rb'
- 'spec/cache/git_spec.rb'
- 'spec/commands/exec_spec.rb'
- - 'spec/support/artifice/endpoint.rb'
- - 'spec/support/artifice/endpoint_500.rb'
- - 'spec/support/fakeweb/windows.rb'
diff --git a/.travis.yml b/.travis.yml
index 88ae318a94..a06deba886 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,11 +13,6 @@ branches:
- /.+-stable$/
notifications:
- email:
- # andre
- - secure: "bCcvqJT7YrBawtkXXwHhT+jOFth7r2Qv/30PkkbhQxk6Jb3xambjCOJ3U6vJ\ngYmiL50exi5lUp3oc3SEbHN5t2CrZqOZDQ6o7P8EAmB5c0oH2RrYaFOkI5Gt\nul/jGH/96A9sj0aMwG7JfdMSfhqj1DUKAm2PnnbXPL853VfmT24="
- # terence
- - secure: "MQ8eA5Jb8YzEpAo58DRGfVJklAPcEbAulpBZnTxp0am6ldneDtJHbQk21w6R\nj5GsDHlzr/lMp/GHIimtUZ7rLohfND8fj/W7fs1Dkd4eN02/ERt98x3pHlqv\nvZgSnZ39uVYv+OcphraE24QaRaGWLhWZAMYQTVe/Yz50NyG8g1U="
slack:
on_success: change
on_failure: always
@@ -30,9 +25,10 @@ addons:
secure: "TrzIv116JLGUxm6PAUskCYrv8KTDguncKROVwbnjVPKTGDAgoDderd8JUdDEXrKoZ9qGLD2TPYKExt9/QDl71E+qHdWnVqWv4HKCUk2P9z/VLKzHuggOUBkCXiJUhjywUieCJhI3N92bfq2EjSBbu2/OFHqWOjLQ+QCooTEBjv8="
rvm:
- - 2.3.1
- - 2.2
- - 2.1
+ - 2.4.0
+ - 2.3.3
+ - 2.2.6
+ - 2.1.10
- 2.0.0
- 1.9.3
- 1.8.7
@@ -43,18 +39,19 @@ env:
# We need to know if changes to rubygems will break bundler on release
- RGV=master
# Test the latest rubygems release with all of our supported rubies
- - RGV=v2.6.6
+ - RGV=v2.6.8
matrix:
include:
+ # Ruby 2.4, Rubygems 2.6.8 and up
# Ruby 2.3, Rubygems 2.5.1 and up
- - rvm: 2.2
+ - rvm: 2.2.6
env: RGV=v2.5.2
# Ruby 2.2, Rubygems 2.4.5 and up
- - rvm: 2.2
+ - rvm: 2.2.6
env: RGV=v2.4.8
# Ruby 2.1, Rubygems 2.2.2 and up
- - rvm: 2.1
+ - rvm: 2.1.10
env: RGV=v2.2.5
# Ruby 2.0.0, Rubygems 2.0.0 and up
- rvm: 2.0.0
@@ -79,11 +76,11 @@ matrix:
- rvm: 1.9.3
env: RGV=v1.5.3
- # ALLOWED FAILURES
# Ruby 1.8.7, Rubygems 1.3.6 and up
- # since the great Travis image outage, frequent random segfaults :'(
- rvm: 1.8.7
env: RGV=v2.2.5
+ # ALLOWED FAILURES
+ # since the great Travis image outage, frequent random segfaults :'(
- rvm: 1.8.7
env: RGV=v2.0.14
- rvm: 1.8.7
@@ -100,13 +97,26 @@ matrix:
env: RGV=v1.3.7
- rvm: 1.8.7
env: RGV=v1.3.6
- # For no apparent reason, this often goes over the Travis limit
- - rvm: 1.8.7
- env: RGV=v2.1.11
# Ruby-head (we want to know how we're doing, but not fail the build)
- rvm: ruby-head
env: RGV=master
allow_failures:
- rvm: 1.8.7
+ env: RGV=v2.0.14
+ - rvm: 1.8.7
+ env: RGV=v1.8.29
+ - rvm: 1.8.7
+ env: RGV=v1.7.2
+ - rvm: 1.8.7
+ env: RGV=v1.6.2
+ - rvm: 1.8.7
+ env: RGV=v1.5.3
+ - rvm: 1.8.7
+ env: RGV=v1.4.2
+ - rvm: 1.8.7
+ env: RGV=v1.3.7
+ - rvm: 1.8.7
+ env: RGV=v1.3.6
- rvm: ruby-head
+ env: RGV=master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7c7f8ba80..55d9ea5ab3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,195 @@
+## 1.14.6 (2017-03-03)
+
+Bugfixes:
+
+ - avoid undefined constant `Bundler::Plugin::API::Source` exception (#5409, @segiddins)
+ - avoid incorrect warnings about needing to enable `specific_platform` (@segiddins)
+ - fail gracefully when the compact index does not send an ETag (#5463, @olleolleolle)
+ - ensure `bundle outdated --local` shows all outdated gems (#5430, @denniss)
+ - fix a case where ruby version requirements could lead to incorrect resolver conflicts (#5425, @segiddins)
+
+## 1.14.5 (2017-02-22)
+
+Bugfixes:
+
+ - avoid loading all unused gemspecs during `bundle exec` on RubyGems 2.3+ (@segiddins)
+ - improve resolver performance when dependencies have zero or one total possibilities ignoring requirements (#5444, #5457, @segiddins)
+ - enable compact index when OpenSSL FIPS mode is enabled but not active (#5433, @wjordan)
+ - use github username instead of git name for the github url in `bundle gem` (#5438, @danielpclark)
+ - avoid a TypeError on RubyGems 2.6.8 when no build settings are set for native extensions (@okkez)
+ - fail gracefully when the dependency api is missing runtime dependencies for a gem (@segiddins)
+ - handle when a platform-specific gem has more dependencies than the ruby platform version (#5339, #5426, @segiddins)
+ - allow running bundler on a machine with no home directory where the temporary directory is not writable (#5371, @segiddins)
+ - avoid gem version conflicts on openssl using Ruby 2.5 (#5235, @rhenium)
+ - fail when installing in frozen mode and the dependencies for `gemspec` gems have changed without the lockfile being updated (#5264, @segiddins)
+
+## 1.14.4 (2017-02-12)
+
+Bugfixes:
+
+ - fail gracefully when attempting to overwrite an existing directory with `bundle gem` (#5358, @nodo)
+ - fix a resolver bug that would cause bundler to report conflicts that it could resolve (#5359, #5362, @segiddins)
+ - set native extension build arguments for git gems (#5401, @segiddins)
+ - fix the suggested `bundle lock` command printed when a dependency is unused on any platform (@5t111111)
+ - ensure the version passed to `ruby` in the Gemfile is valid during Gemfile parsing (#5380, @segiddins)
+ - show `bundle inject` usage when too many arguments are passed (#5384, @Shekharrajak)
+ - stop `bundle show --outdated` from implicitly running `bundle update` (#5375, @colby-swandale)
+ - allow the temporary home directory fallback to work for multiple users (@svoop)
+
+## 1.14.3 (2017-01-24)
+
+Bugfixes:
+
+ - fix the resolver attempting to activate ruby-platform gems when the bundle is only for other platforms (#5349, #5356, @segiddins)
+ - avoid re-resolving a locked gemfile that uses `gemspec` and includes development dependencies (#5349, @segiddins)
+
+## 1.14.2 (2017-01-22)
+
+Bugfixes:
+
+ - fix using `force_ruby_platform` on windows (#5344, @segiddins)
+ - fix an incorrect version conflict error when using `gemspec` on multiple platforms (#5340, @segiddins)
+
+## 1.14.1 (2017-01-21)
+
+Bugfixes:
+
+ - work around a ruby 2.2.2 bug that caused a stack consistency error during installation (#5342, @segiddins)
+
+## 1.14.0 (2017-01-20)
+
+Bugfixes:
+
+ - ensure `Settings::Mirror` is autoloaded under the `Settings` namespace
+ (#5238, @segiddins)
+ - fix `bundler/inline` when `BUNDLE_GEMFILE=""` (#5079, @segiddins)
+
+## 1.14.0.pre.2 (2017-01-11)
+
+Bugfixes:
+
+ - allow not selecting a gem when running `bundle open` (#5301, @segiddins)
+ - support installing gems from git branches that contain shell metacharacters (#5295, @segiddins)
+ - fix a resolver error that could leave dependencies unresolved (#5294, @segiddins)
+ - fix a stack overflow error when invoking commands (#5296, @segiddins)
+
+## 1.14.0.pre.1 (2016-12-29)
+
+Features:
+
+ - `bundle doctor` first runs `bundle check` (@segiddins)
+ - the bundler trampoline is automatically enabled when the target version is greater than bundler 2 (@segiddins)
+ - gem checksums returned by rubygems.org are validated when installing gems (#4464, @segiddins)
+ - use the git username as a github username when running `bundle gem` (@JuanitoFatas)
+ - show more context when the resolver conflicts on required ruby and rubygems versions (@segiddins)
+ - improve platform support by allowing bundler to pick the best platform match during dependency resolution, enabled with the `specific_platform` setting (#4295, #4896, @segiddins)
+ - always prompt the user for a password when using `sudo` (#3006, @segiddins)
+ - support running without a home directory (#4778, @segiddins)
+ - print a warning when the gemfile uses a platform conditional that will exclude the gem from all lockfile platforms (@segiddins)
+ - add the `force_ruby_platform` setting to force bundler to install ruby-platform gems, even on other platforms (#4813, @segiddins)
+ - add conservative update options to `bundle lock` (#4912, @chrismo)
+ - improve `bundle outdated` output to group gems by group (@ryanfox1985)
+ - add conservative update options to `bundle update` (#5065, #5076, @chrismo)
+ - print the output of `bundle env` as github-flavored markdown, making it easier to preserve formatting when copy-pasting into a new issue (@segiddins)
+ - configure the persistence file when using `bundle gem` with `rspec` (@segiddins)
+ - add support for the `ruby_25` gemfile filter (@amatsuda)
+ - when installing with a lockfile that is missing dependencies, allow installation to proceed (but without parallelism) (@segiddins)
+
+Performance:
+
+ - improve `require "bundler"` performance by ~5x (@segiddins)
+ - allow install gems in parallel when running on rubygems 2+
+
+Bugfixes:
+
+ - config files with CRLF line endings can be read (#4435, @segiddins)
+ - `bundle lock` activates gems for the current platform even if they were activated under a different platform for a separate dependency (#4896, @segiddins)
+ - running `bundle env` in a directory without a gemfile no longer crashes (@segiddins)
+ - fail gracefully when attempting to use a source with an unknown URI scheme (#4953, @segiddins)
+ - store paths in the lockfile relative to the root gemfile directory when using `eval_gemfile` (#4966, @segiddins)
+ - `bundle lock` will not update without the `--update` flag (#4957, @segiddins)
+ - the `console` binstub generated by `bundle gem` will load `.irbrc` files (@mattbrictson)
+ - print friendly filesystem access errors in the new index (@segiddins)
+ - print a helpful error when running out of memory on jruby (#4673, @segiddins)
+ - load all rubygems plugins when installing gems (#2824, @segiddins)
+ - `bundle clean --dry-run` prints the list of gems without the `--force` option when no path is set (#5027, @hmistry)
+ - local installs no longer print "this gem may have been yanked" (#5022, @hmistry)
+ - avoid leaking `which` output when running `bundle doctor` (@colby-swandale)
+ - print a warning when attempting to `bundle exec` an empty program (#5084, @bronzdoc)
+ - ensure `bundle outdated` lists all outdated gems (#4979, @chrismo)
+ - fail gracefully when attempting to `bundle gem` with an invalid constant name (#5185, @segiddins)
+ - allow `bundler/inline` to work in a directory that contains a gemfile (#5117, @colby-swandale)
+ - ensure that the new index is thread-safe, allowing installation on rbx (#5142, @segiddins)
+ - remove deprecated `rspec` syntax in `bundle gem` output (@gearnode)
+ - fail gracefully when any system error is encountered when touching the filesystem (#5134, @segiddins)
+ - fix compatibility with a machine running with FIPS mode enabled (#4989, @segiddins)
+ - fix `bundle lock --add-platform ruby` (#5230, @segiddins)
+ - print gem post-install messages when running `bundle update` (@smathy)
+ - ensure errors due to a retries are all separated by a newline (@segiddins)
+ - print out the bundle path in gem not found errors (#4854, @diegosteiner)
+ - fail gracefully when creating threads fails (@segiddins)
+ - avoid downloading metadata for gems that are only development dependencies (@Paxa)
+
+## 1.13.7 (2016-12-25)
+
+Features:
+
+ - add support for the `ruby_24` gemfile filter (#5281, @amatsuda)
+
+## 1.13.6 (2016-10-22)
+
+Bugfixes:
+
+ - make the `gem` method public again, fixing a regression in 1.13.4 (#5102, @segiddins)
+
+## 1.13.5 (2016-10-15)
+
+Bugfixes:
+
+ - Ensure a locked pre-release spec can always be re-resolved (#5089, @segiddins)
+
+## 1.13.4 (2016-10-11)
+
+Bugfixes:
+
+ - stop printing warning when compact index versions file is rewritten (#5064, @indirect)
+ - fix `parent directory is world writable but not sticky` error on install (#5043, @indirect)
+ - fix for `uninitialized constant Bundler::Plugin::API::Source` error (#5010, @hsbt, @aycabta)
+ - make `update` options for major, minor, and patch updates consistent (#4934, @chrismo)
+
+## 1.13.3 (2016-10-10)
+
+Bugfixes:
+
+ - add support for weak etags to the new index (@segiddins)
+
+## 1.13.2 (2016-09-30)
+
+Bugfixes:
+
+ - allow `Settings` to be initialized without a root directory (@m1k3)
+ - allow specifying ruby engines in the gemfile as a symbol (#4919, @JuanitoFatas)
+ - avoid an exception when using `bundler/deployment` with Vlad (@srbaker)
+ - ensure redefined methods have the same visibility as the one they're replacing, fixing `Kernel.require` failing on JRuby (#4975, @segiddins)
+ - ensure that Bundler won't complain about a corrupt lockfile when no lockfile exists when using `gemspec` in the Gemfile (#5006, @segiddins)
+ - fail gracefully when parsing the metadata for a gemspec from the compact index fails (@segiddins)
+ - fix system gems not being copied to --path on bundle install (e.g. --deployment) (#4974, @chrismo)
+
+Performance:
+
+ - avoid parsing the lockfile twice when evaluating gemfiles (@segiddins)
+
+## 1.13.1 (2016-09-13)
+
+Bugfixes:
+
+ - ensure that `Gem::Source` is available, fixing several exceptions (#4944, @dekellum)
+ - ensure that dependency resolution works when multiple gems have the same dependency (#4961, @segiddins)
+
+## 1.13.0 (2016-09-05)
+
+This space deliberately left blank.
+
## 1.13.0.rc.2 (2016-08-21)
Features:
@@ -11,7 +203,7 @@ Features:
- print gem installation errors after other install output (#4834, @segiddins)
- add `lock --remove-platform` flag to remove platforms from the lock (#4877, @segiddins)
- add `only_update_to_newer_versions` setting to prevent downgrades during `update` (@segiddins)
- - expanded expirimental plugin support to include hooks and sources (@asutoshpalai)
+ - expanded experimental plugin support to include hooks and sources (@asutoshpalai)
Bugfixes:
@@ -89,6 +281,11 @@ Bugfixes:
- allow running `bundle install --deployment` after `bundle package --all` with path gems (#2175, @allenzhao)
- add support for patchlevels in ruby versions in the gemfile and gemspecs (#4593, @chalkos)
+## 1.12.6 (2016-10-10)
+
+Bugfixes:
+ - add support for weak etags to the new index (@segiddins)
+
## 1.12.5 (2016-05-25)
Bugfixes:
@@ -1208,13 +1405,13 @@ Bugfixes:
Features:
- - compatibile with Ruby 2.0.0-preview2
- - compatibile with Rubygems 2.0.0.preview2 (@drbrain, @evanphx)
+ - compatible with Ruby 2.0.0-preview2
+ - compatible with Rubygems 2.0.0.preview2 (@drbrain, @evanphx)
- ruby 2.0 added to the `:ruby19` ABI-compatible platform
- lazy load YAML, allowing Psych to be specified in the Gemfile
- significant performance improvements (@cheald, #2181)
- `inject` command for scripted Gemfile additions (Engine Yard)
- - :github option uses slashless arguements as repo owner (@rking)
+ - :github option uses slashless arguments as repo owner (@rking)
- `open` suggests gem names for typos (@jdelStrother)
- `update` reports non-existent gems (@jdelStrother)
- `gem` option --test can generate rspec stubs (@MafcoCinco)
@@ -1638,7 +1835,7 @@ Features:
- Add bundle clean. Removes unused gems from --path directory
- Initial Gemcutter Endpoint API work, BAI Fetching source index
- Added bundle install --standalone
- - Ignore Gemfile.lock when buliding new gems
+ - Ignore Gemfile.lock when building new gems
- Make it possible to override a .gemspec dependency's source in the
Gemfile
@@ -1689,7 +1886,7 @@ Bugfixes:
Features:
- - Compatability with Rubygems 1.8.10 installer changes
+ - Compatibility with Rubygems 1.8.10 installer changes
- Report gem installation failures clearly (@rwilcox, #1380)
- Useful error for cap and vlad on first deploy (@nexmat, @kirs)
@@ -2439,7 +2636,7 @@ Bugfixes:
- make the tests platform agnostic so we can confirm that they're green on JRuby
- fixes for Ruby 1.9
-## 0.9.5 (Feburary 12, 2010)
+## 0.9.5 (February 12, 2010)
Features:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8349280ddd..8dc0548add 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,36 +1,17 @@
-# Contributing
+# Contributing to Bundler
-Bundler welcomes contributions from *everyone*. While contributing, please follow the project [code of conduct](http://bundler.io/conduct.html), so that everyone can be included.
-
-If you'd like to help make Bundler better, you totally rock! Here are some ways you can contribute:
-
- - by using prerelease versions (run `gem install bundler --pre`)
- - by [reporting bugs you encounter or suggesting new features](https://github.com/bundler/bundler/issues/new)
- - by adding to or editing [the Bundler documentation website](http://bundler.io) and [Bundler man pages](http://bundler.io/man/bundle.1.html)
- - by [checking issues for completeness](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md#bug-triage)
- - by closing issues that are not complete
- - by adding a failing test for reproducible [reported bugs](https://github.com/bundler/bundler/issues)
- - by reviewing [pull requests](https://github.com/bundler/bundler/pulls) and suggesting improvements
- - by improving existing code, including suggestions from [PullReview](https://www.pullreview.com/github/bundler/bundler/reviews/master) or [CodeClimate](https://codeclimate.com/github/bundler/bundler)
- - by [writing code](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) (no patch is too small! fix typos or bad whitespace)
- - by backfilling [unit tests](https://github.com/bundler/bundler/tree/master/spec/bundler) for modules that lack [coverage](https://codeclimate.com/github/bundler/bundler/coverage)
-
-If you need help getting started, check out the [DEVELOPMENT.md](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) file for steps that will get you up and running.
+Welcome to Bundler! We are so happy that you're here. We know it can be daunting to joining a new open source project, so here's a quick overview of what you can expect from this documentation.
-Thanks for helping us make Bundler better.
-
-# Roadmap
-
-If you're interested in reading some of the potential ideas or plans for Bundler, see the official Bundler [Trello Board](https://trello.com/b/DioUaKgx/bundler).
-
-# Troubleshooting
+Bundler welcomes contributions from *everyone*. While contributing, please follow the project [code of conduct](http://bundler.io/conduct.html), so that everyone can be included.
-If you're having a problem, please see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md) for troubleshooting steps and a guide for how to submit a ticket that will help us solve the problem you are having as quickly as possible.
+## Quick start
-# Requesting Features
+Some guides you might find useful:
-Feel free to discuss your ideas or feature requests on any of the lists or channels listed below or [create an issue](https://github.com/bundler/bundler/issues/new) with the `feature-request` label. Feature-wise, we consider Bundler stable, so the core team does not tend to work on feature suggestions. Pull requests are welcome though!
+* [Submitting pull requests](doc/development/PULL_REQUESTS.md)
+* [Filing an issue](doc/contributing/ISSUES.md)
+* [Bug triage](doc/contributing/BUG_TRIAGE.md)
-# Discussing Bundler
+## Comprehensive guides
-If you'd like to discuss features, ask questions, or just engage in general Bundler-focused discussion, please see the [#bundler](irc://irc.freenode.net/#bundler) IRC channel on Freenode, and the [Bundler mailing list](http://groups.google.com/group/ruby-bundler) on Google Groups.
+Not finding what you're looking for? More comprehensive guides are [available here](doc/README.md).
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
deleted file mode 100644
index 8964b75c3d..0000000000
--- a/DEVELOPMENT.md
+++ /dev/null
@@ -1,148 +0,0 @@
-Great to have you here! Here are a few ways you can help out with [Bundler](http://github.com/bundler/bundler).
-
-# Where should I start?
-
-You can start learning about Bundler by reading [the documentation](http://bundler.io). If you want, you can also read a (lengthy) explanation of [why Bundler exists and what it does](http://bundler.io/rationale.html). You can also check out discussions about Bundler on the [Bundler mailing list](https://groups.google.com/group/ruby-bundler) and in the [Bundler IRC channel](http://webchat.freenode.net/?channels=%23bundler), which is #bundler on Freenode. Please note that this project is released with a contributor [code of conduct](http://bundler.io/conduct.html). By participating in this project you agree to abide by its terms.
-
-## Your first commits
-
-If you’re interested in contributing to Bundler, that’s awesome! We’d love your help.
-
-If you have any questions after reading this page, please feel free to contact either [@indirect](https://github.com/indirect), [@segiddins](https://github.com/segiddins), or [@RochesterinNYC](https://github.com/RochesterinNYC). They are all happy to provide help working through your first bug fix or thinking through the problem you’re trying to resolve.
-
-## How you can help
-
-We track [small bugs and features](https://github.com/bundler/bundler/issues?labels=small) so that anyone who wants to help can start with something that's not too overwhelming. We also keep a [list of things anyone can help with, any time](https://github.com/bundler/bundler/blob/master/CONTRIBUTING.md#contributing). If nothing on those lists looks good, talk to us, and we'll figure out what you can help with. We can absolutely use your help, no matter what level of programming skill you have at the moment.
-
-# Development setup
-
-Bundler doesn't use a Gemfile to list development dependencies, because when we tried it we couldn't tell if we were awake or it was just another level of dreams. To work on Bundler, you'll probably want to do a couple of things.
-
- 1. Install `groff-base` and `graphviz` packages using your package manager, e.g for ubuntu
-
- $ sudo apt-get install graphviz groff-base -y
-
- and for OS X (with brew installed)
-
- $ brew install graphviz homebrew/dupes/groff
-
- 2. Install Bundler's development dependencies
-
- $ bin/rake spec:deps
-
- 3. Run the test suite, to make sure things are working
-
- $ bin/rake spec
-
- 4. Set up a shell alias to run Bundler from your clone, e.g. a Bash alias:
-
- $ alias dbundle='BUNDLE_DISABLE_POSTIT=1 ruby -I /path/to/bundler/lib /path/to/bundler/exe/bundle'
-
- The `BUNDLE_DISABLE_POSTIT` environment variable ensures that the version of Bundler in `/path/to/bundler/lib` will be used. Without that environment setting, Bundler will automatically download, install, and run the version of Bundler listed in `Gemfile.lock`. With that set up, you can test changes you've made to Bundler by running `dbundle`, without interfering with the regular `bundle` command.
-
-# Submitting Pull Requests
-
-Before you submit a pull request, please remember to do the following:
-
-- Make sure the code formatting and styling adheres to the guidelines. We use Rubocop for this. Lack of formatting adherence will result in automatic Travis build failures.
-
- $ bin/rubocop -a
-
-- Please run the test suite:
-
- $ bin/rspec
-
-- If you are unable to run the entire test suite, please run the unit test suite and at least the integration specs related to the command or domain of Bundler that your code changes relate to.
-
-- Ex. For a pull request that changes something with `bundle update`, you might run:
-
- $ bin/rspec spec/bundler
- $ bin/rspec spec/commands/update_spec.rb
-
-- Please ensure that the commit messages included in the pull request __do not__ have the following:
- - `@tag` Github user or team references (ex. `@indirect` or `@bundler/core`)
- - `#id` references to issues or pull requests (ex. `#43` or `bundler/bundler-site#12`)
-
- If you want to use these mechanisms, please instead include them in the pull request description. This prevents multiple notifications or references being created on commit rebases or pull request/branch force pushes.
-
-- Additionally, do not use `[ci skip]` or `[skip ci]` mechanisms in your pull request titles/descriptions or commit messages. Every potential commit and pull request should run through Bundler's CI system. This applies to all changes/commits (ex. even a change to just documentation or the removal of a comment).
-
-# Bug triage
-
-Triage is the work of processing tickets that have been opened into actionable issues, feature requests, or bug reports. That includes verifying bugs, categorizing the ticket, and ensuring there's enough information to reproduce the bug for anyone who wants to try to fix it.
-
-We've created an [issues guide](https://github.com/bundler/bundler/blob/master/ISSUES.md) to walk Bundler users through the process of troubleshooting issues and reporting bugs.
-
-If you'd like to help, awesome! You can [report a new bug](https://github.com/bundler/bundler/issues/new) or browse our [existing open tickets](https://github.com/bundler/bundler/issues).
-
-Not every ticket will point to a bug in Bundler's code, but open tickets usually mean that there is something we could improve to help that user. Sometimes that means writing additional documentation, sometimes that means making error messages clearer, and sometimes that means explaining to a user that they need to install git to use git gems.
-
-When you're looking at a ticket, here are the main questions to ask:
-
- * Can I reproduce this bug myself?
- * Are the steps to reproduce clearly stated in the ticket?
- * Which versions of Bundler (1.1.x, 1.2.x, git, etc.) manifest this bug?
- * Which operating systems (OS X, Windows, Ubuntu, CentOS, etc.) manifest this bug?
- * Which rubies (MRI, JRuby, Rubinius, etc.) and which versions (1.8.7, 1.9.3, etc.) have this bug?
-
-If you can't reproduce an issue, chances are good that the bug has been fixed (hurrah!). That's a good time to post to the ticket explaining what you did and how it worked.
-
-If you can reproduce an issue, you're well on your way to fixing it. :) Fixing issues is similar to adding new features:
-
- 1. Discuss the fix on the existing issue. Coordinating with everyone else saves duplicate work and serves as a great way to get suggestions and ideas if you need any.
- 2. Base your commits on the correct branch. Bugfixes for 1.x versions of Bundler should be based on the matching 1-x-stable branch.
- 3. Commit the code and at least one test covering your changes to a named branch in your fork.
- 4. Put a line in the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md) summarizing your changes under the next release under the “Bugfixes” heading.
- 5. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your bugfix branch.
-
-Finally, the ticket may be a duplicate of another older ticket. If you notice a ticket is a duplicate, simply comment on the ticket noting the original ticket’s number. For example, you could say “This is a duplicate of issue #42, and can be closed”.
-
-
-# Adding New Features
-
-If you would like to add a new feature to Bundler, please follow these steps:
-
- 1. [Create an issue](https://github.com/bundler/bundler/issues/new) with the [`feature-request` label](https://github.com/bundler/bundler/labels/type:%20feature-request) to discuss your feature.
- 2. Base your commits on the master branch, since we follow [SemVer](http://semver.org) and don't add new features to old releases.
- 3. Commit the code and at least one test covering your changes to a feature branch in your fork.
- 4. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your feature branch.
-
-If you don't hear back immediately, don’t get discouraged! We all have day jobs, but we respond to most tickets within a day or two.
-
-
-# Beta testing
-
-Early releases require heavy testing, especially across various system setups. We :heart: testers, and are big fans of anyone who can run `gem install bundler --pre` and try out upcoming releases in their development and staging environments.
-
-There may not always be prereleases or beta versions of Bundler. The Bundler team will tweet from the [@bundlerio account](http://twitter.com/bundlerio) when a prerelease or beta version becomes available. You are also always welcome to try checking out master and building a gem yourself if you want to try out the latest changes.
-
-
-# Translations
-
-We don't currently have any translations, but please reach out to us if you would like to help get this going.
-
-
-# Documentation
-
-Code needs explanation, and sometimes those who know the code well have trouble explaining it to someone just getting into it. Because of that, we welcome documentation suggestions and patches from everyone, especially if they are brand new to using Bundler.
-
-Bundler has two main sources of documentation: the built-in help (including usage information and man pages) and the [Bundler documentation site](http://bundler.io).
-
-If you’d like to submit a patch to the man pages, follow the steps for submitting a pull request above. All of the man pages are located in the `man` directory. Just use the “Documentation” heading when you describe what you did in the changelog.
-
-If you have a suggestion or proposed change for [bundler.io](http://bundler.io), please open an issue or send a pull request to the [bundler-site](https://github.com/bundler/bundler-site) repository.
-
-
-# Community
-
-Community is an important part of all we do. If you’d like to be part of the Bundler community, you can jump right in and start helping make Bundler better for everyone who uses it.
-
-It would be tremendously helpful to have more people answering questions about Bundler (and often simply about [Rubygems](https://github.com/rubygems/rubygems) or Ruby itself) in our [issue tracker](https://github.com/bundler/bundler/issues) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/bundler).
-
-Additional documentation and explanation is always helpful, too. If you have any suggestions for the Bundler website [bundler.io](http://bundler.io), we would absolutely love it if you opened an issue or pull request on the [bundler-site](https://github.com/bundler/bundler-site) repository.
-
-Finally, sharing your experiences and discoveries by writing them up is a valuable way to help others who have similar problems or experiences in the future. You can write a blog post, create an example and commit it to Github, take screenshots, or make videos.
-
-Publishing examples of how Bundler is used helps everyone, and we’ve discovered that people already use it in ways that we never imagined when we were writing it. If you’re still not sure what to write about, there are also several projects doing interesting things based on Bundler. They could probably use publicity too.
-
-Finally, all contributors to the Bundler project must agree to the contributor [code of conduct](http://bundler.io/conduct.html). By participating in this project you agree to abide by its terms.
diff --git a/README.md b/README.md
index c467673067..56ec5c5c63 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,15 @@ It does this by managing the gems that the application depends on. Given a list
### Installation and usage
+To install:
+
```
gem install bundler
+```
+
+Bundler is most commonly used to manage your application's dependencies. To use it for this:
+
+```
bundle init
echo 'gem "rspec"' >> Gemfile
bundle install
@@ -23,17 +30,24 @@ See [bundler.io](http://bundler.io) for the full documentation.
### Troubleshooting
-For help with common problems, see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md).
+For help with common problems, see [TROUBLESHOOTING](doc/TROUBLESHOOTING.md).
+
+Still stuck? Try [filing an issue](doc/contributing/ISSUES.md).
+
+### Supporting
+
+<a href="https://rubytogether.org/"><img src="https://rubytogether.org/images/rubies.svg" width=200></a><br/>
+Bundler is maintained by <a href="https://rubytogether.org/">Ruby Together</a>, a grassroots initiative committed to supporting the critical Ruby infrastructure you rely on. Contribute today <a href="https://rubytogether.org/developers">as an individual</a> or even better, <a href="https://rubytogether.org/companies">as a company</a>, and ensure that Bundler, RubyGems, and other shared tooling is around for years to come.
### Other questions
-To see what has changed in recent versions of Bundler, see the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md).
+To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANGELOG.md).
-Feel free to chat with the Bundler core team (and many other users) on IRC in the [#bundler](irc://irc.freenode.net/bundler) channel on Freenode, or via email on the [Bundler mailing list](http://groups.google.com/group/ruby-bundler).
+To get in touch with the Bundler core team and other Bundler users, please see [getting help](doc/contributing/GETTING_HELP.md).
### Contributing
-If you'd like to contribute to Bundler, that's awesome, and we <3 you. There's a guide to contributing to Bundler (both code and general help) over in [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md).
+If you'd like to contribute to Bundler, that's awesome, and we <3 you. There's a guide to contributing to Bundler (both code and general help) over in [our documentation section](doc/README.md).
### Code of Conduct
diff --git a/Rakefile b/Rakefile
index cc7162a6ef..9b3538707e 100644
--- a/Rakefile
+++ b/Rakefile
@@ -39,7 +39,7 @@ namespace :spec do
deps = Hash[BUNDLER_SPEC.development_dependencies.map do |d|
[d.name, d.requirement.to_s]
end]
- deps["rubocop"] ||= "= 0.41.2" if RUBY_VERSION >= "1.9.3" # can't go in the gemspec because of the ruby version requirement
+ deps["rubocop"] ||= "= 0.47.1" if RUBY_VERSION >= "2.0.0" # can't go in the gemspec because of the ruby version requirement
# JRuby can't build ronn or rdiscount, so we skip that
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
@@ -47,10 +47,10 @@ namespace :spec do
deps.delete("rdiscount")
end
- deps.sort_by {|name, _| name }.each do |name, version|
- sh %(#{Gem.ruby} -S gem list -i "^#{name}$" -v "#{version}" || ) +
- %(#{Gem.ruby} -S gem install #{name} -v "#{version}" --no-ri --no-rdoc)
- end
+ gem_install_command = "install --no-ri --no-rdoc --conservative " + deps.sort_by {|name, _| name }.map do |name, version|
+ "'#{name}:#{version}'"
+ end.join(" ")
+ sh %(#{Gem.ruby} -S gem #{gem_install_command})
# Download and install gems used inside tests
$LOAD_PATH.unshift("./spec")
@@ -74,7 +74,7 @@ namespace :spec do
$LOAD_PATH.unshift("./spec")
require "support/rubygems_ext"
- Spec::Rubygems::DEPS["codeclimate-test-reporter"] = nil if RUBY_VERSION >= "2.2.0"
+ Spec::Rubygems::DEPS["codeclimate-test-reporter"] = "~> 0.6.0" if RUBY_VERSION >= "2.2.0"
# Install the other gem deps, etc
Rake::Task["spec:deps"].invoke
@@ -91,9 +91,9 @@ begin
RSpec::Core::RakeTask.new
task :spec => "man:build"
- if RUBY_VERSION >= "1.9.3"
+ if RUBY_VERSION >= "2.0.0"
# can't go in the gemspec because of the ruby version requirement
- gem "rubocop", "= 0.41.2"
+ gem "rubocop", "= 0.47.1"
require "rubocop/rake_task"
RuboCop::RakeTask.new
end
@@ -127,7 +127,7 @@ begin
rubyopt = ENV["RUBYOPT"]
# When editing this list, also edit .travis.yml!
branches = %w(master)
- releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.5 v2.4.8 v2.5.2 v2.6.6)
+ releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.5 v2.4.8 v2.5.2 v2.6.8)
(branches + releases).each do |rg|
desc "Run specs with Rubygems #{rg}"
RSpec::Core::RakeTask.new(rg) do |t|
@@ -190,7 +190,7 @@ begin
task :travis do
rg = ENV["RGV"] || raise("Rubygems version is required on Travis!")
- if RUBY_VERSION > "1.9.3"
+ if RUBY_VERSION >= "2.0.0"
puts "\n\e[1;33m[Travis CI] Running bundler linter\e[m\n\n"
Rake::Task["rubocop"].invoke
end
@@ -238,14 +238,15 @@ begin
require "ronn"
namespace :man do
- directory "lib/bundler/man"
+ directory "man"
sources = Dir["man/*.ronn"].map {|f| File.basename(f, ".ronn") }
sources.map do |basename|
ronn = "man/#{basename}.ronn"
- roff = "lib/bundler/man/#{basename}"
+ manual_section = ".1" unless basename =~ /.*(\d+)\Z/
+ roff = "man/#{basename}#{manual_section}"
- file roff => ["lib/bundler/man", ronn] do
+ file roff => ["man", ronn] do
sh "#{Gem.ruby} -S ronn --roff --pipe #{ronn} > #{roff}"
end
@@ -257,9 +258,8 @@ begin
end
task :clean do
- leftovers = Dir["lib/bundler/man/*"].reject do |f|
- basename = File.basename(f).sub(/\.(txt|ronn)/, "")
- sources.include?(basename)
+ leftovers = Dir["man/*"].reject do |f|
+ File.extname(f) == ".ronn" || f == "man/index.txt"
end
rm leftovers if leftovers.any?
end
@@ -285,13 +285,6 @@ end
begin
require "automatiek"
- Automatiek::RakeTask.new("compact_index_client") do |lib|
- lib.download = { :github => "https://github.com/bundler/compact_index_client" }
- lib.namespace = "CompactIndexClient"
- lib.prefix = "Bundler"
- lib.vendor_lib = "lib/bundler/vendor/compact_index_client"
- end
-
Automatiek::RakeTask.new("molinillo") do |lib|
lib.download = { :github => "https://github.com/CocoaPods/Molinillo" }
lib.namespace = "Molinillo"
@@ -312,11 +305,29 @@ begin
lib.prefix = "BundlerVendoredPostIt"
lib.vendor_lib = "lib/bundler/vendor/postit"
end
+
+ Automatiek::RakeTask.new("net-http-persistent") do |lib|
+ lib.download = { :github => "https://github.com/drbrain/net-http-persistent" }
+ lib.namespace = "Net::HTTP::Persistent"
+ lib.prefix = "Bundler::Persistent"
+ lib.vendor_lib = "lib/bundler/vendor/net-http-persistent"
+
+ mixin = Module.new do
+ def namespace_files
+ super
+ require_target = vendor_lib.sub(%r{^(.+?/)?lib/}, "") << "/lib"
+ relative_files = files.map {|f| Pathname.new(f).relative_path_from(Pathname.new(vendor_lib) / "lib").sub_ext("").to_s }
+ process_files(/require (['"])(#{Regexp.union(relative_files)})/, "require \\1#{require_target}/\\2")
+ end
+ end
+ lib.send(:extend, mixin)
+ end
rescue LoadError
namespace :vendor do
task(:molinillo) { abort "Install the automatiek gem to be able to vendor gems." }
task(:thor) { abort "Install the automatiek gem to be able to vendor gems." }
task(:postit) { abort "Install the automatiek gem to be able to vendor gems." }
+ task("net-http-persistent") { abort "Install the automatiek gem to be able to vendor gems." }
end
end
@@ -331,3 +342,5 @@ task :build => ["man:build"]
task :release => ["man:require", "man:build"]
task :default => :spec
+
+Dir["task/*.{rb,rake}"].each(&method(:load))
diff --git a/bin/rubocop b/bin/rubocop
index 78641c589f..c7d45e23fb 100755
--- a/bin/rubocop
+++ b/bin/rubocop
@@ -10,7 +10,7 @@ bundler_spec.dependencies.each do |dep|
gem dep.name, dep.requirement
end
-gem "rubocop", "= 0.41.2"
+gem "rubocop", "= 0.47.1"
Gem.finish_resolve if Gem.respond_to?(:finish_resolve)
diff --git a/bundler.gemspec b/bundler.gemspec
index f67c538fd6..ef848e9e8d 100644
--- a/bundler.gemspec
+++ b/bundler.gemspec
@@ -7,8 +7,12 @@ require "bundler/version"
Gem::Specification.new do |s|
s.name = "bundler"
s.version = Bundler::VERSION
- s.licenses = ["MIT"]
- s.authors = ["André Arko", "Samuel Giddins"]
+ s.license = "MIT"
+ s.authors = [
+ "André Arko", "Samuel Giddins", "Chris Morris", "James Wen", "Tim Moore",
+ "André Medeiros", "Jessica Lynn Suttles", "Terence Lee", "Carl Lerche",
+ "Yehuda Katz"
+ ]
s.email = ["team@bundler.io"]
s.homepage = "http://bundler.io"
s.summary = "The best way to manage your application's dependencies"
@@ -27,7 +31,7 @@ Gem::Specification.new do |s|
s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) }
# we don't check in man pages, but we need to ship them because
# we use them to generate the long-form help for each command.
- s.files += Dir.glob("lib/bundler/man/**/*")
+ s.files += Dir.glob("man/**/*")
s.bindir = "exe"
s.executables = %w(bundle bundler)
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000000..534dd941a7
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,30 @@
+# Docs: Contributing and developing Bundler
+
+_If you're looking for documentation on how to use Bundler: visit [bundler.io](http://bundler.io/), or run `bundle help` from the command line. You may also be interested in [troubleshooting common issues](TROUBLESHOOTING.md) found when using Bundler._
+
+Bundler welcomes contributions from *everyone*. While contributing, please follow the project [code of conduct](http://bundler.io/conduct.html), so that everyone can be included.
+
+If you'd like to help make Bundler better, you totally rock! Thanks for helping us make Bundler better.
+
+## Contributing
+
+* [Overview & getting started](contributing/README.md)
+* [How you can help: your first contributions!](contributing/HOW_YOU_CAN_HELP.md)
+* [Bug triage](contributing/BUG_TRIAGE.md)
+* [Getting help](contributing/GETTING_HELP.md)
+* [Filing issues](contributing/ISSUES.md)
+* [Community](contributing/COMMUNITY.md)
+
+## Development
+
+* [Overview](development/README.md)
+* [Development setup](development/SETUP.md)
+* [Submitting pull requests](development/PULL_REQUESTS.md)
+* [Adding new features](development/NEW_FEATURES.md)
+* [Releasing Bundler](development/RELEASING.md)
+
+## Documentation
+
+* [Overview](documentation/README.md)
+* [Writing docs for man pages](documentation/WRITING.md)
+* [Documentation vision](documentation/VISION.md)
diff --git a/doc/TROUBLESHOOTING.md b/doc/TROUBLESHOOTING.md
new file mode 100644
index 0000000000..e9187c50cd
--- /dev/null
+++ b/doc/TROUBLESHOOTING.md
@@ -0,0 +1,64 @@
+# Troubleshooting common issues
+
+Stuck using Bundler? Browse these common issues before [filing a new issue](contributing/ISSUES.md).
+
+## Permission denied when installing bundler
+
+Certain operating systems such as MacOS and Ubuntu have versions of Ruby that require elevated privileges to install gems.
+
+ ERROR: While executing gem ... (Gem::FilePermissionError)
+ You don't have write permissions for the /Library/Ruby/Gems/2.0.0 directory.
+
+There are multiple ways to solve this issue. You can install bundler with elevated privilges using `sudo` or `su`.
+
+ sudo gem install bundler
+
+If you cannot elevated your privileges or do not want to globally install Bundler, you can use the `--user-install` option.
+
+ gem install bundler --user-install
+
+This will install Bundler into your home directory. Note that you will need to append `~/.gem/ruby/<ruby version>/bin` to your `$PATH` variable to use `bundle`.
+
+## Heroku errors
+
+Please open a ticket with [Heroku](https://www.heroku.com) if you're having trouble deploying. They have a professional support team who can help you resolve Heroku issues far better than the Bundler team can. If the problem that you are having turns out to be a bug in Bundler itself, [Heroku support](https://www.heroku.com/support) can get the exact details to us.
+
+## Other problems
+
+First, figure out exactly what it is that you're trying to do (see [XY Problem](http://xyproblem.info/)). Then, go to the [Bundler documentation website](http://bundler.io) and see if we have instructions on how to do that.
+
+Second, check [the compatibility
+list](http://bundler.io/compatibility.html), and make sure that the version of Bundler that you are using works with the versions of Ruby and Rubygems that you are using. To see your versions:
+
+ # Bundler version
+ bundle -v
+
+ # Ruby version
+ ruby -v
+
+ # Rubygems version
+ gem -v
+
+If these instructions don't work, or you can't find any appropriate instructions, you can try these troubleshooting steps:
+
+ # Remove user-specific gems and git repos
+ rm -rf ~/.bundle/ ~/.gem/bundler/ ~/.gems/cache/bundler/
+
+ # Remove system-wide git repos and git checkouts
+ rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/
+
+ # Remove project-specific settings
+ rm -rf .bundle/
+
+ # Remove project-specific cached gems and repos
+ rm -rf vendor/cache/
+
+ # Remove the saved resolve of the Gemfile
+ rm -rf Gemfile.lock
+
+ # Uninstall the rubygems-bundler and open_gem gems
+ rvm gemset use global # if using rvm
+ gem uninstall rubygems-bundler open_gem
+
+ # Try to install one more time
+ bundle install
diff --git a/doc/contributing/BUG_TRIAGE.md b/doc/contributing/BUG_TRIAGE.md
new file mode 100644
index 0000000000..6667805ac1
--- /dev/null
+++ b/doc/contributing/BUG_TRIAGE.md
@@ -0,0 +1,36 @@
+# Bug triage
+
+Triaging is the work of processing tickets that have been opened by users. Common tasks include verifying bugs, categorizing tickets, and ensuring there's enough information to reproduce the bug for anyone who wants to try to fix it.
+
+We've created an [issues guide](ISSUES.md) to walk users through the process of how to report an issue with the Bundler project. We also have a [troubleshooting guide](../TROUBLESHOOTING.md) to diagnose common problems.
+
+Not every ticket will be a bug in Bundler's code, but open tickets usually mean that the there is something we could improve to help that user. Sometimes that means writing additional documentation or making error messages clearer.
+
+## Triaging existing issues
+
+When you're looking at a ticket, here are the main questions to ask:
+
+ * Can I reproduce this bug myself?
+ * Are the steps to reproduce the bug clearly documented in the ticket?
+ * Which versions of Bundler (1.1.x, 1.2.x, git, etc.) manifest this bug?
+ * Which operating systems (OS X, Windows, Ubuntu, CentOS, etc.) manifest this bug?
+ * Which rubies (MRI, JRuby, Rubinius, etc.) and which versions (1.8.7, 1.9.3, etc.) have this bug?
+
+If you can't reproduce the issue, chances are good that the bug has been fixed already (hurrah!). That's a good time to post to the ticket explaining what you did and how it worked.
+
+If you can reproduce an issue, you're well on your way to fixing it. :)
+
+## Fixing your triaged bug
+
+Everyone is welcome and encouraged to fix any open bug, improve an error message or add documentation. If you have a fix or an improvement to a ticket that you would like to contribute, we have a small guide to help:
+
+ 1. Discuss the fix on the existing issue. Coordinating with everyone else saves duplicate work and serves as a great way to get suggestions and ideas if you need any.
+ 2. Base your commits on the correct branch. Bugfixes for 1.x versions of Bundler should be based on the matching 1-x-stable branch.
+ 3. Review the [pull request guide](../development/PULL_REQUESTS.md).
+ 4. Commit the code with at least one test covering your changes to a named branch in your fork.
+ 5. Put a line in the [CHANGELOG](../../CHANGELOG.md) summarizing your changes under the next release under the “Bugfixes” heading.
+ 6. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your bugfix branch.
+
+## Duplicates!
+
+Finally, the ticket may be a duplicate of another older ticket. If you notice a ticket is a duplicate, simply comment on the ticket noting the original ticket’s number. For example, you could say “This is a duplicate of issue #42, and can be closed”.
diff --git a/doc/contributing/COMMUNITY.md b/doc/contributing/COMMUNITY.md
new file mode 100644
index 0000000000..c25ef63eeb
--- /dev/null
+++ b/doc/contributing/COMMUNITY.md
@@ -0,0 +1,13 @@
+# Community
+
+Community is an important part of all we do. If you’d like to be part of the Bundler community, you can jump right in and start helping make Bundler better for everyone who uses it.
+
+It would be tremendously helpful to have more people answering questions about Bundler (and often simply about [Rubygems](https://github.com/rubygems/rubygems) or Ruby itself) in our [issue tracker](https://github.com/bundler/bundler/issues) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/bundler).
+
+Additional documentation and explanation is always helpful, too. If you have any suggestions for the Bundler website [bundler.io](http://bundler.io), we would absolutely love it if you opened an issue or pull request on the [bundler-site](https://github.com/bundler/bundler-site) repository.
+
+Sharing your experiences and discoveries by writing them up is a valuable way to help others who have similar problems or experiences in the future. You can write a blog post, create an example and commit it to Github, take screenshots, or make videos.
+
+Publishing examples of how Bundler is used helps everyone, and we’ve discovered that people already use it in ways that we never imagined when we were writing it. If you’re still not sure what to write about, there are also several projects doing interesting things based on Bundler. They could probably use publicity too.
+
+Finally, all contributors to the Bundler project must agree to the contributor [code of conduct](http://bundler.io/conduct.html). By participating in this project you agree to abide by its terms.
diff --git a/doc/contributing/GETTING_HELP.md b/doc/contributing/GETTING_HELP.md
new file mode 100644
index 0000000000..55ccb5f5e9
--- /dev/null
+++ b/doc/contributing/GETTING_HELP.md
@@ -0,0 +1,11 @@
+# Getting help
+
+If you have any questions after reading the documentation for contributing, please feel free to contact either [@indirect](https://github.com/indirect), [@segiddins](https://github.com/segiddins), or [@RochesterinNYC](https://github.com/RochesterinNYC). They are all happy to provide help working through your first bug fix or thinking through the problem you’re trying to resolve.
+
+The best ways to get in touch are:
+
+* [Bundler Slack](https://bundler.slack.com).
+ * Not a member of the Slack? Join the Bundler team slack [here](http://slack.bundler.io/)!
+* [Bundler mailing list](http://groups.google.com/group/ruby-bundler)
+
+You may also find our guide on [filing issues](ISSUES.md) to be helpful as well!
diff --git a/doc/contributing/HOW_YOU_CAN_HELP.md b/doc/contributing/HOW_YOU_CAN_HELP.md
new file mode 100644
index 0000000000..b1e90de506
--- /dev/null
+++ b/doc/contributing/HOW_YOU_CAN_HELP.md
@@ -0,0 +1,27 @@
+# How you can help: your first commits!
+
+If you’re interested in contributing to Bundler, that’s awesome! We’d love your help.
+
+If at any point you get stuck, here's how to [get in touch with the Bundler team for help](GETTING_HELP.md).
+
+## First contribution suggestions
+
+We track [small bugs and features](https://github.com/bundler/bundler/labels/contribution%3A%20small) so that anyone who wants to help can start with something that's not too overwhelming.
+
+Generally, great ways to get started helping out with Bundler are:
+
+ - using prerelease versions (run `gem install bundler --pre`)
+ - [reporting bugs you encounter or suggesting new features](https://github.com/bundler/bundler/issues/new)
+ - see our [issues guide](ISSUES.md) for help on filing issues
+ - see the [new features documentation](../development/NEW_FEATURES.md) for more
+ - adding to or editing [the Bundler documentation website](http://bundler.io) and [Bundler man pages](http://bundler.io/man/bundle.1.html)
+ - [checking issues for completeness](BUG_TRIAGE.md)
+ - closing issues that are not complete
+ - adding a failing test for reproducible [reported bugs](https://github.com/bundler/bundler/issues)
+ - reviewing [pull requests](https://github.com/bundler/bundler/pulls) and suggesting improvements
+ - improving existing code, including suggestions from [PullReview](https://www.pullreview.com/github/bundler/bundler/reviews/master) or [CodeClimate](https://codeclimate.com/github/bundler/bundler)
+ - writing code (no patch is too small! fix typos or bad whitespace)
+ - get started setting up your dev environment with [these instructions](../development/DEVELOPMENT_SETUP.md)
+ - backfilling [unit tests](https://github.com/bundler/bundler/tree/master/spec/bundler) for modules that lack [coverage](https://codeclimate.com/github/bundler/bundler/coverage)
+
+If nothing on those lists looks good, [talk to us](http://slack.bundler.io/), and we'll figure out what you can help with. We can absolutely use your help, no matter what level of programming skill you have at the moment.
diff --git a/ISSUES.md b/doc/contributing/ISSUES.md
index e0bc56cafc..df4fa67e97 100644
--- a/ISSUES.md
+++ b/doc/contributing/ISSUES.md
@@ -1,63 +1,20 @@
-# Bundler Issues
+# Filing Issues: a guide
So! You're having problems with Bundler. This file is here to help. If you're running into an error, try reading the rest of this file for help. If you can't figure out how to solve your problem, there are also instructions on how to report a bug.
+Before filing an issue, check our [troubleshooting guide](../TROUBLESHOOTING.md) for quick fixes to common issues.
+
## Documentation
Instructions for common Bundler uses can be found on the [Bundler documentation site](http://bundler.io/).
Detailed information about each Bundler command, including help with common problems, can be found in the [Bundler man pages](http://bundler.io/man/bundle.1.html) or [Bundler Command Line Reference](http://bundler.io/v1.11/commands.html).
-## Troubleshooting
-
-### Heroku errors
-
-Please open a ticket with [Heroku](https://www.heroku.com) if you're having trouble deploying. They have a professional support team who can help you resolve Heroku issues far better than the Bundler team can. If the problem that you are having turns out to be a bug in Bundler itself, [Heroku support](https://www.heroku.com/support) can get the exact details to us.
-
-### Other problems
-
-First, figure out exactly what it is that you're trying to do (see [XY Problem](http://xyproblem.info/)). Then, go to the [Bundler documentation website](http://bundler.io) and see if we have instructions on how to do that.
-
-Second, check [the compatibility
-list](http://bundler.io/compatibility.html), and make sure that the version of Bundler that you are
-using works with the versions of Ruby and Rubygems that you are using. To see your versions:
-
- # Bundler version
- bundle -v
-
- # Ruby version
- ruby -v
-
- # Rubygems version
- gem -v
-
-If these instructions don't work, or you can't find any appropriate instructions, you can try these troubleshooting steps:
-
- # Remove user-specific gems and git repos
- rm -rf ~/.bundle/ ~/.gem/bundler/ ~/.gems/cache/bundler/
-
- # Remove system-wide git repos and git checkouts
- rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/
-
- # Remove project-specific settings
- rm -rf .bundle/
-
- # Remove project-specific cached gems and repos
- rm -rf vendor/cache/
-
- # Remove the saved resolve of the Gemfile
- rm -rf Gemfile.lock
-
- # Uninstall the rubygems-bundler and open_gem gems
- rvm gemset use global # if using rvm
- gem uninstall rubygems-bundler open_gem
-
- # Try to install one more time
- bundle install
-
## Reporting unresolved problems
-Hopefully the troubleshooting steps above resolved your problem. If things still aren't working the way you expect them to, please let us know so that we can diagnose and hopefully fix the problem you're having.
+Check our [troubleshooting common issues guide](../TROUBLESHOOTING.md) and see if your issues is resolved using the steps provided.
+
+Hopefully the troubleshooting steps above resolved your problem! If things still aren't working the way you expect them to, please let us know so that we can diagnose and hopefully fix the problem you're having.
**The best way to report a bug is by providing a reproduction script.** See these examples:
@@ -87,12 +44,6 @@ If your version of Bundler does not have the `bundle env` command, then please i
- Whether you have the `rubygems-bundler` gem, which can break gem executables (run `gem list rubygems-bundler`)
- Whether you have the `open_gem` gem, which can cause rake activation conflicts (run `gem list open_gem`)
-If you are using Rails 2.3, please also include:
-
- - Your `boot.rb` file
- - Your `preinitializer.rb` file
- - Your `environment.rb` file
-
If you have either `rubygems-bundler` or `open_gem` installed, please try removing them and then following the troubleshooting steps above before opening a new ticket.
[Create a gist](https://gist.github.com) containing all of that information, then visit the [Bundler issue tracker](https://github.com/bundler/bundler/issues) and [create a ticket](https://github.com/bundler/bundler/issues/new) describing your problem and linking to your gist.
diff --git a/doc/contributing/README.md b/doc/contributing/README.md
new file mode 100644
index 0000000000..3b04aabcdc
--- /dev/null
+++ b/doc/contributing/README.md
@@ -0,0 +1,38 @@
+# Contributing to Bundler
+
+Welcome to Bundler! We are so happy that you're here. We know it can be daunting to joining a new open source project, so here's a quick overview of what you can expect from this documentation.
+
+*Something missing?* Send us a [pull request](../development/PULL_REQUESTS.md)!
+
+**Recommended first steps**
+
+- Join us in the Bundler slack! Generate an invite [here](http://slack.bundler.io/)
+- *If you're interested in helping with code:*
+ - Get a quick overview of our [development process](../development/README.md)
+ - [Setup your machine for development](../development/SETUP.md)
+ - Checkout [How you can help: your first contributions!](HOW_YOU_CAN_HELP.md) for a list of suggestions to get started
+- *If you're interested in helping with documentation:*
+ - Read up on [how documentation works for Bundler](../documentation/README.md)
+ - Learn about our [documentation vision](../documentation/VISION.md)
+
+You can start learning about Bundler by reading [the documentation](http://bundler.io). If you want, you can also read a (lengthy) explanation of [why Bundler exists and what it does](http://bundler.io/rationale.html).
+
+##[How you can help: your first contributions!](HOW_YOU_CAN_HELP.md)
+
+A detailed overview of how to get started contributing to Bundler, including a long list of suggestions for your first project.
+
+##[Bug triage](BUG_TRIAGE.md)
+
+Want to take a stab at processing issues? Start here.
+
+##[Getting help](GETTING_HELP.md)
+
+How to get in touch with folks who can help when you're stuck. Don't worry! This happens to all of us. We're really nice, we promise.
+
+##[Filing issues](ISSUES.md)
+
+We see a lot of issues in the Bundler repo! Use this guide to file informative, actionable issues.
+
+##[Community](COMMUNITY.md)
+
+Learn more about our goals for the Bundler community and the ways you can help us build better together.
diff --git a/doc/development/NEW_FEATURES.md b/doc/development/NEW_FEATURES.md
new file mode 100644
index 0000000000..9688ef8a42
--- /dev/null
+++ b/doc/development/NEW_FEATURES.md
@@ -0,0 +1,10 @@
+# Adding New Features
+
+If you would like to add a new feature to Bundler, please follow these steps:
+
+ 1. [Create an issue](https://github.com/bundler/bundler/issues/new) with the [`feature-request` label](https://github.com/bundler/bundler/labels/type:%20feature-request) to discuss your feature.
+ 2. Base your commits on the master branch, since we follow [SemVer](http://semver.org) and don't add new features to old releases.
+ 3. Commit the code and at least one test covering your changes to a feature branch in your fork.
+ 4. Send us a [pull request](PULL_REQUESTS.md) from your feature branch.
+
+If you don't hear back immediately, don’t get discouraged! We all have day jobs, but we respond to most tickets within a day or two.
diff --git a/doc/development/PULL_REQUESTS.md b/doc/development/PULL_REQUESTS.md
new file mode 100644
index 0000000000..37129f8ecb
--- /dev/null
+++ b/doc/development/PULL_REQUESTS.md
@@ -0,0 +1,40 @@
+# Submitting Pull Requests
+
+Before you submit a pull request, please remember to do the following:
+
+1. Check your code format and style
+2. Run the test suite
+3. Use a meaningful commit message without tags
+
+## Code formatting
+
+Make sure the code formatting and styling adheres to the guidelines. We use Rubocop for this. Lack of formatting adherence will result in automatic Travis build failures.
+
+ $ bin/rubocop -a
+
+## Tests
+
+Prior to submitting your PR, please run the test suite:
+
+ $ bin/rspec
+
+If you are unable to run the entire test suite, please run the unit test suite and at least the integration specs related to the command or domain of Bundler that your code changes relate to.
+
+Ex. For a pull request that changes something with `bundle update`, you might run:
+
+ $ bin/rspec spec/bundler
+ $ bin/rspec spec/commands/update_spec.rb
+
+## Commit messages
+
+Please ensure that the commit messages included in the pull request __do not__ have the following:
+ - `@tag` Github user or team references (ex. `@indirect` or `@bundler/core`)
+ - `#id` references to issues or pull requests (ex. `#43` or `bundler/bundler-site#12`)
+
+If you want to use these mechanisms, please instead include them in the pull request description. This prevents multiple notifications or references being created on commit rebases or pull request/branch force pushes.
+
+Additionally, do not use `[ci skip]` or `[skip ci]` mechanisms in your pull request titles/descriptions or commit messages. Every potential commit and pull request should run through Bundler's CI system. This applies to all changes/commits (ex. even a change to just documentation or the removal of a comment).
+
+## CHANGELOG.md
+
+Don't forget to add your changes into the CHANGELOG! If you're submitting documentation, note the changes under the "Documentation" heading.
diff --git a/doc/development/README.md b/doc/development/README.md
new file mode 100644
index 0000000000..54cb4a3342
--- /dev/null
+++ b/doc/development/README.md
@@ -0,0 +1,19 @@
+# Development
+
+So, you're ready to start contributing to Bundler! You've come to the right place. Here you'll find an overview of how to work on Bundler locally and a description of the process from code to release.
+
+##[Development setup](SETUP.md)
+
+Guidelines for setting up your local development environment.
+
+##[Submitting pull requests](PULL_REQUESTS.md)
+
+An overview of our preferred PR process, including how to run the test suite and what to expect when you submit code for review.
+
+##[Adding new features](NEW_FEATURES.md)
+
+Guidelines for proposing and writing new features for Bundler.
+
+##[Releasing Bundler](RELEASING.md)
+
+A broad-strokes overview of the release process adhered to by the Bundler core team.
diff --git a/doc/development/RELEASING.md b/doc/development/RELEASING.md
new file mode 100644
index 0000000000..eb496470dc
--- /dev/null
+++ b/doc/development/RELEASING.md
@@ -0,0 +1,9 @@
+# Releasing
+
+_Release process documentation is in progress, see [PR 5252](https://github.com/bundler/bundler/pull/5252)._
+
+## Beta testing
+
+Early releases require heavy testing, especially across various system setups. We :heart: testers, and are big fans of anyone who can run `gem install bundler --pre` and try out upcoming releases in their development and staging environments.
+
+There may not always be prereleases or beta versions of Bundler. The Bundler team will tweet from the [@bundlerio account](http://twitter.com/bundlerio) when a prerelease or beta version becomes available. You are also always welcome to try checking out master and building a gem yourself if you want to try out the latest changes.
diff --git a/doc/development/SETUP.md b/doc/development/SETUP.md
new file mode 100644
index 0000000000..10f8dd30f1
--- /dev/null
+++ b/doc/development/SETUP.md
@@ -0,0 +1,29 @@
+# Development setup
+
+Bundler doesn't use a Gemfile to list development dependencies, because when we tried it we couldn't tell if we were awake or it was just another level of dreams. To work on Bundler, you'll probably want to do a couple of things.
+
+1. Install `groff-base` and `graphviz` packages using your package manager, e.g for ubuntu
+
+ $ sudo apt-get install graphviz groff-base -y
+
+ and for OS X (with brew installed)
+
+ $ brew install graphviz homebrew/dupes/groff
+
+2. Install Bundler's development dependencies
+
+ $ bin/rake spec:deps
+
+3. Run the test suite, to make sure things are working
+
+ $ bin/rake spec
+
+4. Set up a shell alias to run Bundler from your clone, e.g. a Bash alias:
+
+ $ alias dbundle='BUNDLE_TRAMPOLINE_DISABLE=1 ruby -I /path/to/bundler/lib /path/to/bundler/exe/bundle'
+
+ The `BUNDLE_TRAMPOLINE_DISABLE` environment variable ensures that the version of Bundler in `/path/to/bundler/lib` will be used. Without that environment setting, Bundler will automatically download, install, and run the version of Bundler listed in `Gemfile.lock`. With that set up, you can test changes you've made to Bundler by running `dbundle`, without interfering with the regular `bundle` command.
+
+## Debugging with `pry`
+
+To dive into the code with Pry: `RUBYOPT=-rpry dbundle` to require pry and then run commands.
diff --git a/doc/documentation/README.md b/doc/documentation/README.md
new file mode 100644
index 0000000000..7ebb05401c
--- /dev/null
+++ b/doc/documentation/README.md
@@ -0,0 +1,29 @@
+# Documentation
+
+Code needs explanation, and sometimes those who know the code well have trouble explaining it to someone just getting into it. Because of that, we welcome documentation suggestions and patches from everyone, especially if they are brand new to using Bundler.
+
+Currently, Bundler has two main sources of documentation:
+
+1. built-in `help` (including usage information and man pages)
+2. [Bundler documentation site](http://bundler.io)
+
+If you have a suggestion or proposed change for [bundler.io](http://bundler.io), please open an issue or send a pull request to the [bundler-site](https://github.com/bundler/bundler-site) repository.
+
+Not sure where to write documentation? In general, follow these guidelines:
+
+* For an explanation of a specific Bundler command (ex: `bundle clean`): make changes to the man pages
+* For longer explanations or usage guides (ex: "Using Bundler with Rails"): create new documentation within the [bundler-site](https://github.com/bundler/bundler-site) repository
+
+If you are unsure where to begin, ping [@feministy](https://github.com/feministy) or [@indirect](https://github.com/indirect) in the Bundler Slack ([get an invite here](../contributing/GETTING_HELP.md)).
+
+## [Writing docs for man pages)](WRITING.md)
+
+If you’d like to submit a patch to the man pages, you're in the right place! Details on editing and previewing changes to man pages can be found here.
+
+## [Documentation vision](VISION.md)
+
+Just like Bundler, we have a grand plan (really, a wish list of sorts) for Bundler documentation. Preview our hopes and dreams for our documentation here.
+
+## Translations
+
+We don't currently have any translations, but please reach out to us if you would like to help get this going.
diff --git a/doc/documentation/VISION.md b/doc/documentation/VISION.md
new file mode 100644
index 0000000000..3d660ee36d
--- /dev/null
+++ b/doc/documentation/VISION.md
@@ -0,0 +1,26 @@
+# Documentation vision
+
+Currently, documentation for using Bundler is spread across two places:
+
+1. built-in `help` (including usage information and man pages)
+2. [Bundler documentation site](http://bundler.io)
+
+Additional documentation about using Bundler to publish gems can also be found on the [RubyGems guides](http://guides.rubygems.org/).
+
+## Goals
+
+Bundler documentation should provide users with assistance:
+
+1. Installing Bundler
+2. Using Bundler to manage an application's dependencies
+3. Using Bundler to create, package, and publish gems
+
+Our goal is to provide three types of documentation:
+
+* High level overviews that provide topical guidance
+* Step-by-step tutorials
+* Command-specific reference material for the CLI
+
+Additionally, this documentation should be readily available in a logical place and easy to follow.
+
+Someday, we'd like to create deep-dive reference material about the inner workings of Bundler. However, while this is part of our overall vision for Bundler documentation, it is not the focus of our current work.
diff --git a/doc/documentation/WRITING.md b/doc/documentation/WRITING.md
new file mode 100644
index 0000000000..ce6dcce653
--- /dev/null
+++ b/doc/documentation/WRITING.md
@@ -0,0 +1,54 @@
+# Writing docs for man pages
+
+A primary source of help for Bundler users are the man pages: the output printed when you run `bundle help` (or `bundler help`). These pages can be a little tricky to format and preview, but are pretty straightforward once you get the hang of it.
+
+_Note: `bundler` and `bundle` may be used interchangeably in the CLI. This guide uses `bundle` because it's cuter._
+
+## What goes in man pages?
+
+We use man pages for Bundler commands used in the CLI (command line interface). They can vary in length from large (see `bundle install`) to very short (see `bundle clean`).
+
+To see a list of commands available in the Bundler CLI, type:
+
+ $ bundle help
+
+Our goal is to have a man page for every command.
+
+Don't see a man page for a command? Make a new page and send us a PR! We also welcome edits to existing pages.
+
+## Creating a new man page
+
+To create a new man page, simply create a new `.ronn` file in the `man/` directory.
+
+For example: to create a man page for the command `bundle cookies` (not a real command, sadly), I would create a file `man/bundle-cookies.ronn` and add my documentation there.
+
+## Formatting
+
+Our man pages use ronn formatting, a combination of Markdown and standard man page conventions. It can be a little weird getting used to it at first, especially if you've used Markdown a lot.
+
+[The ronn guide formatting guide](https://rtomayko.github.io/ronn/ronn.7.html) provides a good overview of the common types of formatting.
+
+In general, make your page look like the other pages: utilize sections like `##OPTIONS` and formatting like code blocks and definition lists where appropriate.
+
+If you're not sure if the formatting looks right, that's ok! Make a pull request with what you've got and we'll take a peek.
+
+## Previewing
+
+To preview your changes as they will print out for Bundler users, you'll need to run a series of commands:
+
+```
+$ rake spec:deps
+$ rake man:build
+$ man man/bundle-cookies.1
+```
+
+If you make more changes to `bundle-cookies.ronn`, you'll need to run the `rake man:build` again before previewing.
+
+## Testing
+
+We have tests for our documentation! The most important test file to run before you make your pull request is the one for the `help` command and another for documentation quality.
+
+```
+$ rspec ./spec/commands/help_spec.rb
+$ rspec ./spec/quality_spec.rb
+```
diff --git a/exe/bundle b/exe/bundle
index ec88ea7552..ab2bde16e2 100755
--- a/exe/bundle
+++ b/exe/bundle
@@ -2,7 +2,10 @@
# frozen_string_literal: true
# Exit cleanly from an early interrupt
-Signal.trap("INT") { exit 1 }
+Signal.trap("INT") do
+ Bundler.ui.debug("\n#{caller.join("\n")}") if defined?(Bundler)
+ exit 1
+end
update = "update".start_with?(ARGV.first || " ") && ARGV.find {|a| a.start_with?("--bundler") }
update &&= update =~ /--bundler(?:=(.+))?/ && $1 || "> 0.a"
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 8806ae01ef..63a9f5784c 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -3,9 +3,10 @@ require "fileutils"
require "pathname"
require "rbconfig"
require "thread"
+require "tmpdir"
+
require "bundler/errors"
require "bundler/environment_preserver"
-require "bundler/gem_remote_fetcher"
require "bundler/plugin"
require "bundler/rubygems_ext"
require "bundler/rubygems_integration"
@@ -30,6 +31,7 @@ module Bundler
autoload :FeatureFlag, "bundler/feature_flag"
autoload :GemHelper, "bundler/gem_helper"
autoload :GemHelpers, "bundler/gem_helpers"
+ autoload :GemRemoteFetcher, "bundler/gem_remote_fetcher"
autoload :GemVersionPromoter, "bundler/gem_version_promoter"
autoload :Graph, "bundler/graph"
autoload :Index, "bundler/index"
@@ -38,8 +40,6 @@ module Bundler
autoload :LazySpecification, "bundler/lazy_specification"
autoload :LockfileParser, "bundler/lockfile_parser"
autoload :MatchPlatform, "bundler/match_platform"
- autoload :Mirror, "bundler/mirror"
- autoload :Mirrors, "bundler/mirror"
autoload :RemoteSpecification, "bundler/remote_specification"
autoload :Resolver, "bundler/resolver"
autoload :Retry, "bundler/retry"
@@ -133,7 +133,7 @@ module Bundler
@locked_gems ||=
if defined?(@definition) && @definition
definition.locked_gems
- elsif Bundler.default_lockfile.exist?
+ elsif Bundler.default_lockfile.file?
lock = Bundler.read_file(Bundler.default_lockfile)
LockfileParser.new(lock)
end
@@ -143,8 +143,44 @@ module Bundler
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
end
+ def user_home
+ @user_home ||= begin
+ home = Bundler.rubygems.user_home
+
+ warning = if home.nil?
+ "Your home directory is not set."
+ elsif !File.directory?(home)
+ "`#{home}` is not a directory."
+ elsif !File.writable?(home)
+ "`#{home}` is not writable."
+ end
+
+ if warning
+ user_home = tmp_home_path(Etc.getlogin, warning)
+ Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n"
+ user_home
+ else
+ Pathname.new(home)
+ end
+ end
+ end
+
+ def tmp_home_path(login, warning)
+ login ||= "unknown"
+ path = Pathname.new(Dir.tmpdir).join("bundler", "home")
+ SharedHelpers.filesystem_access(path) do |tmp_home_path|
+ unless tmp_home_path.exist?
+ tmp_home_path.mkpath
+ tmp_home_path.chmod(0o777)
+ end
+ tmp_home_path.join(login).tap(&:mkpath)
+ end
+ rescue => e
+ raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}")
+ end
+
def user_bundle_path
- Pathname.new(Bundler.rubygems.user_home).join(".bundle")
+ Pathname.new(user_home).join(".bundle")
end
def home
@@ -258,6 +294,11 @@ EOF
with_clean_env { Kernel.exec(*args) }
end
+ def local_platform
+ return Gem::Platform::RUBY if settings[:force_ruby_platform]
+ Gem::Platform.local
+ end
+
def default_gemfile
SharedHelpers.default_gemfile
end
@@ -329,17 +370,23 @@ EOF
def sudo(str)
SUDO_MUTEX.synchronize do
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " "
- Your user account isn't allowed to install to the system Rubygems.
+ Your user account isn't allowed to install to the system RubyGems.
You can cancel this installation and run:
bundle install --path vendor/bundle
to install the gems into ./vendor/bundle/, or you can enter your password
- and install the bundled gems to Rubygems using sudo.
+ and install the bundled gems to RubyGems using sudo.
Password:
PROMPT
+ unless @prompted_for_sudo ||= system(%(sudo -k -p "#{prompt}" true))
+ raise SudoNotPermittedError,
+ "Bundler requires sudo access to install at the moment. " \
+ "Try installing again, granting Bundler sudo access when prompted, or installing into a different path."
+ end
+
`sudo -p "#{prompt}" #{str}`
end
end
@@ -395,6 +442,12 @@ EOF
end
def reset!
+ reset_paths!
+ Plugin.reset!
+ reset_rubygems!
+ end
+
+ def reset_paths!
@root = nil
@settings = nil
@definition = nil
@@ -403,9 +456,10 @@ EOF
@locked_gems = nil
@bundle_path = nil
@bin_path = nil
+ @user_home = nil
+ end
- Plugin.reset!
-
+ def reset_rubygems!
return unless defined?(@rubygems) && @rubygems
rubygems.undo_replacements
rubygems.reset
@@ -443,7 +497,10 @@ EOF
def configure_gem_path(env = ENV, settings = self.settings)
blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty?
if settings[:disable_shared_gems]
- env["GEM_PATH"] = nil
+ # this needs to be empty string to cause
+ # PathSupport.split_gem_path to only load up the
+ # Bundler --path setting as the GEM_PATH.
+ env["GEM_PATH"] = ""
elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
paths = possibles.flatten.compact.uniq.reject(&:empty?)
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 3bdc3db9e9..cf75c52733 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -13,6 +13,7 @@ module Bundler
Bundler.ui = UI::Shell.new
raise e
ensure
+ warn_on_outdated_bundler
Bundler::SharedHelpers.print_major_deprecations!
end
@@ -22,10 +23,12 @@ module Bundler
def initialize(*args)
super
- Bundler.reset!
custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile]
- ENV["BUNDLE_GEMFILE"] = File.expand_path(custom_gemfile) if custom_gemfile && !custom_gemfile.empty?
+ if custom_gemfile && !custom_gemfile.empty?
+ ENV["BUNDLE_GEMFILE"] = File.expand_path(custom_gemfile)
+ Bundler.reset_paths!
+ end
Bundler.settings[:retry] = options[:retry] if options[:retry]
@@ -36,8 +39,10 @@ module Bundler
ensure
self.options ||= {}
Bundler.settings.cli_flags_given = !options.empty?
+ unprinted_warnings = Bundler.ui.unprinted_warnings
Bundler.ui = UI::Shell.new(options)
Bundler.ui.level = "debug" if options["verbose"]
+ unprinted_warnings.each {|w| Bundler.ui.warn(w) }
if ENV["RUBYGEMS_GEMDEPS"] && !ENV["RUBYGEMS_GEMDEPS"].empty?
Bundler.ui.warn(
@@ -59,30 +64,21 @@ module Bundler
def help(cli = nil)
case cli
- when "gemfile" then command = "gemfile.5"
+ when "gemfile" then command = "gemfile"
when nil then command = "bundle"
else command = "bundle-#{cli}"
end
- manpages = %w(
- bundle
- bundle-config
- bundle-exec
- bundle-gem
- bundle-install
- bundle-package
- bundle-update
- bundle-platform
- gemfile.5
- )
-
- if manpages.include?(command)
- root = File.expand_path("../man", __FILE__)
-
- if Bundler.which("man") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
- Kernel.exec "man #{root}/#{command}"
+ man_path = File.expand_path("../../../man", __FILE__)
+ man_pages = Hash[Dir.glob(File.join(man_path, "*")).grep(/.*\.\d*\Z/).collect do |f|
+ [File.basename(f, ".*"), f]
+ end]
+
+ if man_pages.include?(command)
+ if Bundler.which("man") && man_path !~ %r{^file:/.+!/META-INF/jruby.home/.+}
+ Kernel.exec "man #{man_pages[command]}"
else
- puts File.read("#{root}/#{command}.txt")
+ puts File.read("#{man_path}/#{File.basename(man_pages[command])}.txt")
end
elsif command_path = Bundler.which("bundler-#{cli}")
Kernel.exec(command_path, "--help")
@@ -91,6 +87,9 @@ module Bundler
end
end
+ # Ensure `bundle help --no-color` is valid
+ all_commands["help"].disable_class_options = false
+
def self.handle_no_command_error(command, has_namespace = $thor_runner)
if Bundler.feature_flag.plugins? && Bundler::Plugin.command?(command)
return Bundler::Plugin.exec_command(command, ARGV[1..-1])
@@ -184,11 +183,9 @@ module Bundler
map "i" => "install"
def install
require "bundler/cli/install"
- no_install = Bundler.settings[:no_install]
- Bundler.settings[:no_install] = false if no_install == true
- Install.new(options.dup).run
- ensure
- Bundler.settings[:no_install] = no_install unless no_install.nil?
+ Bundler.settings.temporary(:no_install => false) do
+ Install.new(options.dup).run
+ end
end
desc "update [OPTIONS]", "update the current environment"
@@ -215,14 +212,16 @@ module Bundler
"Update ruby specified in Gemfile.lock"
method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
"Update the locked version of bundler"
- method_option "patch", :type => :boolean, :hide => true, :banner =>
+ method_option "patch", :type => :boolean, :banner =>
"Prefer updating only to next patch version"
- method_option "minor", :type => :boolean, :hide => true, :banner =>
+ method_option "minor", :type => :boolean, :banner =>
"Prefer updating only to next minor version"
- method_option "major", :type => :boolean, :hide => true, :banner =>
+ method_option "major", :type => :boolean, :banner =>
"Prefer updating to next major version (default)"
- method_option "strict", :type => :boolean, :hide => true, :banner =>
- "Do not allow any gem to be updated past latest --patch/--minor/--major"
+ method_option "strict", :type => :boolean, :banner =>
+ "Do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "conservative", :type => :boolean, :banner =>
+ "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
def update(*gems)
require "bundler/cli/update"
Update.new(options, gems).run
@@ -245,6 +244,13 @@ module Bundler
# TODO: 2.0 remove `bundle list`
map %w(list) => "show"
+ desc "info GEM [OPTIONS]", "Show information for the given gem"
+ method_option "path", :type => :boolean, :banner => "Print full path to gem"
+ def info(gem_name)
+ require "bundler/cli/info"
+ Info.new(options, gem_name).run
+ end
+
desc "binstubs GEM [OPTIONS]", "Install the binstubs of the listed gem"
long_desc <<-D
Generate binstubs for executables in [GEM]. Binstubs are put into bin,
@@ -268,16 +274,26 @@ module Bundler
in the given source. Calling outdated with [GEM [GEM]] will only check for newer
versions of the given gems. Prerelease gems are ignored by default. If your gems
are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
+
+ For more information on patch level options (--major, --minor, --patch,
+ --update-strict) see documentation on the same options on the update command.
D
+ method_option "group", :aliases => "--group", :type => :string, :banner => "List gems from a specific group"
+ method_option "groups", :aliases => "--groups", :type => :boolean, :banner => "List gems organized by groups"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
method_option "source", :type => :array, :banner => "Check against a specific source"
method_option "strict", :type => :boolean, :banner =>
"Only list newer versions allowed by your Gemfile requirements"
- method_option "major", :type => :boolean, :banner => "Only list major newer versions"
- method_option "minor", :type => :boolean, :banner => "Only list minor newer versions"
- method_option "patch", :type => :boolean, :banner => "Only list patch newer versions"
+ method_option "update-strict", :type => :boolean, :banner =>
+ "Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version"
+ method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)"
+ method_option "patch", :type => :boolean, :banner => "Prefer updating only to next patch version"
+ method_option "filter-major", :type => :boolean, :banner => "Only list major newer versions"
+ method_option "filter-minor", :type => :boolean, :banner => "Only list minor newer versions"
+ method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions"
method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner =>
"Use minimal formatting for more parseable output"
def outdated(*gems)
@@ -399,6 +415,8 @@ module Bundler
Viz.new(options.dup).run
end
+ old_gem = instance_method(:gem)
+
desc "gem GEM [OPTIONS]", "Creates a skeleton for creating a rubygem"
method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library."
method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config gem.coc true`."
@@ -410,10 +428,30 @@ module Bundler
method_option :test, :type => :string, :lazy_default => "rspec", :aliases => "-t", :banner => "rspec",
:desc => "Generate a test directory for your library, either rspec or minitest. Set a default with `bundle config gem.test rspec`."
def gem(name)
- require "bundler/cli/gem"
- Gem.new(options, name, self).run
end
+ commands["gem"].tap do |gem_command|
+ def gem_command.run(instance, args = [])
+ arity = 1 # name
+
+ require "bundler/cli/gem"
+ cmd_args = args + [instance]
+ cmd_args.unshift(instance.options)
+
+ cmd = begin
+ Gem.new(*cmd_args)
+ rescue ArgumentError => e
+ instance.class.handle_argument_error(self, e, args, arity)
+ end
+
+ cmd.run
+ end
+ end
+
+ undef_method(:gem)
+ define_method(:gem, old_gem)
+ private :gem
+
def self.source_root
File.expand_path(File.join(File.dirname(__FILE__), "templates"))
end
@@ -436,15 +474,15 @@ module Bundler
Platform.new(options).run
end
- desc "inject GEM VERSION ...", "Add the named gem(s), with version requirements, to the resolved Gemfile"
- def inject(name, version, *gems)
+ desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile"
+ def inject(name, version)
SharedHelpers.major_deprecation "The `inject` command has been replaced by the `add` command"
require "bundler/cli/inject"
- Inject.new(options, name, version, gems).run
+ Inject.new(options, name, version).run
end
desc "lock", "Creates a lockfile without installing"
- method_option "update", :type => :array, :lazy_default => [], :banner =>
+ method_option "update", :type => :array, :lazy_default => true, :banner =>
"ignore the existing lockfile, update all gems by default, or update list of given gems"
method_option "local", :type => :boolean, :default => false, :banner =>
"do not attempt to fetch remote gemspecs and use the local gem cache only"
@@ -455,9 +493,19 @@ module Bundler
method_option "full-index", :type => :boolean, :default => false, :banner =>
"Fall back to using the single-file index of all gems"
method_option "add-platform", :type => :array, :default => [], :banner =>
- "add a new platform to the lockfile"
+ "Add a new platform to the lockfile"
method_option "remove-platform", :type => :array, :default => [], :banner =>
- "remove a platform from the lockfile"
+ "Remove a platform from the lockfile"
+ method_option "patch", :type => :boolean, :banner =>
+ "If updating, prefer updating only to next patch version"
+ method_option "minor", :type => :boolean, :banner =>
+ "If updating, prefer updating only to next minor version"
+ method_option "major", :type => :boolean, :banner =>
+ "If updating, prefer updating to next major version (default)"
+ method_option "strict", :type => :boolean, :banner =>
+ "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "conservative", :type => :boolean, :banner =>
+ "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
def lock
require "bundler/cli/lock"
Lock.new(options).run
@@ -545,5 +593,26 @@ module Bundler
command.reject!(&:empty?)
Bundler.ui.info "Running `#{command * " "}` with bundler #{Bundler::VERSION}"
end
+
+ def self.warn_on_outdated_bundler
+ return if Bundler.settings[:disable_version_check]
+
+ latest = Fetcher::CompactIndex.
+ new(nil, Source::Rubygems::Remote.new(URI("https://rubygems.org")), nil).
+ send(:compact_index_client).
+ instance_variable_get(:@cache).
+ dependencies("bundler").
+ map {|d| Gem::Version.new(d.first) }.
+ max
+ return unless latest
+
+ current = Gem::Version.new(VERSION)
+ return if current >= latest
+
+ Bundler.ui.warn "The latest bundler is #{latest}, but you are currently running #{current}.\nTo update, run `gem install bundler#{" --pre" if latest.prerelease?}`"
+ rescue
+ nil
+ end
+ private_class_method :warn_on_outdated_bundler
end
end
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index 3f504ff621..057a7e5695 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -28,7 +28,7 @@ module Bundler
not_installed.each {|s| Bundler.ui.error " * #{s.name} (#{s.version})" }
Bundler.ui.warn "Install missing gems with `bundle install`"
exit 1
- elsif !Bundler.default_lockfile.exist? && Bundler.settings[:frozen]
+ elsif !Bundler.default_lockfile.file? && Bundler.settings[:frozen]
Bundler.ui.error "This bundle has been frozen, but there is no #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} present"
exit 1
else
diff --git a/lib/bundler/cli/clean.rb b/lib/bundler/cli/clean.rb
index 895b6567fa..5eba09c6bc 100644
--- a/lib/bundler/cli/clean.rb
+++ b/lib/bundler/cli/clean.rb
@@ -8,7 +8,7 @@ module Bundler
end
def run
- require_path_or_force
+ require_path_or_force unless options[:"dry-run"]
Bundler.load.clean(options[:"dry-run"])
end
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index 491ea04a11..c1e108d752 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -1,6 +1,23 @@
# frozen_string_literal: true
module Bundler
module CLI::Common
+ def self.output_post_install_messages(messages)
+ return if Bundler.settings["ignore_messages"]
+ messages.to_a.each do |name, msg|
+ print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"]
+ end
+ end
+
+ def self.print_post_install_message(name, msg)
+ Bundler.ui.confirm "Post-install message from #{name}:"
+ Bundler.ui.info msg
+ end
+
+ def self.output_without_groups_message
+ return unless Bundler.settings.without.any?
+ Bundler.ui.confirm without_groups_message
+ end
+
def self.without_groups_message
groups = Bundler.settings.without
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
@@ -52,5 +69,18 @@ module Bundler
message += "\nDid you mean #{suggestions}?" if suggestions
message
end
+
+ def self.configure_gem_version_promoter(definition, options)
+ patch_level = patch_level_options(options)
+ raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1
+ definition.gem_version_promoter.tap do |gvp|
+ gvp.level = patch_level.first || :major
+ gvp.strict = options[:strict] || options["update-strict"]
+ end
+ end
+
+ def self.patch_level_options(options)
+ [:major, :minor, :patch].select {|v| options.keys.include?(v.to_s) }
+ end
end
end
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
index 728662024b..ae27983240 100644
--- a/lib/bundler/cli/doctor.rb
+++ b/lib/bundler/cli/doctor.rb
@@ -14,11 +14,11 @@ module Bundler
end
def otool_available?
- system("otool --version 2>#{Bundler::NULL} >#{Bundler::NULL}")
+ Bundler.which("otool")
end
def ldd_available?
- !system("ldd --help 2>#{Bundler::NULL} >#{Bundler::NULL}").nil?
+ Bundler.which("ldd")
end
def dylibs_darwin(path)
diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb
index 4f238bbb59..62f7bc26cb 100644
--- a/lib/bundler/cli/exec.rb
+++ b/lib/bundler/cli/exec.rb
@@ -91,6 +91,12 @@ module Bundler
"#!/usr/bin/env jruby\n",
"#!#{Gem.ruby}\n",
]
+
+ if File.zero?(file)
+ Bundler.ui.warn "#{file} is empty"
+ return false
+ end
+
first_line = File.open(file, "rb") {|f| f.read(possibilities.map(&:size).max) }
possibilities.any? {|shebang| first_line.start_with?(shebang) }
end
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 4dc0dbdb6b..7fa005289e 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -29,8 +29,11 @@ module Bundler
constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase }
constant_array = constant_name.split("::")
- git_user_name = `git config user.name`.chomp
- git_user_email = `git config user.email`.chomp
+ git_installed = Bundler.git_present?
+
+ git_author_name = git_installed ? `git config user.name`.chomp : ""
+ github_username = git_installed ? `git config github.user`.chomp : ""
+ git_user_email = git_installed ? `git config user.email`.chomp : ""
config = {
:name => name,
@@ -39,19 +42,18 @@ module Bundler
:makefile_path => "#{underscored_name}/#{underscored_name}",
:constant_name => constant_name,
:constant_array => constant_array,
- :author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
+ :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
:email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
:test => options[:test],
:ext => options[:ext],
:exe => options[:exe],
:bundler_version => bundler_dependency_version,
- :git_user_name => git_user_name.empty? ? "[USERNAME]" : git_user_name
+ :github_username => github_username.empty? ? "[USERNAME]" : github_username
}
ensure_safe_gem_name(name, constant_array)
templates = {
"Gemfile.tt" => "Gemfile",
- "gitignore.tt" => ".gitignore",
"lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
"lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
"newgem.gemspec.tt" => "#{name}.gemspec",
@@ -66,6 +68,8 @@ module Bundler
bin/setup
)
+ templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present?
+
if test_framework = ask_and_set_test_framework
config[:test] = test_framework
config[:test_framework_version] = TEST_FRAMEWORK_VERSIONS[test_framework]
@@ -122,7 +126,10 @@ module Bundler
end
templates.each do |src, dst|
- thor.template("newgem/#{src}", target.join(dst), config)
+ destination = target.join(dst)
+ SharedHelpers.filesystem_access(destination) do
+ thor.template("newgem/#{src}", destination, config)
+ end
end
executables.each do |file|
@@ -131,14 +138,18 @@ module Bundler
path.chmod(executable)
end
- Bundler.ui.info "Initializing git repo in #{target}"
- Dir.chdir(target) do
- `git init`
- `git add .`
+ if Bundler.git_present?
+ Bundler.ui.info "Initializing git repo in #{target}"
+ Dir.chdir(target) do
+ `git init`
+ `git add .`
+ end
end
# Open gemspec in editor
open_editor(options["edit"], target.join("#{name}.gemspec")) if options[:edit]
+ rescue Errno::EEXIST => e
+ raise GenericSystemCallError.new(e, "There was a conflict while creating the new gem.")
end
private
@@ -202,10 +213,23 @@ module Bundler
if name =~ /^\d/
Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
exit 1
- elsif constant_array.inject(Object) {|c, s| (c.const_defined?(s) && c.const_get(s)) || break }
- Bundler.ui.error "Invalid gem name #{name} constant #{constant_array.join("::")} is already in use. Please choose another gem name."
- exit 1
end
+
+ constant_name = constant_array.join("::")
+
+ existing_constant = constant_array.inject(Object) do |c, s|
+ defined = begin
+ c.const_defined?(s)
+ rescue NameError
+ Bundler.ui.error "Invalid gem name #{name} -- `#{constant_name}` is an invalid constant name"
+ exit 1
+ end
+ (defined && c.const_get(s)) || break
+ end
+
+ return unless existing_constant
+ Bundler.ui.error "Invalid gem name #{name} constant #{constant_name} is already in use. Please choose another gem name."
+ exit 1
end
def open_editor(editor, file)
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
new file mode 100644
index 0000000000..4465fba9d4
--- /dev/null
+++ b/lib/bundler/cli/info.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+require "bundler/cli/common"
+
+module Bundler
+ class CLI::Info
+ attr_reader :gem_name, :options
+ def initialize(options, gem_name)
+ @options = options
+ @gem_name = gem_name
+ end
+
+ def run
+ spec = spec_for_gem(gem_name)
+
+ spec_not_found(gem_name) unless spec
+ return print_gem_path(spec) if @options[:path]
+ print_gem_info(spec)
+ end
+
+ private
+
+ def spec_for_gem(gem_name)
+ spec = Bundler.definition.specs.find {|s| s.name == gem_name }
+ spec || default_gem_spec(gem_name)
+ end
+
+ def default_gem_spec(gem_name)
+ return unless Gem::Specification.respond_to?(:find_all_by_name)
+ gem_spec = Gem::Specification.find_all_by_name(gem_name).last
+ return gem_spec if gem_spec && gem_spec.respond_to?(:default_gem?) && gem_spec.default_gem?
+ end
+
+ def spec_not_found(gem_name)
+ raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies)
+ end
+
+ def print_gem_path(spec)
+ Bundler.ui.info spec.full_gem_path
+ end
+
+ def print_gem_info(spec)
+ gem_info = String.new
+ gem_info << " * #{spec.name} (#{spec.version}#{spec.git_version})\n"
+ gem_info << "\tSummary: #{spec.summary}\n" if spec.summary
+ gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage
+ gem_info << "\tPath: #{spec.full_gem_path}\n"
+ gem_info << "\tDefault Gem: yes" if spec.respond_to?(:default_gem?) && spec.default_gem?
+ Bundler.ui.info gem_info
+ end
+ end
+end
diff --git a/lib/bundler/cli/inject.rb b/lib/bundler/cli/inject.rb
index 9d1d08120a..cf35e4985b 100644
--- a/lib/bundler/cli/inject.rb
+++ b/lib/bundler/cli/inject.rb
@@ -2,13 +2,13 @@
module Bundler
class CLI::Inject
attr_reader :options, :name, :version, :group, :source, :gems
- def initialize(options, name, version, gems)
+ def initialize(options, name, version)
@options = options
@name = name
@version = version || last_version_number
@group = options[:group]
@source = options[:source]
- @gems = gems
+ @gems = []
end
def run
@@ -18,6 +18,8 @@ module Bundler
# Build an array of Dependency objects out of the arguments
deps = []
+ # when `inject` support addition of more than one gem, then this loop will
+ # help. Currently this loop is running once.
gems.each_slice(4) do |gem_name, gem_version, gem_group, gem_source|
ops = Gem::Requirement::OPS.map {|key, _val| key }
has_op = ops.any? {|op| gem_version.start_with? op }
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index f1632c9ee2..752c5e9c55 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -1,4 +1,6 @@
# frozen_string_literal: true
+require "bundler/cli/common"
+
module Bundler
class CLI::Install
attr_reader :options
@@ -57,12 +59,9 @@ module Bundler
if options["binstubs"]
Bundler::SharedHelpers.major_deprecation \
- "the --binstubs option will be removed in favor of `bundle binstubs`"
+ "The --binstubs option will be removed in favor of `bundle binstubs`"
end
- # rubygems plugins sometimes hook into the gem install process
- Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
-
Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
definition = Bundler.definition
@@ -72,21 +71,17 @@ module Bundler
Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.settings[:frozen]
Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}."
- confirm_without_groups
+ Bundler::CLI::Common.output_without_groups_message
if Bundler.settings[:path]
absolute_path = File.expand_path(Bundler.settings[:path])
relative_path = absolute_path.sub(File.expand_path(".") + File::SEPARATOR, "." + File::SEPARATOR)
Bundler.ui.confirm "Bundled gems are installed into #{relative_path}."
else
- Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed."
+ Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed."
end
- unless Bundler.settings["ignore_messages"]
- installer.post_install_messages.to_a.each do |name, msg|
- print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"]
- end
- end
+ Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
warn_ambiguous_gems
@@ -135,12 +130,6 @@ module Bundler
end
end
- def confirm_without_groups
- return unless Bundler.settings.without.any?
- require "bundler/cli/common"
- Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
- end
-
def dependencies_count_for(definition)
count = definition.dependencies.count
"#{count} Gemfile #{count == 1 ? "dependency" : "dependencies"}"
@@ -151,11 +140,6 @@ module Bundler
"#{count} #{count == 1 ? "gem" : "gems"} now installed"
end
- def print_post_install_message(name, msg)
- Bundler.ui.confirm "Post-install message from #{name}:"
- Bundler.ui.info msg
- end
-
def check_for_group_conflicts
if options[:without] && options[:with]
conflicting_groups = options[:without] & options[:with]
@@ -170,8 +154,8 @@ module Bundler
def check_for_options_conflicts
if (options[:path] || options[:deployment]) && options[:system]
error_message = String.new
- error_message << "You have specified both a path to install your gems to as well as --system. Please choose.\n" if options[:path]
- error_message << "You have specified both --deployment as well as --system. Please choose.\n" if options[:deployment]
+ error_message << "You have specified both --path as well as --system. Please choose only one option.\n" if options[:path]
+ error_message << "You have specified both --deployment as well as --system. Please choose only one option.\n" if options[:deployment]
raise InvalidOption.new(error_message)
end
end
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index a6a95f895c..beeb2e4633 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -1,4 +1,6 @@
# frozen_string_literal: true
+require "bundler/cli/common"
+
module Bundler
class CLI::Lock
attr_reader :options
@@ -17,14 +19,13 @@ module Bundler
ui = Bundler.ui
Bundler.ui = UI::Silent.new if print
- gems = options[:update]
Bundler::Fetcher.disable_endpoint = options["full-index"]
- if gems && !gems.empty?
- definition = Bundler.definition(:gems => gems)
- else
- definition = Bundler.definition(true)
- end
+ update = options[:update]
+ update = { :gems => update, :lock_shared_dependencies => options[:conservative] } if update.is_a?(Array)
+ definition = Bundler.definition(update)
+
+ Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) if options[:update]
options["remove-platform"].each do |platform|
definition.remove_platform(platform)
@@ -32,7 +33,7 @@ module Bundler
options["add-platform"].each do |platform_string|
platform = Gem::Platform.new(platform_string)
- if platform.to_a.compact == %w(unknown)
+ if platform.to_s == "unknown"
Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
"and adding it will likely lead to resolution errors"
end
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
index 0bbfa63d34..9a21f6811c 100644
--- a/lib/bundler/cli/open.rb
+++ b/lib/bundler/cli/open.rb
@@ -13,7 +13,8 @@ module Bundler
def run
editor = [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
- path = Bundler::CLI::Common.select_spec(name, :regex_match).full_gem_path
+ return unless spec = Bundler::CLI::Common.select_spec(name, :regex_match)
+ path = spec.full_gem_path
Dir.chdir(path) do
command = Shellwords.split(editor) + [path]
Bundler.with_clean_env do
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index de71075522..93e50b10b6 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -4,6 +4,7 @@ require "bundler/cli/common"
module Bundler
class CLI::Outdated
attr_reader :options, :gems
+
def initialize(options, gems)
@options = options
@gems = gems
@@ -21,7 +22,11 @@ module Bundler
Bundler.definition.validate_runtime!
current_specs = Bundler.ui.silence { Bundler.load.specs }
current_dependencies = {}
- Bundler.ui.silence { Bundler.load.dependencies.each {|dep| current_dependencies[dep.name] = dep } }
+ Bundler.ui.silence do
+ Bundler.load.dependencies.each do |dep|
+ current_dependencies[dep.name] = dep
+ end
+ end
definition = if gems.empty? && sources.empty?
# We're doing a full update
@@ -30,7 +35,23 @@ module Bundler
Bundler.definition(:gems => gems, :sources => sources)
end
- definition_resolution = proc { options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely! }
+ Bundler::CLI::Common.configure_gem_version_promoter(
+ Bundler.definition,
+ options
+ )
+
+ # the patch level options imply strict is also true. It wouldn't make
+ # sense otherwise.
+ strict = options[:strict] ||
+ Bundler::CLI::Common.patch_level_options(options).any?
+
+ filter_options_patch = options.keys &
+ %w(filter-major filter-minor filter-patch)
+
+ definition_resolution = proc do
+ options[:local] ? definition.resolve_with_cache! : definition.resolve_remotely!
+ end
+
if options[:parseable]
Bundler.ui.silence(&definition_resolution)
else
@@ -38,83 +59,169 @@ module Bundler
end
Bundler.ui.info ""
+ outdated_gems_by_groups = {}
+ outdated_gems_list = []
- out_count = 0
# Loop through the current specs
- gemfile_specs, dependency_specs = current_specs.partition {|spec| current_dependencies.key? spec.name }
- [gemfile_specs.sort_by(&:name), dependency_specs.sort_by(&:name)].flatten.each do |current_spec|
+ gemfile_specs, dependency_specs = current_specs.partition do |spec|
+ current_dependencies.key? spec.name
+ end
+
+ (gemfile_specs + dependency_specs).sort_by(&:name).each do |current_spec|
next if !gems.empty? && !gems.include?(current_spec.name)
dependency = current_dependencies[current_spec.name]
+ active_spec = retrieve_active_spec(strict, definition, current_spec)
- if options["strict"]
- active_spec = definition.specs.detect {|spec| spec.name == current_spec.name && spec.platform == current_spec.platform }
- else
- active_specs = definition.index[current_spec.name].select {|spec| spec.platform == current_spec.platform }.sort_by(&:version)
- if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
- active_spec = active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
+ next if active_spec.nil?
+ if filter_options_patch.any?
+ update_present = update_present_via_semver_portions(current_spec, active_spec, options)
+ next unless update_present
+ end
+
+ gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
+ if gem_outdated || (current_spec.git_version != active_spec.git_version)
+ groups = nil
+ if dependency && !options[:parseable]
+ groups = dependency.groups.join(", ")
end
- active_spec = active_specs.last
- if options[:major] || options[:minor] || options[:patch]
- update_present = update_present_via_semver_portions(current_spec, active_spec, options)
- active_spec = nil unless update_present
+ outdated_gems_list << { :active_spec => active_spec,
+ :current_spec => current_spec,
+ :dependency => dependency,
+ :groups => groups }
+
+ outdated_gems_by_groups[groups] ||= []
+ outdated_gems_by_groups[groups] << { :active_spec => active_spec,
+ :current_spec => current_spec,
+ :dependency => dependency,
+ :groups => groups }
+ end
+
+ Bundler.ui.debug "from #{active_spec.loaded_from}"
+ end
+
+ if outdated_gems_list.empty?
+ display_nothing_outdated_message(filter_options_patch)
+ else
+ unless options[:parseable]
+ if options[:pre]
+ Bundler.ui.info "Outdated gems included in the bundle (including " \
+ "pre-releases):"
+ else
+ Bundler.ui.info "Outdated gems included in the bundle:"
end
end
- next if active_spec.nil?
+ options_include_groups = [:group, :groups].select do |v|
+ options.keys.include?(v.to_s)
+ end
- gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
- git_outdated = current_spec.git_version != active_spec.git_version
- if gem_outdated || git_outdated
- unless options[:parseable]
- if out_count == 0
- if options["pre"]
- Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):"
+ if options_include_groups.any?
+ ordered_groups = outdated_gems_by_groups.keys.compact.sort
+ [nil, ordered_groups].flatten.each do |groups|
+ gems = outdated_gems_by_groups[groups]
+ contains_group = if groups
+ groups.split(",").include?(options[:group])
+ else
+ options[:group] == "group"
+ end
+
+ next if (!options[:groups] && !contains_group) || gems.nil?
+
+ unless options[:parseable]
+ if groups
+ Bundler.ui.info "===== Group #{groups} ====="
else
- Bundler.ui.info "Outdated gems included in the bundle:"
+ Bundler.ui.info "===== Without group ====="
end
end
+
+ gems.each do |gem|
+ print_gem(
+ gem[:current_spec],
+ gem[:active_spec],
+ gem[:dependency],
+ groups,
+ options_include_groups.any?
+ )
+ end
+ end
+ else
+ outdated_gems_list.each do |gem|
+ print_gem(
+ gem[:current_spec],
+ gem[:active_spec],
+ gem[:dependency],
+ gem[:groups],
+ options_include_groups.any?
+ )
end
+ end
- spec_version = "#{active_spec.version}#{active_spec.git_version}"
- current_version = "#{current_spec.version}#{current_spec.git_version}"
- dependency_version = %(, requested #{dependency.requirement}) if dependency && dependency.specific?
+ exit 1
+ end
+ end
- if dependency && !options[:parseable]
- groups = dependency.groups.join(", ")
- pl = (dependency.groups.length > 1) ? "s" : ""
- groups = " in group#{pl} \"#{groups}\""
- end
+ private
- spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, installed #{current_version}#{dependency_version})"
- if options[:parseable]
- Bundler.ui.info spec_outdated_info.to_s.rstrip
- else
- Bundler.ui.info " * #{spec_outdated_info}#{groups}".rstrip
- end
+ def retrieve_active_spec(strict, definition, current_spec)
+ if strict
+ active_spec = definition.find_resolved_spec(current_spec)
+ else
+ active_specs = definition.find_indexed_specs(current_spec)
+ if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
+ active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
+ end
+ active_spec = active_specs.last
+ end
- out_count += 1
+ active_spec
+ end
+
+ def display_nothing_outdated_message(filter_options_patch)
+ unless options[:parseable]
+ if filter_options_patch.any?
+ display = filter_options_patch.map do |o|
+ o.sub("filter-", "")
+ end.join(" or ")
+
+ Bundler.ui.info "No #{display} updates to display.\n"
+ else
+ Bundler.ui.info "Bundle up to date!\n"
end
- Bundler.ui.debug "from #{active_spec.loaded_from}"
end
+ end
- if out_count.zero?
- Bundler.ui.info "Bundle up to date!\n" unless options[:parseable]
+ def print_gem(current_spec, active_spec, dependency, groups, options_include_groups)
+ spec_version = "#{active_spec.version}#{active_spec.git_version}"
+ current_version = "#{current_spec.version}#{current_spec.git_version}"
+
+ if dependency && dependency.specific?
+ dependency_version = %(, requested #{dependency.requirement})
+ end
+
+ spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \
+ "installed #{current_version}#{dependency_version})"
+
+ output_message = if options[:parseable]
+ spec_outdated_info.to_s
+ elsif options_include_groups || !groups
+ " * #{spec_outdated_info}"
else
- exit 1
+ " * #{spec_outdated_info} in groups \"#{groups}\""
end
- end
- private
+ Bundler.ui.info output_message.rstrip
+ end
def check_for_deployment_mode
if Bundler.settings[:frozen]
- error_message = "You are trying to check outdated gems in deployment mode. " \
- "Run `bundle outdated` elsewhere.\n" \
- "\nIf this is a development machine, remove the #{Bundler.default_gemfile} freeze" \
- "\nby running `bundle install --no-deployment`."
- raise ProductionError, error_message
+ raise ProductionError, "You are trying to check outdated gems in " \
+ "deployment mode. Run `bundle outdated` elsewhere.\n" \
+ "\nIf this is a development machine, remove the " \
+ "#{Bundler.default_gemfile} freeze" \
+ "\nby running `bundle install --no-deployment`."
end
end
@@ -123,16 +230,15 @@ module Bundler
active_major = active_spec.version.segments.first
update_present = false
+ update_present = active_major > current_major if options["filter-major"]
- update_present = active_major > current_major if options[:major]
-
- if !update_present && (options[:minor] || options[:patch]) && current_major == active_major
+ if !update_present && (options["filter-minor"] || options["filter-patch"]) && current_major == active_major
current_minor = get_version_semver_portion_value(current_spec, 1)
active_minor = get_version_semver_portion_value(active_spec, 1)
- update_present = active_minor > current_minor if options[:minor]
+ update_present = active_minor > current_minor if options["filter-minor"]
- if !update_present && options[:patch] && current_minor == active_minor
+ if !update_present && options["filter-patch"] && current_minor == active_minor
current_patch = get_version_semver_portion_value(current_spec, 2)
active_patch = get_version_semver_portion_value(active_spec, 2)
diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb
index 77e845a603..47d4470aec 100644
--- a/lib/bundler/cli/show.rb
+++ b/lib/bundler/cli/show.rb
@@ -64,6 +64,7 @@ module Bundler
else
definition.resolve_with_cache!
end
+ Bundler.reset!
definition.specs
end
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
index 5aac47bd09..8a7541c259 100644
--- a/lib/bundler/cli/update.rb
+++ b/lib/bundler/cli/update.rb
@@ -1,4 +1,6 @@
# frozen_string_literal: true
+require "bundler/cli/common"
+
module Bundler
class CLI::Update
attr_reader :options, :gems
@@ -27,7 +29,6 @@ module Bundler
names = Bundler.locked_gems.specs.map(&:name)
gems.each do |g|
next if names.include?(g)
- require "bundler/cli/common"
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(g, names)
end
@@ -36,15 +37,11 @@ module Bundler
gems.concat(specs.map(&:name))
end
- Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby])
+ Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
+ :lock_shared_dependencies => options[:conservative])
end
- patch_level = [:major, :minor, :patch].select {|v| options.keys.include?(v.to_s) }
- raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1
- Bundler.definition.gem_version_promoter.tap do |gvp|
- gvp.level = patch_level.first || :major
- gvp.strict = options[:strict]
- end
+ Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options)
Bundler::Fetcher.disable_endpoint = options["full-index"]
@@ -54,11 +51,8 @@ module Bundler
Bundler.settings[:jobs] = opts["jobs"] if opts["jobs"]
- # rubygems plugins sometimes hook into the gem install process
- Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
-
Bundler.definition.validate_runtime!
- Installer.install Bundler.root, Bundler.definition, opts
+ installer = Installer.install Bundler.root, Bundler.definition, opts
Bundler.load.cache if Bundler.app_cache.exist?
if Bundler.settings[:clean] && Bundler.settings[:path]
@@ -67,15 +61,8 @@ module Bundler
end
Bundler.ui.confirm "Bundle updated!"
- without_groups_messages
- end
-
- private
-
- def without_groups_messages
- return unless Bundler.settings.without.any?
- require "bundler/cli/common"
- Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
+ Bundler::CLI::Common.output_without_groups_message
+ Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
end
end
end
diff --git a/lib/bundler/cli/viz.rb b/lib/bundler/cli/viz.rb
index 75e6affff9..418c462a93 100644
--- a/lib/bundler/cli/viz.rb
+++ b/lib/bundler/cli/viz.rb
@@ -21,7 +21,7 @@ module Bundler
rescue StandardError => e
raise unless e.message =~ /GraphViz not installed or dot not in PATH/
Bundler.ui.error e.message
- Bundler.ui.warn "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`."
+ Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`."
end
end
end
diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb
new file mode 100644
index 0000000000..3ed05ca484
--- /dev/null
+++ b/lib/bundler/compact_index_client.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+require "pathname"
+require "set"
+
+module Bundler
+ class CompactIndexClient
+ DEBUG_MUTEX = Mutex.new
+ def self.debug
+ return unless ENV["DEBUG_COMPACT_INDEX"]
+ DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
+ end
+
+ class Error < StandardError; end
+
+ require "bundler/compact_index_client/cache"
+ require "bundler/compact_index_client/updater"
+
+ attr_reader :directory
+
+ # @return [Lambda] A lambda that takes an array of inputs and a block, and
+ # maps the inputs with the block in parallel.
+ #
+ attr_accessor :in_parallel
+
+ def initialize(directory, fetcher)
+ @directory = Pathname.new(directory)
+ @updater = Updater.new(fetcher)
+ @cache = Cache.new(@directory)
+ @endpoints = Set.new
+ @info_checksums_by_name = {}
+ @parsed_checksums = false
+ @mutex = Mutex.new
+ @in_parallel = lambda do |inputs, &blk|
+ inputs.map(&blk)
+ end
+ end
+
+ def names
+ Bundler::CompactIndexClient.debug { "/names" }
+ update(@cache.names_path, "names")
+ @cache.names
+ end
+
+ def versions
+ Bundler::CompactIndexClient.debug { "/versions" }
+ update(@cache.versions_path, "versions")
+ versions, @info_checksums_by_name = @cache.versions
+ versions
+ end
+
+ def dependencies(names)
+ Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
+ in_parallel.call(names) do |name|
+ update_info(name)
+ @cache.dependencies(name).map {|d| d.unshift(name) }
+ end.flatten(1)
+ end
+
+ def spec(name, version, platform = nil)
+ Bundler::CompactIndexClient.debug { "spec(name = #{name}, version = #{version}, platform = #{platform})" }
+ update_info(name)
+ @cache.specific_dependency(name, version, platform)
+ end
+
+ def update_and_parse_checksums!
+ Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
+ return @info_checksums_by_name if @parsed_checksums
+ update(@cache.versions_path, "versions")
+ @info_checksums_by_name = @cache.checksums
+ @parsed_checksums = true
+ end
+
+ private
+
+ def update(local_path, remote_path)
+ Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
+ unless synchronize { @endpoints.add?(remote_path) }
+ Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
+ return
+ end
+ @updater.update(local_path, url(remote_path))
+ end
+
+ def update_info(name)
+ Bundler::CompactIndexClient.debug { "update_info(#{name})" }
+ path = @cache.info_path(name)
+ checksum = @updater.checksum_for_file(path)
+ unless existing = @info_checksums_by_name[name]
+ Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
+ return
+ end
+ if checksum == existing
+ Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
+ return
+ end
+ Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
+ update(path, "info/#{name}")
+ end
+
+ def url(path)
+ path
+ end
+
+ def synchronize
+ @mutex.synchronize { yield }
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
new file mode 100644
index 0000000000..e44f05dc7e
--- /dev/null
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+require "digest/md5"
+
+module Bundler
+ class CompactIndexClient
+ class Cache
+ attr_reader :directory
+
+ def initialize(directory)
+ @directory = Pathname.new(directory).expand_path
+ info_roots.each do |dir|
+ SharedHelpers.filesystem_access(dir) do
+ FileUtils.mkdir_p(dir)
+ end
+ end
+ end
+
+ def names
+ lines(names_path)
+ end
+
+ def names_path
+ directory.join("names")
+ end
+
+ def versions
+ versions_by_name = Hash.new {|hash, key| hash[key] = [] }
+ info_checksums_by_name = {}
+
+ lines(versions_path).each do |line|
+ name, versions_string, info_checksum = line.split(" ", 3)
+ info_checksums_by_name[name] = info_checksum || ""
+ versions_string.split(",").each do |version|
+ if version.start_with?("-")
+ version = version[1..-1].split("-", 2).unshift(name)
+ versions_by_name[name].delete(version)
+ else
+ version = version.split("-", 2).unshift(name)
+ versions_by_name[name] << version
+ end
+ end
+ end
+
+ [versions_by_name, info_checksums_by_name]
+ end
+
+ def versions_path
+ directory.join("versions")
+ end
+
+ def checksums
+ checksums = {}
+
+ lines(versions_path).each do |line|
+ name, _, checksum = line.split(" ", 3)
+ checksums[name] = checksum
+ end
+
+ checksums
+ end
+
+ def dependencies(name)
+ lines(info_path(name)).map do |line|
+ parse_gem(line)
+ end
+ end
+
+ def info_path(name)
+ name = name.to_s
+ if name =~ /[^a-z0-9_-]/
+ name += "-#{Digest::MD5.hexdigest(name).downcase}"
+ info_roots.last.join(name)
+ else
+ info_roots.first.join(name)
+ end
+ end
+
+ def specific_dependency(name, version, platform)
+ pattern = [version, platform].compact.join("-")
+ return nil if pattern.empty?
+
+ gem_lines = info_path(name).read
+ gem_line = gem_lines[/^#{Regexp.escape(pattern)}\b.*/, 0]
+ gem_line ? parse_gem(gem_line) : nil
+ end
+
+ private
+
+ def lines(path)
+ return [] unless path.file?
+ lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
+ header = lines.index("---")
+ header ? lines[header + 1..-1] : lines
+ end
+
+ def parse_gem(string)
+ version_and_platform, rest = string.split(" ", 2)
+ version, platform = version_and_platform.split("-", 2)
+ dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
+ dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
+ requirements = requirements ? requirements.map {|r| parse_dependency(r) } : []
+ [version, platform, dependencies, requirements]
+ end
+
+ def parse_dependency(string)
+ dependency = string.split(":")
+ dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency
+ end
+
+ def info_roots
+ [
+ directory.join("info"),
+ directory.join("info-special-characters"),
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
new file mode 100644
index 0000000000..5aa480fdcc
--- /dev/null
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+require "fileutils"
+require "stringio"
+require "tmpdir"
+require "zlib"
+
+module Bundler
+ class CompactIndexClient
+ class Updater
+ class MisMatchedChecksumError < Error
+ def initialize(path, server_checksum, local_checksum)
+ @path = path
+ @server_checksum = server_checksum
+ @local_checksum = local_checksum
+ end
+
+ def message
+ "The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
+ "(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
+ end
+ end
+
+ def initialize(fetcher)
+ @fetcher = fetcher
+ end
+
+ def update(local_path, remote_path, retrying = nil)
+ headers = {}
+
+ Dir.mktmpdir("bundler-compact-index-") do |local_temp_dir|
+ local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)
+
+ # first try to fetch any new bytes on the existing file
+ if retrying.nil? && local_path.file?
+ FileUtils.cp local_path, local_temp_path
+ headers["If-None-Match"] = etag_for(local_temp_path)
+ headers["Range"] = "bytes=#{local_temp_path.size}-"
+ else
+ # Fastly ignores Range when Accept-Encoding: gzip is set
+ headers["Accept-Encoding"] = "gzip"
+ end
+
+ response = @fetcher.call(remote_path, headers)
+ return nil if response.is_a?(Net::HTTPNotModified)
+
+ content = response.body
+ if response["Content-Encoding"] == "gzip"
+ content = Zlib::GzipReader.new(StringIO.new(content)).read
+ end
+
+ mode = response.is_a?(Net::HTTPPartialContent) ? "a" : "w"
+ SharedHelpers.filesystem_access(local_temp_path) do
+ local_temp_path.open(mode) {|f| f << content }
+ end
+
+ response_etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
+ if etag_for(local_temp_path) == response_etag
+ SharedHelpers.filesystem_access(local_path) do
+ FileUtils.mv(local_temp_path, local_path)
+ end
+ return nil
+ end
+
+ if retrying
+ raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path))
+ end
+
+ update(local_path, remote_path, :retrying)
+ end
+ end
+
+ def etag_for(path)
+ sum = checksum_for_file(path)
+ sum ? %("#{sum}") : nil
+ end
+
+ def checksum_for_file(path)
+ return nil unless path.file?
+ # This must use IO.read instead of Digest.file().hexdigest
+ # because we need to preserve \n line endings on windows when calculating
+ # the checksum
+ SharedHelpers.filesystem_access(path, :read) do
+ Digest::MD5.hexdigest(IO.read(path))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index 6180285942..cca40100ad 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -16,6 +16,7 @@ module Bundler
2.2
2.3
2.4
+ 2.5
).freeze
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
@@ -57,15 +58,15 @@ module Bundler
end
def mswin64?
- Bundler::WINDOWS && Gem::Platform.local.os == "mswin64" && Gem::Platform.local.cpu == "x64"
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end
def mingw?
- Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != "x64"
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end
def x64_mingw?
- Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu == "x64"
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
end
(KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 0cb6e7e399..8e8897df02 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -102,15 +102,19 @@ module Bundler
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
- @gem_version_promoter = create_gem_version_promoter
-
add_current_platform unless Bundler.settings[:frozen]
+ converge_path_sources_to_gemspec_sources
@path_changes = converge_paths
- eager_unlock = expand_dependencies(@unlock[:gems])
- @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name)
-
@source_changes = converge_sources
+
+ unless @unlock[:lock_shared_dependencies]
+ eager_unlock = expand_dependencies(@unlock[:gems])
+ @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name)
+ end
+
+ @gem_version_promoter = create_gem_version_promoter
+
@dependency_changes = converge_dependencies
@local_changes = converge_locals
@@ -135,17 +139,15 @@ module Bundler
end
def create_gem_version_promoter
- locked_specs = begin
+ locked_specs =
if @unlocking && @locked_specs.empty? && !@lockfile_contents.empty?
# Definition uses an empty set of locked_specs to indicate all gems
# are unlocked, but GemVersionPromoter needs the locked_specs
# for conservative comparison.
- locked = Bundler::LockfileParser.new(@lockfile_contents)
- Bundler::SpecSet.new(locked.specs)
+ Bundler::SpecSet.new(@locked_gems.specs)
else
@locked_specs
end
- end
GemVersionPromoter.new(locked_specs, @unlock[:gems])
end
@@ -175,7 +177,7 @@ module Bundler
rescue GemNotFound => e # Handle yanked gem
gem_name, gem_version = extract_gem_info(e)
locked_gem = @locked_specs[gem_name].last
- raise if locked_gem.nil? || locked_gem.version.to_s != gem_version
+ raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \
"be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \
"that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \
@@ -247,7 +249,7 @@ module Bundler
else
# Run a resolve against the locally available gems
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
- last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve)
+ last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
end
end
end
@@ -407,8 +409,10 @@ module Bundler
"updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control."
unless explicit_flag
+
+ suggested_command = Bundler.settings.locations("frozen")[:global] == "1" ? "bundle config --delete frozen" : "bundle install --no-deployment"
msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
- "freeze \nby running `bundle install --no-deployment`."
+ "freeze \nby running `#{suggested_command}`."
end
added = []
@@ -460,12 +464,13 @@ module Bundler
changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
end
+ msg << "\n\n#{change_reason.split(", ").join("\n")}\n"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
msg << "\n"
- raise ProductionError, msg if added.any? || deleted.any? || changed.any?
+ raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
end
def validate_runtime!
@@ -498,14 +503,10 @@ module Bundler
end
end
- # TODO: refactor this so that `match_platform` can be called with two platforms
- DummyPlatform = Struct.new(:platform)
- class DummyPlatform; include MatchPlatform; end
def validate_platforms!
return if @platforms.any? do |bundle_platform|
- bundle_platform = DummyPlatform.new(bundle_platform)
Bundler.rubygems.platforms.any? do |local_platform|
- bundle_platform.match_platform(local_platform)
+ MatchPlatform.platforms_match?(bundle_platform, local_platform)
end
end
@@ -525,11 +526,19 @@ module Bundler
end
def add_current_platform
- current_platform = Bundler.rubygems.platforms.last
+ current_platform = Bundler.local_platform
add_platform(current_platform) if Bundler.settings[:specific_platform]
add_platform(generic(current_platform))
end
+ def find_resolved_spec(current_spec)
+ specs.find_by_name_and_platform(current_spec.name, current_spec.platform)
+ end
+
+ def find_indexed_specs(current_spec)
+ index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
+ end
+
attr_reader :sources
private :sources
@@ -586,7 +595,8 @@ module Bundler
locked_index = Index.new
locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
- source.specs != locked_index
+ # order here matters, since Index#== is checking source.specs.include?(locked_index)
+ locked_index != source.specs
end
# Get all locals and override their matching sources.
@@ -622,15 +632,20 @@ module Bundler
gemspec_source || source
end
- def converge_sources
- changes = false
-
+ def converge_path_sources_to_gemspec_sources
@locked_sources.map! do |source|
converge_path_source_to_gemspec_source(source)
end
@locked_specs.each do |spec|
spec.source &&= converge_path_source_to_gemspec_source(spec.source)
end
+ @locked_deps.each do |dep|
+ dep.source &&= converge_path_source_to_gemspec_source(dep.source)
+ end
+ end
+
+ def converge_sources
+ changes = false
# Get the Rubygems sources from the Gemfile.lock
locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@@ -680,7 +695,8 @@ module Bundler
dep.platforms.concat(@platforms.map {|p| Dependency::REVERSE_PLATFORM_MAP[p] }.flatten(1)).uniq!
end
end
- Set.new(@dependencies) != Set.new(@locked_deps)
+ dependency_without_type = proc {|d| Gem::Dependency.new(d.name, *d.requirement.as_list) }
+ Set.new(@dependencies.map(&dependency_without_type)) != Set.new(@locked_deps.map(&dependency_without_type))
end
# Remove elements from the locked specs that are expired. This will most
@@ -738,8 +754,9 @@ module Bundler
next unless other
deps2 = other.dependencies.select {|d| d.type != :development }
+ runtime_dependencies = s.dependencies.select {|d| d.type != :development }
# If the dependencies of the path source have changed, unlock it
- next unless s.dependencies.sort == deps2.sort
+ next unless runtime_dependencies.sort == deps2.sort
end
converged << s
@@ -810,8 +827,18 @@ module Bundler
deps = []
dependencies.each do |dep|
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
- next unless remote || dep.current_platform?
- dep.gem_platforms(@platforms).each do |p|
+ next if !remote && !dep.current_platform?
+ platforms = dep.gem_platforms(@platforms)
+ if platforms.empty?
+ mapped_platforms = dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] }
+ Bundler.ui.warn \
+ "The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \
+ "Bundler is installing for #{@platforms.join ", "} but the dependency " \
+ "is only for #{mapped_platforms.join ", "}. " \
+ "To add those platforms to the bundle, " \
+ "run `bundle lock --add-platform #{mapped_platforms.join " "}`."
+ end
+ platforms.each do |p|
deps << DepProxy.new(dep, p) if remote || p == generic_local_platform
end
end
@@ -888,7 +915,7 @@ module Bundler
end
def additional_base_requirements_for_resolve
- return [] unless @locked_gems && Bundler.settings[:only_update_to_newer_versions]
+ return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions?
@locked_gems.specs.reduce({}) do |requirements, locked_spec|
dep = Gem::Dependency.new(locked_spec.name, ">= #{locked_spec.version}")
requirements[locked_spec.name] = DepProxy.new(dep, locked_spec.platform)
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index 66162d741a..d2bac66cdb 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -17,6 +17,8 @@ module Bundler
:ruby_21 => Gem::Platform::RUBY,
:ruby_22 => Gem::Platform::RUBY,
:ruby_23 => Gem::Platform::RUBY,
+ :ruby_24 => Gem::Platform::RUBY,
+ :ruby_25 => Gem::Platform::RUBY,
:mri => Gem::Platform::RUBY,
:mri_18 => Gem::Platform::RUBY,
:mri_19 => Gem::Platform::RUBY,
@@ -24,6 +26,8 @@ module Bundler
:mri_21 => Gem::Platform::RUBY,
:mri_22 => Gem::Platform::RUBY,
:mri_23 => Gem::Platform::RUBY,
+ :mri_24 => Gem::Platform::RUBY,
+ :mri_25 => Gem::Platform::RUBY,
:rbx => Gem::Platform::RUBY,
:jruby => Gem::Platform::JAVA,
:jruby_18 => Gem::Platform::JAVA,
@@ -35,12 +39,16 @@ module Bundler
:mswin_21 => Gem::Platform::MSWIN,
:mswin_22 => Gem::Platform::MSWIN,
:mswin_23 => Gem::Platform::MSWIN,
+ :mswin_24 => Gem::Platform::MSWIN,
+ :mswin_25 => Gem::Platform::MSWIN,
:mswin64 => Gem::Platform::MSWIN64,
:mswin64_19 => Gem::Platform::MSWIN64,
:mswin64_20 => Gem::Platform::MSWIN64,
:mswin64_21 => Gem::Platform::MSWIN64,
:mswin64_22 => Gem::Platform::MSWIN64,
:mswin64_23 => Gem::Platform::MSWIN64,
+ :mswin64_24 => Gem::Platform::MSWIN64,
+ :mswin64_25 => Gem::Platform::MSWIN64,
:mingw => Gem::Platform::MINGW,
:mingw_18 => Gem::Platform::MINGW,
:mingw_19 => Gem::Platform::MINGW,
@@ -48,11 +56,15 @@ module Bundler
:mingw_21 => Gem::Platform::MINGW,
:mingw_22 => Gem::Platform::MINGW,
:mingw_23 => Gem::Platform::MINGW,
+ :mingw_24 => Gem::Platform::MINGW,
+ :mingw_25 => Gem::Platform::MINGW,
:x64_mingw => Gem::Platform::X64_MINGW,
:x64_mingw_20 => Gem::Platform::X64_MINGW,
:x64_mingw_21 => Gem::Platform::X64_MINGW,
:x64_mingw_22 => Gem::Platform::X64_MINGW,
:x64_mingw_23 => Gem::Platform::X64_MINGW,
+ :x64_mingw_24 => Gem::Platform::X64_MINGW,
+ :x64_mingw_25 => Gem::Platform::X64_MINGW,
}.freeze
REVERSE_PLATFORM_MAP = {}.tap do |reverse_platform_map|
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index b064c80d4c..cdbae076f0 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -65,7 +65,7 @@ module Bundler
case specs_by_name_and_version.size
when 1
specs = specs_by_name_and_version.values.first
- spec = specs.find {|s| s.match_platform(Gem::Platform.local) } || specs.first
+ spec = specs.find {|s| s.match_platform(Bundler.local_platform) } || specs.first
@gemspecs << spec
@@ -393,7 +393,8 @@ module Bundler
"as an option for #{command}, but it is invalid."
end
- message << " Valid options are: #{valid_keys.join(", ")}"
+ message << " Valid options are: #{valid_keys.join(", ")}."
+ message << " You may be able to resolve this by upgrading Bundler to the newest version."
raise InvalidOption, message
end
end
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index 69d05167e8..5e14f03265 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -91,6 +91,13 @@ module Bundler
end
def __swap__(spec)
+ without_type = proc {|d| Gem::Dependency.new(d.name, d.requirements_list.sort) }
+ if (extra_deps = spec.runtime_dependencies.map(&without_type).-(dependencies.map(&without_type))) && extra_deps.any?
+ Bundler.ui.debug "#{full_name} from #{remote} has corrupted API dependencies (API returned #{dependencies}, real spec has (#{spec.runtime_dependencies}))"
+ raise APIResponseMismatchError,
+ "Downloading #{full_name} revealed dependencies not in the API (#{extra_deps.map(&:to_s).join(", ")})." \
+ "\nInstalling with `--full-index` should fix the problem."
+ end
@remote_specification = spec
end
@@ -113,6 +120,8 @@ module Bundler
@required_ruby_version = Gem::Requirement.new(v)
end
end
+ rescue => e
+ raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}"
end
def build_dependency(name, requirements)
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
index 8baf698dff..7e9bcc2866 100644
--- a/lib/bundler/env.rb
+++ b/lib/bundler/env.rb
@@ -12,41 +12,49 @@ module Bundler
print_gemfile = options.delete(:print_gemfile)
print_gemspecs = options.delete(:print_gemspecs)
- out = String.new("Environment\n\n")
- out << " Bundler #{Bundler::VERSION}\n"
- out << " Rubygems #{Gem::VERSION}\n"
- out << " Ruby #{ruby_version}"
- out << " GEM_HOME #{ENV["GEM_HOME"]}\n" unless ENV["GEM_HOME"].nil? || ENV["GEM_HOME"].empty?
- out << " GEM_PATH #{ENV["GEM_PATH"]}\n" unless ENV["GEM_PATH"] == ENV["GEM_HOME"]
- out << " RVM #{ENV["rvm_version"]}\n" if ENV["rvm_version"]
- out << " Git #{git_version}\n"
- out << " OpenSSL #{OpenSSL::OPENSSL_VERSION}\n" if defined?(OpenSSL::OPENSSL_VERSION)
+ out = String.new("## Environment\n\n```\n")
+ out << "Bundler #{Bundler::VERSION}\n"
+ out << "Rubygems #{Gem::VERSION}\n"
+ out << "Ruby #{ruby_version}"
+ out << "GEM_HOME #{ENV["GEM_HOME"]}\n" unless ENV["GEM_HOME"].nil? || ENV["GEM_HOME"].empty?
+ out << "GEM_PATH #{ENV["GEM_PATH"]}\n" unless ENV["GEM_PATH"] == ENV["GEM_HOME"]
+ out << "RVM #{ENV["rvm_version"]}\n" if ENV["rvm_version"]
+ out << "Git #{git_version}\n"
+ out << "Platform #{Gem::Platform.local}\n"
+ out << "OpenSSL #{OpenSSL::OPENSSL_VERSION}\n" if defined?(OpenSSL::OPENSSL_VERSION)
%w(rubygems-bundler open_gem).each do |name|
specs = Bundler.rubygems.find_name(name)
- out << " #{name} (#{specs.map(&:version).join(",")})\n" unless specs.empty?
+ out << "#{name} (#{specs.map(&:version).join(",")})\n" unless specs.empty?
end
- out << "\nBundler settings\n\n" unless Bundler.settings.all.empty?
+ out << "```\n"
+
+ out << "\n## Bundler settings\n\n```\n" unless Bundler.settings.all.empty?
Bundler.settings.all.each do |setting|
- out << " " << setting << "\n"
+ out << setting << "\n"
Bundler.settings.pretty_values_for(setting).each do |line|
- out << " " << line << "\n"
+ out << " " << line << "\n"
end
end
+ out << "```\n"
+
+ return out unless SharedHelpers.in_bundle?
if print_gemfile
- out << "\n#{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)}\n\n"
- out << " " << read_file(Bundler.default_gemfile).gsub(/\n/, "\n ") << "\n"
+ out << "\n## Gemfile\n"
+ out << "\n### #{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "```ruby\n" << read_file(Bundler.default_gemfile).chomp << "\n```\n"
- out << "\n#{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n"
- out << " " << read_file(Bundler.default_lockfile).gsub(/\n/, "\n ") << "\n"
+ out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n"
end
if print_gemspecs
dsl = Dsl.new.tap {|d| d.eval_gemfile(Bundler.default_gemfile) }
+ out << "\n## Gemspecs\n" unless dsl.gemspecs.empty?
dsl.gemspecs.each do |gs|
- out << "\n#{File.basename(gs.loaded_from)}"
- out << "\n\n " << read_file(gs.loaded_from).gsub(/\n/, "\n ") << "\n"
+ out << "\n### #{File.basename(gs.loaded_from)}"
+ out << "\n\n```ruby\n" << read_file(gs.loaded_from).chomp << "\n```\n"
end
end
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index 7681ea73ae..6ce8493ea7 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -52,6 +52,9 @@ module Bundler
class CyclicDependencyError < BundlerError; status_code(21); end
class GemfileLockNotFound < BundlerError; status_code(22); end
class PluginError < BundlerError; status_code(29); end
+ class SudoNotPermittedError < BundlerError; status_code(30); end
+ class ThreadCreationError < BundlerError; status_code(33); end
+ class APIResponseMismatchError < BundlerError; status_code(34); end
class GemfileEvalError < GemfileError; end
class MarshalError < StandardError; end
@@ -131,4 +134,24 @@ module Bundler
status_code(28)
end
+
+ class NoSpaceOnDeviceError < PermissionError
+ def message
+ "There was an error while trying to #{action} `#{@path}`. " \
+ "There was insufficient space remaining on the device."
+ end
+
+ status_code(31)
+ end
+
+ class GenericSystemCallError < BundlerError
+ attr_reader :underlying_error
+
+ def initialize(underlying_error, message)
+ @underlying_error = underlying_error
+ super("#{message}\nThe underlying system error is #{@underlying_error.class}: #{@underlying_error}")
+ end
+
+ status_code(32)
+ end
end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index 80bf2a5150..150cac1e67 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -1,17 +1,22 @@
# frozen_string_literal: true
module Bundler
class FeatureFlag
- def self.settings_flag(flag)
+ def self.settings_flag(flag, &default)
unless Bundler::Settings::BOOL_KEYS.include?(flag.to_s)
raise "Cannot use `#{flag}` as a settings feature flag since it isn't a bool key"
end
- define_method("#{flag}?") { Bundler.settings[flag] }
+ define_method("#{flag}?") do
+ value = Bundler.settings[flag]
+ value = instance_eval(&default) if value.nil? && !default.nil?
+ value
+ end
end
(1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } }
- settings_flag :allow_offline_install
- settings_flag :plugins
+ settings_flag(:allow_offline_install) { bundler_2_mode? }
+ settings_flag(:only_update_to_newer_versions) { bundler_2_mode? }
+ settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
def initialize(bundler_version)
@bundler_version = Gem::Version.create(bundler_version)
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 0e890d491c..9e208e4957 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -237,7 +237,7 @@ module Bundler
Bundler.settings[:ssl_client_cert]
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
- con = Net::HTTP::Persistent.new "bundler", :ENV
+ con = Bundler::Persistent::Net::HTTP::Persistent.new "bundler", :ENV
if gem_proxy = Bundler.rubygems.configuration[:http_proxy]
con.proxy = URI.parse(gem_proxy) if gem_proxy != :no_proxy
end
@@ -273,7 +273,7 @@ module Bundler
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
- Net::HTTP::Persistent::Error, Zlib::BufError, Errno::EHOSTUNREACH
+ Bundler::Persistent::Net::HTTP::Persistent::Error, Zlib::BufError, Errno::EHOSTUNREACH
].freeze
def bundler_cert_store
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index 9461368df5..97de88101b 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -3,10 +3,10 @@ require "bundler/fetcher/base"
require "bundler/worker"
module Bundler
+ autoload :CompactIndexClient, "bundler/compact_index_client"
+
class Fetcher
class CompactIndex < Base
- require "bundler/vendor/compact_index_client/lib/compact_index_client"
-
def self.compact_index_request(method_name)
method = instance_method(method_name)
undef_method(method_name)
@@ -61,12 +61,13 @@ module Bundler
compact_index_request :fetch_spec
def available?
- user_home = Pathname.new(Bundler.rubygems.user_home)
+ return nil unless md5_available?
+ user_home = Bundler.user_home
return nil unless user_home.directory? && user_home.writable?
# Read info file checksums out of /versions, so we can know if gems are up to date
fetch_uri.scheme != "file" && compact_index_client.update_and_parse_checksums!
rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
- Bundler.ui.warn(e.message)
+ Bundler.ui.debug(e.message)
nil
end
compact_index_request :available?
@@ -78,9 +79,9 @@ module Bundler
private
def compact_index_client
- @compact_index_client ||=
+ @compact_index_client ||= begin
SharedHelpers.filesystem_access(cache_path) do
- CompactIndexClient.new(cache_path, compact_fetcher)
+ CompactIndexClient.new(cache_path, client_fetcher)
end.tap do |client|
client.in_parallel = lambda do |inputs, &blk|
func = lambda {|object, _index| blk.call(object) }
@@ -89,12 +90,13 @@ module Bundler
inputs.map { worker.deq }
end
end
+ end
end
def bundle_worker(func = nil)
@bundle_worker ||= begin
worker_name = "Compact Index (#{display_uri.host})"
- Bundler::Worker.new(25, worker_name, func)
+ Bundler::Worker.new(Bundler.current_ruby.rbx? ? 1 : 25, worker_name, func)
end
@bundle_worker.tap do |worker|
worker.instance_variable_set(:@func, func) if func
@@ -105,17 +107,29 @@ module Bundler
Bundler.user_cache.join("compact_index", remote.cache_slug)
end
- def compact_fetcher
- lambda do |path, headers|
- begin
- downloader.fetch(fetch_uri + path, headers)
- rescue NetworkDownError => e
- raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
- Bundler.ui.warn "Using the cached data for the new index because of a network error: #{e}"
- Net::HTTPNotModified.new(nil, nil, nil)
- end
+ def client_fetcher
+ ClientFetcher.new(self, Bundler.ui)
+ end
+
+ ClientFetcher = Struct.new(:fetcher, :ui) do
+ def call(path, headers)
+ fetcher.downloader.fetch(fetcher.fetch_uri + path, headers)
+ rescue NetworkDownError => e
+ raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
+ ui.warn "Using the cached data for the new index because of a network error: #{e}"
+ Net::HTTPNotModified.new(nil, nil, nil)
end
end
+
+ def md5_available?
+ require "openssl"
+ OpenSSL::Digest::MD5.digest("")
+ true
+ rescue LoadError
+ true
+ rescue OpenSSL::Digest::DigestError
+ false
+ end
end
end
end
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
index 1cd5f9a213..445b0f2332 100644
--- a/lib/bundler/fetcher/dependency.rb
+++ b/lib/bundler/fetcher/dependency.rb
@@ -73,7 +73,7 @@ module Bundler
def dependency_api_uri(gem_names = [])
uri = fetch_uri + "api/v1/dependencies"
- uri.query = "gems=#{CGI.escape(gem_names.join(","))}" if gem_names.any?
+ uri.query = "gems=#{CGI.escape(gem_names.sort.join(","))}" if gem_names.any?
uri
end
end
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index c8d714c05a..ee1aa1a972 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -38,6 +38,8 @@ module Bundler
end
def request(uri, options)
+ validate_uri_scheme!(uri)
+
Bundler.ui.debug "HTTP GET #{uri}"
req = Net::HTTP::Get.new uri.request_uri, options
if uri.user
@@ -61,6 +63,15 @@ module Bundler
raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
end
end
+
+ private
+
+ def validate_uri_scheme!(uri)
+ return if uri.scheme =~ /\Ahttps?\z/
+ raise InvalidOption,
+ "The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
+ "Did you mean `http` or `https`?"
+ end
end
end
end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index df45dd6946..3ba3dcdd91 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -37,6 +37,11 @@ module Bundler
when Gem::InvalidSpecificationException
Bundler.ui.error error.message, :wrap => true
when SystemExit
+ when *[defined?(Java::JavaLang::OutOfMemoryError) && Java::JavaLang::OutOfMemoryError].compact
+ Bundler.ui.error "\nYour JVM has run out of memory, and Bundler cannot continue. " \
+ "You can decrease the amount of memory Bundler needs by removing gems from your Gemfile, " \
+ "especially large gems. (Gems can be as large as hundreds of megabytes, and Bundler has to read those files!). " \
+ "Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)."
else request_issue_report_for(error)
end
end
@@ -53,25 +58,41 @@ module Bundler
def request_issue_report_for(e)
Bundler.ui.info <<-EOS.gsub(/^ {8}/, "")
--- ERROR REPORT TEMPLATE -------------------------------------------------------
- - What did you do?
+ # Error Report
+
+ ## Questions
+
+ Please fill out answers to these questions, it'll help us figure out
+ why things are going wrong.
+
+ - **What did you do?**
I ran the command `#{$PROGRAM_NAME} #{ARGV.join(" ")}`
- - What did you expect to happen?
+ - **What did you expect to happen?**
I expected Bundler to...
- - What happened instead?
+ - **What happened instead?**
Instead, what happened was...
+ - **Have you tried any solutions posted on similar issues in our issue tracker, stack overflow, or google?**
+
+ I tried...
+
+ - **Have you read our issues document, https://github.com/bundler/bundler/blob/master/doc/contributing/ISSUES.md?**
+
+ ...
- Error details
+ ## Backtrace
- #{e.class}: #{e.message}
- #{e.backtrace && e.backtrace.join("\n ")}
+ ```
+ #{e.class}: #{e.message}
+ #{e.backtrace && e.backtrace.join("\n ").chomp}
+ ```
- #{Bundler::Env.new.report(:print_gemfile => false, :print_gemspecs => false).gsub(/\n/, "\n ").strip}
+ #{Bundler::Env.new.report}
--- TEMPLATE END ----------------------------------------------------------------
EOS
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index fdb2db7dbf..936d1361fa 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -94,11 +94,12 @@ module Bundler
def rubygem_push(path)
allowed_push_host = nil
gem_command = "gem push '#{path}'"
+ gem_command += " --key #{gem_key}" if gem_key
if @gemspec.respond_to?(:metadata)
allowed_push_host = @gemspec.metadata["allowed_push_host"]
gem_command += " --host #{allowed_push_host}" if allowed_push_host
end
- unless allowed_push_host || Pathname.new("~/.gem/credentials").expand_path.file?
+ unless allowed_push_host || Bundler.user_home.join(".gem/credentials").file?
raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
end
sh(gem_command)
@@ -181,6 +182,10 @@ module Bundler
end
end
+ def gem_key
+ Bundler.settings["gem.push_key"].to_s.downcase if Bundler.settings["gem.push_key"]
+ end
+
def gem_push?
!%w(n no nil false off 0).include?(ENV["gem_push"].to_s.downcase)
end
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index 6d926ce83f..955834ff01 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -25,7 +25,7 @@ module Bundler
module_function :generic
def generic_local_platform
- generic(Gem::Platform.local)
+ generic(Bundler.local_platform)
end
module_function :generic_local_platform
diff --git a/lib/bundler/gemdeps.rb b/lib/bundler/gemdeps.rb
new file mode 100644
index 0000000000..8595b8c7ea
--- /dev/null
+++ b/lib/bundler/gemdeps.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+module Bundler
+ class Gemdeps
+ def initialize(runtime)
+ @runtime = runtime
+ end
+
+ def requested_specs
+ @runtime.requested_specs
+ end
+
+ def specs
+ @runtime.specs
+ end
+
+ def dependencies
+ @runtime.dependencies
+ end
+
+ def current_dependencies
+ @runtime.current_dependencies
+ end
+
+ def requires
+ @runtime.requires
+ end
+ end
+end
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index 4529c57279..373f6132af 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -109,14 +109,19 @@ module Bundler
# returns a list of the dependencies
def unmet_dependency_names
- names = dependency_names
- names.delete_if {|n| n == "bundler" }
- names.select {|n| search(n).empty? }
+ dependency_names.select do |name|
+ name != "bundler" && search(name).empty?
+ end
end
def dependency_names
names = []
- each {|s| names.concat(s.dependencies.map(&:name)) }
+ each do |spec|
+ spec.dependencies.each do |dep|
+ next if dep.type == :development
+ names << dep.name
+ end
+ end
names.uniq
end
@@ -139,6 +144,8 @@ module Bundler
end
end
+ # Whether all the specs in self are in other
+ # TODO: rename to #include?
def ==(other)
all? do |spec|
other_spec = other[spec].first
@@ -179,7 +186,8 @@ module Bundler
end
wants_prerelease = dependency.requirement.prerelease?
- only_prerelease = specs.all? {|spec| spec.version.prerelease? }
+ wants_prerelease ||= base && base.any? {|base_spec| base_spec.version.prerelease? }
+ only_prerelease = specs.all? {|spec| spec.version.prerelease? }
unless wants_prerelease || only_prerelease
found.reject! {|spec| spec.version.prerelease? }
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index dec3be3e98..4d3791bfb2 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -39,7 +39,7 @@ def gemfile(install = false, options = {}, &gemfile)
def Bundler.root
Bundler::SharedHelpers.pwd.expand_path
end
- ENV["BUNDLE_GEMFILE"] ||= "Gemfile"
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
builder = Bundler::Dsl.new
@@ -60,7 +60,7 @@ def gemfile(install = false, options = {}, &gemfile)
Bundler.ui = ui if install
if install || missing_specs.call
- installer = Bundler::Installer.install(Bundler.root, definition, :system => true)
+ installer = Bundler::Installer.install(Bundler.root, definition, :system => true, :inline => true)
installer.post_install_messages.each do |name, message|
Bundler.ui.info "Post-install message from #{name}:\n#{message}"
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index 824b1a45cd..cbfdddeaf7 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -49,7 +49,7 @@ module Bundler
# Bundler returns a warning message stating so and this method returns.
#
# Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so
- # then proceeds to set up a defintion based on the default gemfile (Gemfile) and the
+ # then proceeds to set up a definition based on the default gemfile (Gemfile) and the
# default lock file (Gemfile.lock). However, this is not the case if the platform is different
# to that which is specified in Gemfile.lock, or if there are any missing specs for the gems.
#
@@ -159,6 +159,7 @@ module Bundler
# that said, it's a rare situation (other than rake), and parallel
# installation is SO MUCH FASTER. so we let people opt in.
def install(options)
+ Bundler.rubygems.load_plugins
force = options["force"]
jobs = 1
jobs = [Bundler.settings[:jobs].to_i - 1, 1].max if can_install_in_parallel?
@@ -211,7 +212,7 @@ module Bundler
end
def resolve_if_need(options)
- if Bundler.default_lockfile.exist? && !options["update"]
+ if !options["update"] && !options[:inline] && Bundler.default_lockfile.file?
local = Bundler.ui.silence do
begin
tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil)
diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb
index 84dee979b5..10e7c7fcd7 100644
--- a/lib/bundler/installer/gem_installer.rb
+++ b/lib/bundler/installer/gem_installer.rb
@@ -16,7 +16,7 @@ module Bundler
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
generate_executable_stubs
return true, post_install_message
- rescue Bundler::InstallHookError, Bundler::SecurityError
+ rescue Bundler::InstallHookError, Bundler::SecurityError, APIResponseMismatchError
raise
rescue Errno::ENOSPC
return false, out_of_space_message
@@ -52,12 +52,12 @@ module Bundler
end
def install
- spec.source.install(spec, :force => force, :ensure_builtin_gems_cached => standalone)
+ spec.source.install(spec, :force => force, :ensure_builtin_gems_cached => standalone, :build_args => Array(spec_settings))
end
def install_with_settings
# Build arguments are global, so this is mutexed
- Bundler.rubygems.with_build_args([spec_settings]) { install }
+ Bundler.rubygems.install_with_build_args([spec_settings]) { install }
end
def out_of_space_message
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index 12f11bb5f0..97c124e015 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -47,27 +47,30 @@ module Bundler
# sure needed dependencies have been installed.
def dependencies_installed?(all_specs)
installed_specs = all_specs.select(&:installed?).map(&:name)
- dependencies(all_specs.map(&:name)).all? {|d| installed_specs.include? d.name }
+ dependencies.all? {|d| installed_specs.include? d.name }
end
# Represents only the non-development dependencies, the ones that are
# itself and are in the total list.
- def dependencies(all_spec_names)
+ def dependencies
@dependencies ||= begin
- deps = all_dependencies.reject {|dep| ignorable_dependency? dep }
- missing = deps.reject {|dep| all_spec_names.include? dep.name }
- unless missing.empty?
- raise Bundler::LockfileError, "Your Gemfile.lock is corrupt. The following #{missing.size > 1 ? "gems are" : "gem is"} missing " \
- "from the DEPENDENCIES section: '#{missing.map(&:name).join('\' \'')}'"
- end
- deps
+ all_dependencies.reject {|dep| ignorable_dependency? dep }
end
end
+ def missing_lockfile_dependencies(all_spec_names)
+ deps = all_dependencies.reject {|dep| ignorable_dependency? dep }
+ deps.reject {|dep| all_spec_names.include? dep.name }
+ end
+
# Represents all dependencies
def all_dependencies
@spec.dependencies
end
+
+ def to_s
+ "#<#{self.class} #{@spec.full_name} (#{state})>"
+ end
end
def self.call(*args)
@@ -79,15 +82,23 @@ module Bundler
[Bundler.settings[:jobs].to_i - 1, 1].max
end
+ attr_reader :size
+
def initialize(installer, all_specs, size, standalone, force)
@installer = installer
@size = size
@standalone = standalone
@force = force
@specs = all_specs.map {|s| SpecInstallation.new(s) }
+ @spec_set = all_specs
end
def call
+ # Since `autoload` has the potential for threading issues on 1.8.7
+ # TODO: remove in bundler 2.0
+ require "bundler/gem_remote_fetcher" if RUBY_VERSION < "1.9"
+
+ check_for_corrupt_lockfile
enqueue_specs
process_specs until @specs.all?(&:installed?) || @specs.any?(&:failed?)
handle_error if @specs.any?(&:failed?)
@@ -106,7 +117,7 @@ module Bundler
spec_install.post_install_message = message
elsif !success
spec_install.state = :failed
- spec_install.error = message
+ spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}"
end
spec_install
}
@@ -131,6 +142,44 @@ module Bundler
raise Bundler::InstallError, errors.map(&:to_s).join("\n\n")
end
+ def check_for_corrupt_lockfile
+ missing_dependencies = @specs.map do |s|
+ [
+ s,
+ s.missing_lockfile_dependencies(@specs.map(&:name)),
+ ]
+ end.reject { |a| a.last.empty? }
+ return if missing_dependencies.empty?
+
+ warning = []
+ warning << "Your lockfile was created by an old Bundler that left some things out."
+ if @size != 1
+ warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time."
+ @size = 1
+ end
+ warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile."
+ warning << "The missing gems are:"
+
+ missing_dependencies.each do |spec, missing|
+ warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}"
+ end
+
+ Bundler.ui.warn(warning.join("\n"))
+ end
+
+ def require_tree_for_spec(spec)
+ tree = @spec_set.what_required(spec)
+ t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n")
+ tree.each_with_index do |s, depth|
+ t << " " * depth.succ << s.name
+ unless tree.last == s
+ t << %( was resolved to #{s.version}, which depends on)
+ end
+ t << %(\n)
+ end
+ t
+ end
+
# Keys in the remains hash represent uninstalled gems specs.
# We enqueue all gem specs that do not have any dependencies.
# Later we call this lambda again to install specs that depended on
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 0b667f7dbe..891495c1d4 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require "uri"
-require "rubygems/spec_fetcher"
require "bundler/match_platform"
module Bundler
@@ -69,8 +68,19 @@ module Bundler
end
def __materialize__
- search_object = Bundler.settings[:specific_platform] ? self : Dependency.new(name, version)
- @specification = source.specs.search(search_object).last
+ search_object = Bundler.settings[:specific_platform] || Bundler.settings[:force_ruby_platform] ? self : Dependency.new(name, version)
+ @specification = if source.is_a?(Source::Gemspec) && source.gemspec.name == name
+ source.gemspec.tap {|s| s.source = source }
+ else
+ search = source.specs.search(search_object).last
+ if search && Gem::Platform.new(search.platform) != Gem::Platform.new(platform) && !search.runtime_dependencies.-(dependencies.reject {|d| d.type == :development }).empty?
+ Bundler.ui.warn "Unable to use the platform-specific (#{search.platform}) version of #{name} (#{version}) " \
+ "because it has different dependencies from the #{platform} version. " \
+ "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
+ search = source.specs.search(self).last
+ end
+ search
+ end
end
def respond_to?(*args)
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 51148ab614..d885c049d2 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require "strscan"
# Some versions of the Bundler 1.1 RC series introduced corrupted
# lockfiles. There were two major problems:
diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb
index 0a4e4c7e3a..050cd0efd3 100644
--- a/lib/bundler/match_platform.rb
+++ b/lib/bundler/match_platform.rb
@@ -6,10 +6,18 @@ module Bundler
include GemHelpers
def match_platform(p)
- Gem::Platform::RUBY == platform ||
- platform.nil? || p == platform ||
- generic(Gem::Platform.new(platform)) === p ||
- Gem::Platform.new(platform) === p
+ MatchPlatform.platforms_match?(platform, p)
+ end
+
+ def self.platforms_match?(gemspec_platform, local_platform)
+ return true if gemspec_platform.nil?
+ return true if Gem::Platform::RUBY == gemspec_platform
+ return true if local_platform == gemspec_platform
+ gemspec_platform = Gem::Platform.new(gemspec_platform)
+ return true if GemHelpers.generic(gemspec_platform) === local_platform
+ return true if gemspec_platform === local_platform
+
+ false
end
end
end
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
index a1f3aaefa2..97a6776adb 100644
--- a/lib/bundler/mirror.rb
+++ b/lib/bundler/mirror.rb
@@ -1,4 +1,6 @@
# frozen_string_literal: true
+require "socket"
+
module Bundler
class Settings
# Class used to build the mirror set and then find a mirror for a given URI
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
index 8fb4119323..66f485ef8e 100644
--- a/lib/bundler/plugin.rb
+++ b/lib/bundler/plugin.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
+require "bundler/plugin/api"
module Bundler
module Plugin
- autoload :API, "bundler/plugin/api"
autoload :DSL, "bundler/plugin/dsl"
autoload :Index, "bundler/plugin/index"
autoload :Installer, "bundler/plugin/installer"
@@ -37,7 +37,11 @@ module Bundler
save_plugins names, specs
rescue PluginError => e
- specs.values.map {|spec| Bundler.rm_rf(spec.full_gem_path) } if specs
+ if specs
+ specs_to_delete = Hash[specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) }]
+ specs_to_delete.values.each {|spec| Bundler.rm_rf(spec.full_gem_path) }
+ end
+
Bundler.ui.error "Failed to install plugin #{name}: #{e.message}\n #{e.backtrace.join("\n ")}"
end
diff --git a/lib/bundler/plugin/api.rb b/lib/bundler/plugin/api.rb
index 5bd8792e55..a2d5cbb4ac 100644
--- a/lib/bundler/plugin/api.rb
+++ b/lib/bundler/plugin/api.rb
@@ -24,6 +24,7 @@ module Bundler
module Plugin
class API
autoload :Source, "bundler/plugin/api/source"
+
# The plugins should declare that they handle a command through this helper.
#
# @param [String] command being handled by them
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb
index 78514563f7..5d3f58df92 100644
--- a/lib/bundler/plugin/api/source.rb
+++ b/lib/bundler/plugin/api/source.rb
@@ -6,7 +6,7 @@ module Bundler
module Plugin
class API
# This class provides the base to build source plugins
- # All the method here are require to build a source plugin (except
+ # All the method here are required to build a source plugin (except
# `uri_hash`, `gem_install_dir`; they are helpers).
#
# Defaults for methods, where ever possible are provided which is
@@ -93,7 +93,7 @@ module Bundler
# It should be called in `install` after the plugin is done placing the
# gem at correct install location.
#
- # It also runs Gem hooks `post_install`, `post_build` and `post_install`
+ # It also runs Gem hooks `pre_install`, `post_build` and `post_install`
#
# Note: Do not override if you don't know what you are doing.
def post_install(spec, disable_exts = false)
@@ -287,6 +287,12 @@ module Bundler
def root
Bundler.root
end
+
+ # @private
+ # Returns true
+ def bundler_plugin_api_source?
+ true
+ end
end
end
end
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
index 7f89d26178..8dde072f16 100644
--- a/lib/bundler/plugin/index.rb
+++ b/lib/bundler/plugin/index.rb
@@ -20,6 +20,8 @@ module Bundler
end
end
+ attr_reader :commands
+
def initialize
@plugin_paths = {}
@commands = {}
diff --git a/lib/bundler/postit_trampoline.rb b/lib/bundler/postit_trampoline.rb
index 2a22489954..b62a5e7676 100644
--- a/lib/bundler/postit_trampoline.rb
+++ b/lib/bundler/postit_trampoline.rb
@@ -8,11 +8,11 @@ environment = BundlerVendoredPostIt::PostIt::Environment.new([])
version = Gem::Requirement.new(environment.bundler_version)
if version.requirements.size == 1 && version.requirements.first.first == "=" # version.exact?
if version.requirements.first.last.segments.first >= 2
- ENV["BUNDLE_ENABLE_TRAMPOLINE"] = "true"
+ ENV["BUNDLE_TRAMPOLINE_FORCE"] = "true"
end
end
-if ENV["BUNDLE_ENABLE_TRAMPOLINE"] && !ENV["BUNDLE_DISABLE_POSTIT"]
+if ENV["BUNDLE_TRAMPOLINE_FORCE"] && !ENV["BUNDLE_TRAMPOLINE_DISABLE"]
installed_version =
if defined?(Bundler::VERSION)
Bundler::VERSION
@@ -70,4 +70,4 @@ You're running Bundler #{installed_version} but this project uses #{running_vers
abort "The running bundler (#{running_version}) does not match the required `#{version}`"
end
-end # if ENV["BUNDLE_ENABLE_TRAMPOLINE"] && !ENV["BUNDLE_DISABLE_POSTIT"]
+end # if ENV["BUNDLE_TRAMPOLINE_FORCE"] && !ENV["BUNDLE_TRAMPOLINE_DISABLE"]
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index f45bf4a5ed..e5f9c78b00 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require "uri"
-require "rubygems/spec_fetcher"
module Bundler
# Represents a lazily loaded gem specification, where the full specification
@@ -50,6 +49,13 @@ module Bundler
# once the remote gem is downloaded, the backend specification will
# be swapped out.
def __swap__(spec)
+ without_type = proc {|d| Gem::Dependency.new(d.name, d.requirements_list) }
+ if (extra_deps = spec.runtime_dependencies.map(&without_type).-(dependencies.map(&without_type))) && extra_deps.any?
+ Bundler.ui.debug "#{full_name} from #{remote} has corrupted API dependencies (API returned #{dependencies}, real spec has (#{spec.runtime_dependencies}))"
+ raise APIResponseMismatchError,
+ "Downloading #{full_name} revealed dependencies not in the API (#{extra_deps.map(&without_type).map(&:to_s).join(", ")})." \
+ "\nInstalling with `--full-index` should fix the problem."
+ end
@_remote_specification = spec
end
@@ -82,8 +88,9 @@ module Bundler
_remote_specification.send(method, *args, &blk)
end
- def respond_to_missing?(method, include_all)
- _remote_specification.respond_to?(method, include_all)
+ def respond_to?(method, include_all = false)
+ super || _remote_specification.respond_to?(method, include_all)
end
+ public :respond_to?
end
end
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index b8016b37a9..b8d044e6ef 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -14,7 +14,7 @@ module Bundler
def message
conflicts.sort.reduce(String.new) do |o, (name, conflict)|
- o << %(Bundler could not find compatible versions for gem "#{name}":\n)
+ o << %(\nBundler could not find compatible versions for gem "#{name}":\n)
if conflict.locked_requirement
o << %( In snapshot (#{Bundler.default_lockfile.basename}):\n)
o << %( #{printable_dep(conflict.locked_requirement)}\n)
@@ -62,7 +62,7 @@ module Bundler
end
end
o
- end
+ end.strip
end
end
@@ -175,14 +175,15 @@ module Bundler
# ==== Returns
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
# collection of gemspecs is returned. Otherwise, nil is returned.
- def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [])
+ def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
+ platforms = Set.new(platforms) if platforms
base = SpecSet.new(base) unless base.is_a?(SpecSet)
- resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements)
+ resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
result = resolver.start(requirements)
SpecSet.new(result)
end
- def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements)
+ def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
@index = index
@source_requirements = source_requirements
@base = base
@@ -194,6 +195,7 @@ module Bundler
@base_dg.add_vertex(ls.name, DepProxy.new(dep, ls.platform), true)
end
additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
+ @platforms = platforms
@gem_version_promoter = gem_version_promoter
end
@@ -228,11 +230,11 @@ module Bundler
def debug?
return @debug_mode if defined?(@debug_mode)
- @debug_mode = ENV["DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER_TREE"]
+ @debug_mode = ENV["DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER_TREE"] || false
end
def before_resolution
- Bundler.ui.info "Resolving dependencies...", false
+ Bundler.ui.info "Resolving dependencies...", debug?
end
def after_resolution
@@ -240,7 +242,7 @@ module Bundler
end
def indicate_progress
- Bundler.ui.info ".", false
+ Bundler.ui.info ".", false unless debug?
end
include Molinillo::SpecificationProvider
@@ -308,13 +310,15 @@ module Bundler
def requirement_satisfied_by?(requirement, activated, spec)
return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
- spec.activate_platform!(requirement.__platform) || spec.for?(requirement.__platform)
+ spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform)
+ true
end
def sort_dependencies(dependencies, activated, conflicts)
dependencies.sort_by do |dependency|
name = name_for(dependency)
[
+ @base_dg.vertex_named(name) ? 0 : 1,
activated.vertex_named(name).payload ? 0 : 1,
amount_constrained(dependency),
conflicts[name] ? 0 : 1,
@@ -325,6 +329,12 @@ module Bundler
private
+ # returns an integer \in (-\infty, 0]
+ # a number closer to 0 means the dependency is less constraining
+ #
+ # dependencies w/ 0 or 1 possibilities (ignoring version requirements)
+ # are given very negative values, so they _always_ sort first,
+ # before dependencies that are unconstrained
def amount_constrained(dependency)
@amount_constrained ||= {}
@amount_constrained[dependency.name] ||= begin
@@ -332,8 +342,9 @@ module Bundler
dependency.requirement.satisfied_by?(base.first.version) ? 0 : 1
else
all = index_for(dependency).search(dependency.name).size
+
if all <= 1
- all
+ all - 1_000_000
else
search = search_for(dependency).size
search - all
@@ -365,8 +376,13 @@ module Bundler
"Source does not contain any versions of '#{requirement}'"
end
else
+ cache_message = begin
+ " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
+ rescue GemfileNotFound
+ nil
+ end
message = "Could not find gem '#{requirement}' in any of the gem sources " \
- "listed in your Gemfile or available on this machine."
+ "listed in your Gemfile#{cache_message}."
end
raise GemNotFound, message
end
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
index a7a72feed5..092fb866b3 100644
--- a/lib/bundler/retry.rb
+++ b/lib/bundler/retry.rb
@@ -43,7 +43,10 @@ module Bundler
def fail_attempt(e)
@failed = true
- raise e if last_attempt? || @exceptions.any? {|k| e.is_a?(k) }
+ if last_attempt? || @exceptions.any? {|k| e.is_a?(k) }
+ Bundler.ui.info "" unless Bundler.ui.debug?
+ raise e
+ end
return true unless name
Bundler.ui.info "" unless Bundler.ui.debug? # Add new line incase dots preceded this
Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug?
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index 8c050c6d31..f0a001d296 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -21,10 +21,14 @@ module Bundler
# must not be specified, or the engine version
# specified must match the version.
- @versions = Array(versions)
+ @versions = Array(versions).map do |v|
+ op, v = Gem::Requirement.parse(v)
+ op == "=" ? v.to_s : "#{op} #{v}"
+ end
+
@gem_version = Gem::Requirement.create(@versions.first).requirements.first.last
- @input_engine = engine
- @engine = engine || "ruby"
+ @input_engine = engine && engine.to_s
+ @engine = engine && engine.to_s || "ruby"
@engine_versions = (engine_version && Array(engine_version)) || @versions
@engine_gem_version = Gem::Requirement.create(@engine_versions.first).requirements.first.last
@patchlevel = patchlevel
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index 7cd83e631e..7293e7cfc7 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -8,6 +8,16 @@ end
require "rubygems"
require "rubygems/specification"
+
+begin
+ # Possible use in Gem::Specification#source below and require
+ # shouldn't be deferred.
+ require "rubygems/source"
+rescue LoadError
+ # Not available before Rubygems 2.0.0, ignore
+ nil
+end
+
require "bundler/match_platform"
module Gem
@@ -32,7 +42,11 @@ module Gem
attr_writer :full_gem_path unless instance_methods.include?(:full_gem_path=)
def full_gem_path
- if source.respond_to?(:path) || source.is_a?(Bundler::Plugin::API::Source)
+ # this cannot check source.is_a?(Bundler::Plugin::API::Source)
+ # because that _could_ trip the autoload, and if there are unresolved
+ # gems at that time, this method could be called inside another require,
+ # thus raising with that constant being undefined. Better to check a method
+ if source.respond_to?(:path) || (source.respond_to?(:bundler_plugin_api_source?) && source.bundler_plugin_api_source?)
Pathname.new(loaded_from).dirname.expand_path(source.root).to_s.untaint
else
rg_full_gem_path
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index 0aa9fd91d6..977e13d948 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -28,15 +28,27 @@ module Bundler
digest = Digest::SHA256.new
digest << io.read(16_384) until io.eof?
io.rewind
- digest.send(checksum_type(checksum))
+ send(checksum_type(checksum), digest)
end
unless digest == checksum
- raise SecurityError,
- "The checksum for the downloaded `#{spec.full_name}.gem` did not match " \
- "the checksum given by the API. This means that the contents of the " \
- "gem appear to be different from what was uploaded, and could be an indicator of a security issue.\n" \
- "(The expected SHA256 checksum was #{checksum.inspect}, but the checksum for the downloaded gem was #{digest.inspect}.)\n" \
- "Bundler cannot continue installing #{spec.name} (#{spec.version})."
+ raise SecurityError, <<-MESSAGE
+ Bundler cannot continue installing #{spec.name} (#{spec.version}).
+ The checksum for the downloaded `#{spec.full_name}.gem` does not match \
+ the checksum given by the server. This means the contents of the downloaded \
+ gem is different from what was uploaded to the server, and could be a potential security issue.
+
+ To resolve this issue:
+ 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem`
+ 2. run `bundle install`
+
+ 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:
+ 1. run `bundle config disable_checksum_validation true` to turn off checksum verification
+ 2. run `bundle install`
+
+ (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \
+ checksum for the downloaded gem was #{digest.inspect}.)
+ MESSAGE
end
true
end
@@ -48,5 +60,17 @@ module Bundler
else raise InstallError, "The given checksum for #{spec.full_name} (#{checksum.inspect}) is not a valid SHA256 hexdigest nor base64digest"
end
end
+
+ def hexdigest!(digest)
+ digest.hexdigest!
+ end
+
+ def base64digest!(digest)
+ if digest.respond_to?(:base64digest!)
+ digest.base64digest!
+ else
+ [digest.digest!].pack("m0")
+ end
+ end
end
end
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index c1bb6c7ab8..90768aac1d 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -71,8 +71,13 @@ module Bundler
spec.installed_by_version = Gem::Version.create(installed_by_version)
end
- def spec_missing_extensions?(spec)
- !spec.respond_to?(:missing_extensions?) || spec.missing_extensions?
+ def spec_missing_extensions?(spec, default = true)
+ return spec.missing_extensions? if spec.respond_to?(:missing_extensions?)
+
+ return false if spec.respond_to?(:default_gem?) && spec.default_gem?
+ return false if spec.extensions.empty?
+
+ default
end
def path(obj)
@@ -80,6 +85,7 @@ module Bundler
end
def platforms
+ return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform]
Gem.platforms
end
@@ -194,6 +200,10 @@ module Bundler
end
end
+ def load_plugins
+ Gem.load_plugins if Gem.respond_to?(:load_plugins)
+ end
+
def ui=(obj)
Gem::DefaultUserInteraction.ui = obj
end
@@ -203,6 +213,7 @@ module Bundler
end
def fetch_specs(all, pre, &blk)
+ require "rubygems/spec_fetcher"
specs = Gem::SpecFetcher.new.list(all, pre)
specs.each { yield } if block_given?
specs
@@ -243,6 +254,10 @@ module Bundler
end
end
+ def install_with_build_args(args)
+ with_build_args(args) { yield }
+ end
+
def gem_from_path(path, policy = nil)
require "rubygems/format"
Gem::Format.from_file_by_path(path, policy)
@@ -348,6 +363,10 @@ module Bundler
true
end
+
+ # TODO: delete this in 2.0, it's a backwards compatibility shim
+ # see https://github.com/bundler/bundler/issues/5102
+ kernel_class.send(:public, :gem)
end
end
@@ -397,6 +416,17 @@ module Bundler
spec
end
+ redefine_method(gem_class, :activate_bin_path) do |name, *args|
+ exec_name = args.first
+ return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle"
+
+ # Copy of Rubygems activate_bin_path impl
+ requirement = args.last
+ spec = find_spec_for_exe name, exec_name, [requirement]
+ Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate }
+ spec.bin_file exec_name
+ end
+
redefine_method(gem_class, :bin_path) do |name, *args|
exec_name = args.first
return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle"
@@ -489,6 +519,7 @@ module Bundler
end
def redefine_method(klass, method, unbound_method = nil, &block)
+ visibility = method_visibility(klass, method)
begin
if (instance_method = klass.instance_method(method)) && method != :initialize
# doing this to ensure we also get private methods
@@ -501,8 +532,20 @@ module Bundler
@replaced_methods[[method, klass]] = instance_method
if unbound_method
klass.send(:define_method, method, unbound_method)
+ klass.send(visibility, method)
elsif block
klass.send(:define_method, method, &block)
+ klass.send(visibility, method)
+ end
+ end
+
+ def method_visibility(klass, method)
+ if klass.private_method_defined?(method)
+ :private
+ elsif klass.protected_method_defined?(method)
+ :protected
+ else
+ :public
end
end
@@ -682,6 +725,10 @@ module Bundler
def repository_subdirectories
Gem::REPOSITORY_SUBDIRECTORIES
end
+
+ def install_with_build_args(args)
+ yield
+ end
end
# RubyGems 2.1.0
@@ -722,6 +769,14 @@ module Bundler
end.map(&:to_spec)
end
end
+
+ def use_gemdeps(gemfile)
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile)
+ runtime = Bundler.setup
+ Bundler.ui = nil
+ activated_spec_names = runtime.requested_specs.map(&:to_spec).sort_by(&:name)
+ [Gemdeps.new(runtime), activated_spec_names]
+ end
end
end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index c5fd46d440..7339f721ad 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -3,13 +3,20 @@ require "uri"
module Bundler
class Settings
+ autoload :Mirror, "bundler/mirror"
+ autoload :Mirrors, "bundler/mirror"
+
BOOL_KEYS = %w(
allow_offline_install
+ auto_install
cache_all
+ cache_all_platforms
disable_checksum_validation
disable_exec_load
disable_local_branch_check
disable_shared_gems
+ disable_version_check
+ force_ruby_platform
frozen
gem.coc
gem.mit
@@ -42,11 +49,18 @@ module Bundler
@local_config = load_config(local_config_file)
@global_config = load_config(global_config_file)
@cli_flags_given = false
+ @temporary = {}
end
def [](name)
key = key_for(name)
- value = (@local_config[key] || ENV[key] || @global_config[key] || DEFAULT_CONFIG[name])
+ value = @temporary.fetch(name) do
+ @local_config.fetch(key) do
+ ENV.fetch(key) do
+ @global_config.fetch(key) do
+ DEFAULT_CONFIG.fetch(name) do
+ nil
+ end end end end end
if value.nil?
nil
@@ -76,9 +90,19 @@ module Bundler
local_config_file || raise(GemfileNotFound, "Could not locate Gemfile")
set_key(key, value, @local_config, local_config_file)
end
-
alias_method :set_local, :[]=
+ def temporary(update)
+ existing = Hash[update.map {|k, _| [k, @temporary[k]] }]
+ @temporary.update(update)
+ return unless block_given?
+ begin
+ yield
+ ensure
+ existing.each {|k, v| v.nil? ? @temporary.delete(k) : @temporary[k] = v }
+ end
+ end
+
def delete(key)
@local_config.delete(key_for(key))
end
@@ -221,7 +245,12 @@ module Bundler
end
def to_bool(value)
- !(value.nil? || value == "" || value =~ /^(false|f|no|n|0)$/i || value == false)
+ case value
+ when nil, /\A(false|f|no|n|0|)\z/i, false
+ false
+ else
+ true
+ end
end
def is_num(value)
@@ -256,7 +285,11 @@ module Bundler
if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty?
Pathname.new(ENV["BUNDLE_CONFIG"])
else
- Bundler.user_bundle_path.join("config")
+ begin
+ Bundler.user_bundle_path.join("config")
+ rescue PermissionError, GenericSystemCallError
+ nil
+ end
end
end
@@ -279,10 +312,10 @@ module Bundler
}xo
def load_config(config_file)
- return unless config_file
+ return {} if !config_file || ignore_config?
SharedHelpers.filesystem_access(config_file, :read) do |file|
valid_file = file.exist? && !file.size.zero?
- return {} if ignore_config? || !valid_file
+ return {} unless valid_file
require "bundler/yaml_serializer"
YAMLSerializer.load file.read
end
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 4e213beed3..eb816b9988 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -22,7 +22,7 @@ if Bundler::SharedHelpers.in_bundle?
unless ENV["BUNDLE_POSTIT_TRAMPOLINING_VERSION"]
# Add bundler to the load path after disabling system gems
- # This is guarenteed to be done already if we've trampolined
+ # This is guaranteed to be done already if we've trampolined
bundler_lib = File.expand_path("../..", __FILE__)
$LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index ca4eafd623..0d68c85831 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -8,6 +8,7 @@ require "bundler/current_ruby"
module Gem
class Dependency
+ # This is only needed for RubyGems < 1.4
unless method_defined? :requirement
def requirement
version_requirements
@@ -18,8 +19,6 @@ end
module Bundler
module SharedHelpers
- attr_accessor :gem_loaded
-
def default_gemfile
gemfile = find_gemfile
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
@@ -39,10 +38,12 @@ module Bundler
bundle_dir = find_directory(".bundle")
return nil unless bundle_dir
- global_bundle_dir = File.join(Bundler.rubygems.user_home, ".bundle")
+ bundle_dir = Pathname.new(bundle_dir)
+
+ global_bundle_dir = Bundler.user_home.join(".bundle")
return nil if bundle_dir == global_bundle_dir
- Pathname.new(bundle_dir)
+ bundle_dir
end
def in_bundle?
@@ -101,16 +102,24 @@ module Bundler
# end
#
# @see {Bundler::PermissionError}
- def filesystem_access(path, action = :write)
- yield path.dup.untaint
+ def filesystem_access(path, action = :write, &block)
+ # Use block.call instead of yield because of a bug in Ruby 2.2.2
+ # See https://github.com/bundler/bundler/issues/5341 for details
+ block.call(path.dup.untaint)
rescue Errno::EACCES
raise PermissionError.new(path, action)
rescue Errno::EAGAIN
raise TemporaryResourceError.new(path, action)
rescue Errno::EPROTO
raise VirtualProtocolError.new
+ rescue Errno::ENOSPC
+ raise NoSpaceOnDeviceError.new(path, action)
rescue *[const_get_safely(:ENOTSUP, Errno)].compact
raise OperationNotSupportedError.new(path, action)
+ rescue Errno::EEXIST, Errno::ENOENT
+ raise
+ rescue SystemCallError => e
+ raise GenericSystemCallError.new(e, "There was an error accessing `#{path}`.")
end
def const_get_safely(constant_name, namespace)
@@ -202,24 +211,27 @@ module Bundler
def set_rubylib
rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
- rubylib.unshift File.expand_path("../..", __FILE__)
+ rubylib.unshift bundler_ruby_lib
ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
end
+ def bundler_ruby_lib
+ File.expand_path("../..", __FILE__)
+ end
+
def clean_load_path
# handle 1.9 where system gems are always on the load path
- if defined?(::Gem)
- me = File.expand_path("../../", __FILE__)
- me = /^#{Regexp.escape(me)}/
+ return unless defined?(::Gem)
- loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
+ bundler_lib = bundler_ruby_lib
- $LOAD_PATH.reject! do |p|
- next if File.expand_path(p) =~ me
- loaded_gem_paths.delete(p)
- end
- $LOAD_PATH.uniq!
+ loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
+
+ $LOAD_PATH.reject! do |p|
+ next if File.expand_path(p).start_with?(bundler_lib)
+ loaded_gem_paths.delete(p)
end
+ $LOAD_PATH.uniq!
end
def prints_major_deprecations?
@@ -234,7 +246,7 @@ module Bundler
def deprecate_gemfile(gemfile)
return unless gemfile && File.basename(gemfile) == "Gemfile"
Bundler::SharedHelpers.major_deprecation \
- "gems.rb and gems.locked will be prefered to Gemfile and Gemfile.lock."
+ "gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock."
end
extend self
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index b6f3a4311d..cf56ed1cc1 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -14,13 +14,13 @@ module Bundler
def version_message(spec)
message = "#{spec.name} #{spec.version}"
- message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY
+ message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY && !spec.platform.nil?
if Bundler.locked_gems
locked_spec = Bundler.locked_gems.specs.find {|s| s.name == spec.name }
locked_spec_version = locked_spec.version if locked_spec
if locked_spec_version && spec.version != locked_spec_version
- message += " (#{Bundler.ui.add_color("was #{locked_spec_version}", :green)})"
+ message += Bundler.ui.add_color(" (was #{locked_spec_version})", version_color(spec.version, locked_spec_version))
end
end
@@ -34,5 +34,25 @@ module Bundler
def include?(other)
other == self
end
+
+ def inspect
+ "#<#{self.class}:0x#{object_id} #{self}>"
+ end
+
+ private
+
+ def version_color(spec_version, locked_spec_version)
+ if Gem::Version.correct?(spec_version) && Gem::Version.correct?(locked_spec_version)
+ # display yellow if there appears to be a regression
+ earlier_version?(spec_version, locked_spec_version) ? :yellow : :green
+ else
+ # default to green if the versions cannot be directly compared
+ :green
+ end
+ end
+
+ def earlier_version?(spec_version, locked_spec_version)
+ Gem::Version.new(spec_version) < Gem::Version.new(locked_spec_version)
+ end
end
end
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index d1757a4a93..30ff27c446 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -161,7 +161,9 @@ module Bundler
local_specs
end
- def install(spec, force = false)
+ def install(spec, options = {})
+ force = options[:force]
+
Bundler.ui.info "Using #{version_message(spec)} from #{self}"
if requires_checkout? && !@copied && !force
@@ -170,7 +172,8 @@ module Bundler
serialize_gemspecs_in(install_path)
@copied = true
end
- generate_bin(spec, !Bundler.rubygems.spec_missing_extensions?(spec))
+ generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] }
+ generate_bin(spec, generate_bin_options)
requires_checkout? ? spec.post_install_message : nil
end
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index c44f00d7b1..e9b9c4dbe4 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+require "shellwords"
require "tempfile"
module Bundler
class Source
@@ -180,7 +181,7 @@ module Bundler
def find_local_revision
allowed_in_path do
- git("rev-parse --verify #{ref}", true).strip
+ git("rev-parse --verify #{Shellwords.shellescape(ref)}", true).strip
end
end
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 69bb0c1af2..2900b3f939 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -4,10 +4,12 @@ module Bundler
class Path < Source
autoload :Installer, "bundler/source/path/installer"
- attr_reader :path, :options, :root_path
+ attr_reader :path, :options, :root_path, :original_path
attr_writer :name
attr_accessor :version
+ protected :original_path
+
DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze
def initialize(options)
@@ -46,7 +48,7 @@ module Bundler
def to_lock
out = String.new("PATH\n")
- out << " remote: #{relative_path}\n"
+ out << " remote: #{lockfile_path}\n"
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
out << " specs:\n"
end
@@ -61,7 +63,7 @@ module Bundler
def eql?(other)
return unless other.class == self.class
- expanded_path == expand(other.path) &&
+ expand(@original_path) == expand(other.original_path) &&
version == other.version
end
@@ -71,9 +73,9 @@ module Bundler
File.basename(expanded_path.to_s)
end
- def install(spec, force = false)
+ def install(spec, options = {})
Bundler.ui.info "Using #{version_message(spec)} from #{self}"
- generate_bin(spec, :disable_extensions)
+ generate_bin(spec, :disable_extensions => true)
nil # no post-install message
end
@@ -129,6 +131,11 @@ module Bundler
"`#{somepath}`.\nThe error message was: #{e.message}."
end
+ def lockfile_path
+ return relative_path(original_path) if original_path.absolute?
+ expand(original_path).relative_path_from(Bundler.root)
+ end
+
def app_cache_path(custom_path = nil)
@app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
end
@@ -181,14 +188,14 @@ module Bundler
index
end
- def relative_path
+ def relative_path(path = self.path)
if path.to_s.start_with?(root_path.to_s)
return path.relative_path_from(root_path)
end
path
end
- def generate_bin(spec, disable_extensions = false)
+ def generate_bin(spec, options = {})
gem_dir = Pathname.new(spec.full_gem_path)
# Some gem authors put absolute paths in their gemspec
@@ -203,7 +210,12 @@ module Bundler
end
end.compact
- installer = Path::Installer.new(spec, :env_shebang => false, :disable_extensions => disable_extensions)
+ installer = Path::Installer.new(
+ spec,
+ :env_shebang => false,
+ :disable_extensions => options[:disable_extensions],
+ :build_args => options[:build_args]
+ )
installer.post_install
rescue Gem::InvalidSpecificationException => e
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb
index abc46d5a04..9c2f74a31b 100644
--- a/lib/bundler/source/path/installer.rb
+++ b/lib/bundler/source/path/installer.rb
@@ -13,7 +13,7 @@ module Bundler
@format_executable = options[:format_executable] || false
@build_args = options[:build_args] || Bundler.rubygems.build_args
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
- @disable_extentions = options[:disable_extensions]
+ @disable_extensions = options[:disable_extensions]
if Bundler.requires_sudo?
@tmp_dir = Bundler.tmp(spec.full_name).to_s
@@ -27,7 +27,7 @@ module Bundler
SharedHelpers.chdir(@gem_dir) do
run_hooks(:pre_install)
- unless @disable_extentions
+ unless @disable_extensions
build_extensions
run_hooks(:post_build)
end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 89f7673eb8..8c592cb727 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "uri"
require "rubygems/user_interaction"
-require "rubygems/spec_fetcher"
module Bundler
class Source
@@ -141,6 +140,7 @@ module Bundler
:ignore_dependencies => true,
:wrappers => true,
:env_shebang => true,
+ :build_args => opts[:build_args],
:bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum
).install
end
@@ -292,6 +292,10 @@ module Bundler
next if spec.name == "bundler" && spec.version.to_s != VERSION
have_bundler = true if spec.name == "bundler"
spec.source = self
+ if Bundler.rubygems.spec_missing_extensions?(spec, false)
+ Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions"
+ next
+ end
idx << spec
end
@@ -322,6 +326,10 @@ module Bundler
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
+ if Bundler.rubygems.spec_missing_extensions?(s, false)
+ Bundler.ui.debug "Source #{self} is ignoring #{s} because it is missing extensions"
+ next
+ end
idx << s
end
end
@@ -402,6 +410,7 @@ module Bundler
return false unless spec.remote
uri = spec.remote.uri
spec.fetch_platform
+ Bundler.ui.confirm("Fetching #{version_message(spec)}")
download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir
gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem"
diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb
index 92f8a40588..b49e645506 100644
--- a/lib/bundler/source/rubygems/remote.rb
+++ b/lib/bundler/source/rubygems/remote.rb
@@ -30,6 +30,10 @@ module Bundler
end
end
+ def to_s
+ "rubygems remote at #{anonymized_uri}"
+ end
+
private
def apply_auth(uri, auth)
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index fe31b17f0e..f3826beaa5 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -20,8 +20,8 @@ module Bundler
specs = []
skip += ["bundler"]
- until deps.empty?
- dep = deps.shift
+ loop do
+ break unless dep = deps.shift
next if handled[dep] || skip.include?(dep.name)
handled[dep] = true
@@ -114,6 +114,17 @@ module Bundler
SpecSet.new(arr)
end
+ def find_by_name_and_platform(name, platform)
+ @specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
+ end
+
+ def what_required(spec)
+ unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } }
+ return [spec]
+ end
+ what_required(req) << spec
+ end
+
private
def sorted
@@ -151,14 +162,15 @@ module Bundler
end
def spec_for_dependency(dep, match_current_platform)
+ specs_for_platforms = lookup[dep.name]
if match_current_platform
Bundler.rubygems.platforms.reverse_each do |pl|
- match = GemHelpers.select_best_platform_match(lookup[dep.name], pl)
+ match = GemHelpers.select_best_platform_match(specs_for_platforms, pl)
return match if match
end
nil
else
- GemHelpers.select_best_platform_match(lookup[dep.name], dep.__platform)
+ GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform)
end
end
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
index f4ee7d0644..cbcadee269 100644
--- a/lib/bundler/stub_specification.rb
+++ b/lib/bundler/stub_specification.rb
@@ -15,6 +15,13 @@ module Bundler
_remote_specification.to_yaml
end
+ if Bundler.rubygems.provides?(">= 2.3")
+ # This is defined directly to avoid having to load every installed spec
+ def missing_extensions?
+ stub.missing_extensions?
+ end
+ end
+
private
def _remote_specification
diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable
index b72c267dd1..fe22de0a6d 100755
--- a/lib/bundler/templates/Executable
+++ b/lib/bundler/templates/Executable
@@ -1,4 +1,4 @@
-#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
+#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
# frozen_string_literal: true
#
# This file was generated by Bundler.
diff --git a/lib/bundler/templates/Executable.standalone b/lib/bundler/templates/Executable.standalone
index c114afe337..4bf0753f44 100644
--- a/lib/bundler/templates/Executable.standalone
+++ b/lib/bundler/templates/Executable.standalone
@@ -1,4 +1,4 @@
-#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
+#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
#
# This file was generated by Bundler.
#
@@ -6,9 +6,9 @@
# this file is here to facilitate running it.
#
-require 'pathname'
+require "pathname"
path = Pathname.new(__FILE__)
-$:.unshift File.expand_path '../<%= standalone_path %>', path.realpath
+$:.unshift File.expand_path "../<%= standalone_path %>", path.realpath
-require 'bundler/setup'
-load File.expand_path '../<%= executable_path %>', path.realpath
+require "bundler/setup"
+load File.expand_path "../<%= executable_path %>", path.realpath
diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt
index d24b8525da..4cd2e40f4f 100644
--- a/lib/bundler/templates/newgem/Gemfile.tt
+++ b/lib/bundler/templates/newgem/Gemfile.tt
@@ -1,4 +1,4 @@
-source 'https://rubygems.org'
+source "https://rubygems.org"
-# Specify your gem's dependencies in <%=config[:name]%>.gemspec
+# Specify your gem's dependencies in <%= config[:name] %>.gemspec
gemspec
diff --git a/lib/bundler/templates/newgem/LICENSE.txt.tt b/lib/bundler/templates/newgem/LICENSE.txt.tt
index 8fef84cd53..76ef4b0191 100644
--- a/lib/bundler/templates/newgem/LICENSE.txt.tt
+++ b/lib/bundler/templates/newgem/LICENSE.txt.tt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) <%=Time.now.year%> <%=config[:author]%>
+Copyright (c) <%= Time.now.year %> <%= config[:author] %>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
index ad8d88b6e4..6eb270a124 100644
--- a/lib/bundler/templates/newgem/README.md.tt
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -1,6 +1,6 @@
-# <%=config[:constant_name]%>
+# <%= config[:constant_name] %>
-Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%=config[:namespaced_path]%>`. To experiment with that code, run `bin/console` for an interactive prompt.
+Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
TODO: Delete this and the text above, and describe your gem
@@ -9,7 +9,7 @@ TODO: Delete this and the text above, and describe your gem
Add this line to your application's Gemfile:
```ruby
-gem '<%=config[:name]%>'
+gem '<%= config[:name] %>'
```
And then execute:
@@ -18,7 +18,7 @@ And then execute:
Or install it yourself as:
- $ gem install <%=config[:name]%>
+ $ gem install <%= config[:name] %>
## Usage
@@ -32,10 +32,16 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
## Contributing
-Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:git_user_name] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.<% end %>
-<% if config[:mit] %>
+Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.<% end %>
+<% if config[:mit] -%>
## License
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
-<% end %>
+<% end -%>
+
+<% if config[:coc] -%>
+## Code of Conduct
+
+Everyone interacting in the <%= config[:constant_name] %> project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md).
+<% end -%>
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index 8beb91aee6..099da6f3ec 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -1,14 +1,14 @@
require "bundler/gem_tasks"
-<% if config[:test] == 'minitest' -%>
+<% if config[:test] == "minitest" -%>
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
- t.test_files = FileList['test/**/*_test.rb']
+ t.test_files = FileList["test/**/*_test.rb"]
end
-<% elsif config[:test] == 'rspec' -%>
+<% elsif config[:test] == "rspec" -%>
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
@@ -19,8 +19,8 @@ require "rake/extensiontask"
task :build => :compile
-Rake::ExtensionTask.new("<%=config[:underscored_name]%>") do |ext|
- ext.lib_dir = "lib/<%=config[:namespaced_path]%>"
+Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
+ ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
end
task :default => [:clobber, :compile, :<%= config[:test_task] %>]
diff --git a/lib/bundler/templates/newgem/bin/console.tt b/lib/bundler/templates/newgem/bin/console.tt
index f402bd639e..a27f82430f 100644
--- a/lib/bundler/templates/newgem/bin/console.tt
+++ b/lib/bundler/templates/newgem/bin/console.tt
@@ -11,4 +11,4 @@ require "<%= config[:namespaced_path] %>"
# Pry.start
require "irb"
-IRB.start
+IRB.start(__FILE__)
diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
index 5dad3641ff..8177c4d202 100644
--- a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
@@ -1,9 +1,9 @@
-#include "<%=config[:underscored_name]%>.h"
+#include "<%= config[:underscored_name] %>.h"
-VALUE rb_m<%=config[:constant_array].join%>;
+VALUE rb_m<%= config[:constant_array].join %>;
void
-Init_<%=config[:underscored_name]%>(void)
+Init_<%= config[:underscored_name] %>(void)
{
- rb_m<%=config[:constant_array].join%> = rb_define_module(<%=config[:constant_name].inspect%>);
+ rb_m<%= config[:constant_array].join %> = rb_define_module(<%= config[:constant_name].inspect %>);
}
diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
index 960fdfb492..c6e420b66e 100644
--- a/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
@@ -1,6 +1,6 @@
-#ifndef <%=config[:underscored_name].upcase%>_H
-#define <%=config[:underscored_name].upcase%>_H 1
+#ifndef <%= config[:underscored_name].upcase %>_H
+#define <%= config[:underscored_name].upcase %>_H 1
#include "ruby.h"
-#endif /* <%=config[:underscored_name].upcase%>_H */
+#endif /* <%= config[:underscored_name].upcase %>_H */
diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt
index ebff7ac53c..573d76b4c2 100644
--- a/lib/bundler/templates/newgem/gitignore.tt
+++ b/lib/bundler/templates/newgem/gitignore.tt
@@ -14,3 +14,8 @@
*.a
mkmf.log
<%- end -%>
+<%- if config[:test] == "rspec" -%>
+
+# rspec failure tracking
+.rspec_status
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt
index b3f816bac6..7d8ad90ab0 100644
--- a/lib/bundler/templates/newgem/lib/newgem.rb.tt
+++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt
@@ -1,12 +1,12 @@
-require "<%=config[:namespaced_path]%>/version"
+require "<%= config[:namespaced_path] %>/version"
<%- if config[:ext] -%>
-require "<%=config[:namespaced_path]%>/<%=config[:underscored_name]%>"
+require "<%= config[:namespaced_path] %>/<%= config[:underscored_name] %>"
<%- end -%>
-<%- config[:constant_array].each_with_index do |c,i| -%>
-<%= ' '*i %>module <%= c %>
+<%- config[:constant_array].each_with_index do |c, i| -%>
+<%= " " * i %>module <%= c %>
<%- end -%>
-<%= ' '*config[:constant_array].size %># Your code goes here...
+<%= " " * config[:constant_array].size %># Your code goes here...
<%- (config[:constant_array].size-1).downto(0) do |i| -%>
-<%= ' '*i %>end
+<%= " " * i %>end
<%- end -%>
diff --git a/lib/bundler/templates/newgem/lib/newgem/version.rb.tt b/lib/bundler/templates/newgem/lib/newgem/version.rb.tt
index 5874085d61..389daf5048 100644
--- a/lib/bundler/templates/newgem/lib/newgem/version.rb.tt
+++ b/lib/bundler/templates/newgem/lib/newgem/version.rb.tt
@@ -1,7 +1,7 @@
-<%- config[:constant_array].each_with_index do |c,i| -%>
-<%= ' '*i %>module <%= c %>
+<%- config[:constant_array].each_with_index do |c, i| -%>
+<%= " " * i %>module <%= c %>
<%- end -%>
-<%= ' '*config[:constant_array].size %>VERSION = "0.1.0"
+<%= " " * config[:constant_array].size %>VERSION = "0.1.0"
<%- (config[:constant_array].size-1).downto(0) do |i| -%>
-<%= ' '*i %>end
+<%= " " * i %>end
<%- end -%>
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index 9d6d491dce..caea7fe7be 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -1,13 +1,13 @@
# coding: utf-8
-lib = File.expand_path('../lib', __FILE__)
+lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
-require '<%=config[:namespaced_path]%>/version'
+require "<%= config[:namespaced_path] %>/version"
Gem::Specification.new do |spec|
- spec.name = <%=config[:name].inspect%>
- spec.version = <%=config[:constant_name]%>::VERSION
- spec.authors = [<%=config[:author].inspect%>]
- spec.email = [<%=config[:email].inspect%>]
+ spec.name = <%= config[:name].inspect %>
+ spec.version = <%= config[:constant_name] %>::VERSION
+ spec.authors = [<%= config[:author].inspect %>]
+ spec.email = [<%= config[:email].inspect %>]
spec.summary = %q{TODO: Write a short summary, because Rubygems requires one.}
spec.description = %q{TODO: Write a longer description or delete this line.}
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
# to allow pushing to a single host or delete this section to allow pushing to any host.
if spec.respond_to?(:metadata)
- spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
+ spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
else
raise "RubyGems 2.0 or newer is required to protect against " \
"public gem pushes."
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
<%- if config[:ext] -%>
- spec.extensions = ["ext/<%=config[:underscored_name]%>/extconf.rb"]
+ spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
<%- end -%>
spec.add_development_dependency "bundler", "~> <%= config[:bundler_version] %>"
@@ -41,6 +41,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "rake-compiler"
<%- end -%>
<%- if config[:test] -%>
- spec.add_development_dependency "<%=config[:test]%>", "~> <%=config[:test_framework_version]%>"
+ spec.add_development_dependency "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>"
<%- end -%>
end
diff --git a/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt b/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
index b33d65188a..b7ef7f9e4a 100644
--- a/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
+++ b/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
@@ -1,6 +1,6 @@
require "spec_helper"
-describe <%= config[:constant_name] %> do
+RSpec.describe <%= config[:constant_name] %> do
it "has a version number" do
expect(<%= config[:constant_name] %>::VERSION).not_to be nil
end
diff --git a/lib/bundler/templates/newgem/spec/spec_helper.rb.tt b/lib/bundler/templates/newgem/spec/spec_helper.rb.tt
index 4c69d354b3..805cf57e01 100644
--- a/lib/bundler/templates/newgem/spec/spec_helper.rb.tt
+++ b/lib/bundler/templates/newgem/spec/spec_helper.rb.tt
@@ -1,2 +1,14 @@
-$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
+require "bundler/setup"
require "<%= config[:namespaced_path] %>"
+
+RSpec.configure do |config|
+ # Enable flags like --only-failures and --next-failure
+ config.example_status_persistence_file_path = ".rspec_status"
+
+ # Disable RSpec exposing methods globally on `Module` and `main`
+ config.disable_monkey_patching!
+
+ config.expect_with :rspec do |c|
+ c.syntax = :expect
+ end
+end
diff --git a/lib/bundler/templates/newgem/test/newgem_test.rb.tt b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
index 95e33a34ea..f2af9f90e0 100644
--- a/lib/bundler/templates/newgem/test/newgem_test.rb.tt
+++ b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
@@ -1,4 +1,4 @@
-require 'test_helper'
+require "test_helper"
class <%= config[:constant_name] %>Test < Minitest::Test
def test_that_it_has_a_version_number
diff --git a/lib/bundler/templates/newgem/test/test_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt
index 49a56c1800..725e3e4647 100644
--- a/lib/bundler/templates/newgem/test/test_helper.rb.tt
+++ b/lib/bundler/templates/newgem/test/test_helper.rb.tt
@@ -1,4 +1,4 @@
-$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
-require '<%= config[:namespaced_path] %>'
+$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
+require "<%= config[:namespaced_path] %>"
-require 'minitest/autorun'
+require "minitest/autorun"
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
index 5c1fa61568..697290f795 100644
--- a/lib/bundler/ui/shell.rb
+++ b/lib/bundler/ui/shell.rb
@@ -83,6 +83,10 @@ module Bundler
with_level("silent", &blk)
end
+ def unprinted_warnings
+ []
+ end
+
private
# valimism
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
index 367eaa58c2..48390b7198 100644
--- a/lib/bundler/ui/silent.rb
+++ b/lib/bundler/ui/silent.rb
@@ -2,6 +2,12 @@
module Bundler
module UI
class Silent
+ attr_writer :shell
+
+ def initialize
+ @warnings = []
+ end
+
def add_color(string, color)
string
end
@@ -13,6 +19,7 @@ module Bundler
end
def warn(message, newline = nil)
+ @warnings |= [message]
end
def error(message, newline = nil)
@@ -32,18 +39,30 @@ module Bundler
def ask(message)
end
+ def yes?(msg)
+ raise "Cannot ask yes? with a silent shell"
+ end
+
+ def no?
+ raise "Cannot ask no? with a silent shell"
+ end
+
def level=(name)
end
def level(name = nil)
end
- def trace(message, newline = nil)
+ def trace(message, newline = nil, force = false)
end
def silence
yield
end
+
+ def unprinted_warnings
+ @warnings
+ end
end
end
end
diff --git a/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb b/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb
deleted file mode 100644
index 9ab2722f18..0000000000
--- a/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-require "pathname"
-require "set"
-
-class Bundler::CompactIndexClient
- class Error < StandardError; end
-
- require "bundler/vendor/compact_index_client/lib/compact_index_client/cache"
- require "bundler/vendor/compact_index_client/lib/compact_index_client/updater"
- require "bundler/vendor/compact_index_client/lib/compact_index_client/version"
-
- attr_reader :directory
-
- # @return [Lambda] A lambda that takes an array of inputs and a block, and
- # maps the inputs with the block in parallel.
- #
- attr_accessor :in_parallel
-
- def initialize(directory, fetcher)
- @directory = Pathname.new(directory)
- @updater = Updater.new(fetcher)
- @cache = Cache.new(@directory)
- @endpoints = Set.new
- @info_checksums_by_name = {}
- @in_parallel = lambda do |inputs, &blk|
- inputs.map(&blk)
- end
- end
-
- def names
- update(@cache.names_path, "names")
- @cache.names
- end
-
- def versions
- update(@cache.versions_path, "versions")
- versions, @info_checksums_by_name = @cache.versions
- versions
- end
-
- def dependencies(names)
- in_parallel.call(names) do |name|
- update_info(name)
- @cache.dependencies(name).map {|d| d.unshift(name) }
- end.flatten(1)
- end
-
- def spec(name, version, platform = nil)
- update_info(name)
- @cache.specific_dependency(name, version, platform)
- end
-
- def update_and_parse_checksums!
- return @info_checksums_by_name if @parsed_checksums
- update(@cache.versions_path, "versions")
- @info_checksums_by_name = @cache.checksums
- @parsed_checksums = true
- end
-
-private
-
- def update(local_path, remote_path)
- return unless @endpoints.add?(remote_path)
- @updater.update(local_path, url(remote_path))
- end
-
- def update_info(name)
- path = @cache.info_path(name)
- checksum = @updater.checksum_for_file(path)
- return unless existing = @info_checksums_by_name[name]
- return if checksum == existing
- update(path, "info/#{name}")
- end
-
- def url(path)
- path
- end
-end
diff --git a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb b/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb
deleted file mode 100644
index d2639ee717..0000000000
--- a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-require "digest/md5"
-class Bundler::CompactIndexClient
- class Cache
- attr_reader :directory
-
- def initialize(directory)
- @directory = Pathname.new(directory).expand_path
- info_roots.each {|dir| FileUtils.mkdir_p(dir) }
- end
-
- def names
- lines(names_path)
- end
-
- def names_path
- directory.join("names")
- end
-
- def versions
- versions_by_name = Hash.new {|hash, key| hash[key] = [] }
- info_checksums_by_name = {}
-
- lines(versions_path).each do |line|
- name, versions_string, info_checksum = line.split(" ", 3)
- info_checksums_by_name[name] = info_checksum || ""
- versions_string.split(",").each do |version|
- if version.start_with?("-")
- version = version[1..-1].split("-", 2).unshift(name)
- versions_by_name[name].delete(version)
- else
- version = version.split("-", 2).unshift(name)
- versions_by_name[name] << version
- end
- end
- end
-
- [versions_by_name, info_checksums_by_name]
- end
-
- def versions_path
- directory.join("versions")
- end
-
- def checksums
- checksums = {}
-
- lines(versions_path).each do |line|
- name, _, checksum = line.split(" ", 3)
- checksums[name] = checksum
- end
-
- checksums
- end
-
- def dependencies(name)
- lines(info_path(name)).map do |line|
- parse_gem(line)
- end
- end
-
- def info_path(name)
- name = name.to_s
- if name =~ /[^a-z0-9_-]/
- name += "-#{Digest::MD5.hexdigest(name).downcase}"
- info_roots.last.join(name)
- else
- info_roots.first.join(name)
- end
- end
-
- def specific_dependency(name, version, platform)
- pattern = [version, platform].compact.join("-")
- return nil if pattern.empty?
-
- gem_lines = info_path(name).read
- gem_line = gem_lines[/^#{Regexp.escape(pattern)}\b.*/, 0]
- gem_line ? parse_gem(gem_line) : nil
- end
-
- private
-
- def lines(path)
- return [] unless path.file?
- lines = path.read.split("\n")
- header = lines.index("---")
- lines = header ? lines[header + 1..-1] : lines
- end
-
- def parse_gem(string)
- version_and_platform, rest = string.split(" ", 2)
- version, platform = version_and_platform.split("-", 2)
- dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
- dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
- requirements = requirements ? requirements.map {|r| parse_dependency(r) } : []
- [version, platform, dependencies, requirements]
- end
-
- def parse_dependency(string)
- dependency = string.split(":")
- dependency[-1] = dependency[-1].split("&") if dependency.size > 1
- dependency
- end
-
- def info_roots
- [
- directory.join("info"),
- directory.join("info-special-characters"),
- ]
- end
- end
-end
diff --git a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb b/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb
deleted file mode 100644
index 5c5ba41434..0000000000
--- a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-require "fileutils"
-require "stringio"
-require "tmpdir"
-require "zlib"
-
-class Bundler::CompactIndexClient
- class Updater
- class MisMatchedChecksumError < Error
- def initialize(path, server_checksum, local_checksum)
- @path = path
- @server_checksum = server_checksum
- @local_checksum = local_checksum
- end
-
- def message
- "The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
- "(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
- end
- end
-
- def initialize(fetcher)
- @fetcher = fetcher
- end
-
- def update(local_path, remote_path, retrying = nil)
- headers = {}
-
- Dir.mktmpdir(local_path.basename.to_s, local_path.dirname) do |local_temp_dir|
- local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)
-
- # download new file if retrying
- if retrying.nil? && local_path.file?
- FileUtils.cp local_path, local_temp_path
- headers["If-None-Match"] = etag_for(local_temp_path)
- headers["Range"] = "bytes=#{local_temp_path.size}-"
- else
- # Fastly ignores Range when Accept-Encoding: gzip is set
- headers["Accept-Encoding"] = "gzip"
- end
-
- response = @fetcher.call(remote_path, headers)
- return if response.is_a?(Net::HTTPNotModified)
-
- content = response.body
- if response["Content-Encoding"] == "gzip"
- content = Zlib::GzipReader.new(StringIO.new(content)).read
- end
-
- mode = response.is_a?(Net::HTTPPartialContent) ? "a" : "w"
- local_temp_path.open(mode) {|f| f << content }
-
- response_etag = response["ETag"]
- if etag_for(local_temp_path) == response_etag
- FileUtils.mv(local_temp_path, local_path)
- return
- end
-
- if retrying.nil?
- update(local_path, remote_path, :retrying)
- else
- raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path))
- end
- end
- end
-
- def etag_for(path)
- sum = checksum_for_file(path)
- sum ? %("#{sum}") : nil
- end
-
- def checksum_for_file(path)
- return nil unless path.file?
- # This must use IO.read instead of Digest.file().hexdigest
- # because we need to preserve \n line endings on windows when calculating
- # the checksum
- Digest::MD5.hexdigest(IO.read(path))
- end
- end
-end
diff --git a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb b/lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb
deleted file mode 100644
index 64520daead..0000000000
--- a/lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-class Bundler::CompactIndexClient
- VERSION = "0.1.0".freeze
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
index ce5c5efa46..76e84ab7e6 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
@@ -98,18 +98,27 @@ module Bundler::Molinillo
"#{self.class}:#{vertices.values.inspect}"
end
+ # @param [Hash] options options for dot output.
# @return [String] Returns a dot format representation of the graph
- def to_dot
+ def to_dot(options = {})
+ edge_label = options.delete(:edge_label)
+ raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
+
dot_vertices = []
dot_edges = []
vertices.each do |n, v|
dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
v.outgoing_edges.each do |e|
- dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=\"#{e.requirement}\"]"
+ label = edge_label ? edge_label.call(e) : e.requirement
+ dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
end
end
+
+ dot_vertices.uniq!
dot_vertices.sort!
+ dot_edges.uniq!
dot_edges.sort!
+
dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
dot.join("\n")
end
@@ -119,10 +128,12 @@ module Bundler::Molinillo
# {Vertex#successors}
def ==(other)
return false unless other
+ return true if equal?(other)
vertices.each do |name, vertex|
other_vertex = other.vertex_named(name)
return false unless other_vertex
- return false unless other_vertex.successors.map(&:name).to_set == vertex.successors.map(&:name).to_set
+ return false unless vertex.payload == other_vertex.payload
+ return false unless other_vertex.successors.to_set == vertex.successors.to_set
end
end
@@ -134,6 +145,7 @@ module Bundler::Molinillo
def add_child_vertex(name, payload, parent_names, requirement)
root = !parent_names.delete(nil) { true }
vertex = add_vertex(name, payload, root)
+ vertex.explicit_requirements << requirement if root
parent_names.each do |parent_name|
parent_node = vertex_named(parent_name)
add_edge(parent_node, vertex, requirement)
@@ -152,7 +164,7 @@ module Bundler::Molinillo
# Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
# removing any non-root vertices that were orphaned in the process
# @param [String] name
- # @return [void]
+ # @return [Array<Vertex>] the vertices which have been detached
def detach_vertex_named(name)
log.detach_vertex_named(self, name)
end
@@ -182,6 +194,13 @@ module Bundler::Molinillo
add_edge_no_circular(origin, destination, requirement)
end
+ # Deletes an {Edge} from the dependency graph
+ # @param [Edge] edge
+ # @return [Void]
+ def delete_edge(edge)
+ log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
+ end
+
# Sets the payload of the vertex with the given name
# @param [String] name the name of the vertex
# @param [Object] payload the payload
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
index c8eacbe08f..e0dfe6cbbd 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
@@ -7,7 +7,7 @@ module Bundler::Molinillo
# rubocop:disable Lint/UnusedMethodArgument
# @return [Symbol] The name of the action.
- def self.name
+ def self.action_name
raise 'Abstract'
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
index a7e703a8f9..9092e4d546 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -7,8 +7,8 @@ module Bundler::Molinillo
class AddEdgeNoCircular < Action
# @!group Action
- # (see Action.name)
- def self.name
+ # (see Action.action_name)
+ def self.action_name
:add_vertex
end
@@ -23,8 +23,8 @@ module Bundler::Molinillo
# (see Action#down)
def down(graph)
edge = make_edge(graph)
- edge.origin.outgoing_edges.delete(edge)
- edge.destination.incoming_edges.delete(edge)
+ delete_first(edge.origin.outgoing_edges, edge)
+ delete_first(edge.destination.incoming_edges, edge)
end
# @!group AddEdgeNoCircular
@@ -53,6 +53,13 @@ module Bundler::Molinillo
@destination = destination
@requirement = requirement
end
+
+ private
+
+ def delete_first(array, item)
+ return unless index = array.index(item)
+ array.delete_at(index)
+ end
end
end
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
index 14cd027804..eda4251801 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
@@ -7,8 +7,8 @@ module Bundler::Molinillo
class AddVertex < Action # :nodoc:
# @!group Action
- # (see Action.name)
- def self.name
+ # (see Action.action_name)
+ def self.action_name
:add_vertex
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
new file mode 100644
index 0000000000..e9125a59c6
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action'
+module Bundler::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # (see DependencyGraph#delete_edge)
+ class DeleteEdge < Action
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :delete_edge
+ end
+
+ # (see Action#up)
+ def up(graph)
+ edge = make_edge(graph)
+ edge.origin.outgoing_edges.delete(edge)
+ edge.destination.incoming_edges.delete(edge)
+ end
+
+ # (see Action#down)
+ def down(graph)
+ edge = make_edge(graph)
+ edge.origin.outgoing_edges << edge
+ edge.destination.incoming_edges << edge
+ edge
+ end
+
+ # @!group DeleteEdge
+
+ # @return [String] the name of the origin of the edge
+ attr_reader :origin_name
+
+ # @return [String] the name of the destination of the edge
+ attr_reader :destination_name
+
+ # @return [Object] the requirement that the edge represents
+ attr_reader :requirement
+
+ # @param [DependencyGraph] graph the graph to find vertices from
+ # @return [Edge] The edge this action adds
+ def make_edge(graph)
+ Edge.new(
+ graph.vertex_named(origin_name),
+ graph.vertex_named(destination_name),
+ requirement
+ )
+ end
+
+ # Initialize an action to add an edge to a dependency graph
+ # @param [String] origin_name the name of the origin of the edge
+ # @param [String] destination_name the name of the destination of the edge
+ # @param [Object] requirement the requirement that the edge represents
+ def initialize(origin_name, destination_name, requirement)
+ @origin_name = origin_name
+ @destination_name = destination_name
+ @requirement = requirement
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
index 78c0da67ef..d20b2cb0e0 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -8,22 +8,29 @@ module Bundler::Molinillo
# @!group Action
# (see Action#name)
- def self.name
+ def self.action_name
:add_vertex
end
# (see Action#up)
def up(graph)
- return unless @vertex = graph.vertices.delete(name)
+ return [] unless @vertex = graph.vertices.delete(name)
+
+ removed_vertices = [@vertex]
@vertex.outgoing_edges.each do |e|
v = e.destination
v.incoming_edges.delete(e)
- graph.detach_vertex_named(v.name) unless v.root? || v.predecessors.any?
+ if !v.root? && v.incoming_edges.empty?
+ removed_vertices.concat graph.detach_vertex_named(v.name)
+ end
end
+
@vertex.incoming_edges.each do |e|
v = e.origin
v.outgoing_edges.delete(e)
end
+
+ removed_vertices
end
# (see Action#down)
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
index 863b4912be..72a705e023 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular'
require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge'
require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named'
require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload'
require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag'
@@ -40,6 +41,16 @@ module Bundler::Molinillo
push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
end
+ # {include:DependencyGraph#delete_edge}
+ # @param [Graph] graph the graph to perform the action on
+ # @param [String] origin_name
+ # @param [String] destination_name
+ # @param [Object] requirement
+ # @return (see DependencyGraph#delete_edge)
+ def delete_edge(graph, origin_name, destination_name, requirement)
+ push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement))
+ end
+
# @macro action
def set_payload(graph, name, payload)
push_action(graph, SetPayload.new(name, payload))
@@ -92,7 +103,7 @@ module Bundler::Molinillo
loop do
action = pop!(graph)
raise "No tag #{tag.inspect} found" unless action
- break if action.class.name == :tag && action.tag == tag
+ break if action.class.action_name == :tag && action.tag == tag
end
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
index f2fe4b0289..8d8e10fedf 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
@@ -7,8 +7,8 @@ module Bundler::Molinillo
class SetPayload < Action # :nodoc:
# @!group Action
- # (see Action.name)
- def self.name
+ # (see Action.action_name)
+ def self.action_name
:set_payload
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
index cb0e626e6a..53524d36ad 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
@@ -7,8 +7,8 @@ module Bundler::Molinillo
class Tag < Action
# @!group Action
- # (see Action.name)
- def self.name
+ # (see Action.action_name)
+ def self.action_name
:tag
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
index 14d4957a91..eab989e7bc 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
@@ -10,7 +10,7 @@ module Bundler::Molinillo
# @return [Object] the payload the vertex holds
attr_accessor :payload
- # @return [Arrary<Object>] the explicit requirements that required
+ # @return [Array<Object>] the explicit requirements that required
# this vertex
attr_reader :explicit_requirements
@@ -81,6 +81,7 @@ module Bundler::Molinillo
# @return [Boolean] whether the two vertices are equal, determined
# by a recursive traversal of each {Vertex#successors}
def ==(other)
+ return true if equal?(other)
shallow_eql?(other) &&
successors.to_set == other.successors.to_set
end
@@ -89,6 +90,7 @@ module Bundler::Molinillo
# @return [Boolean] whether the two vertices are equal, determined
# solely by {#name} and {#payload} equality
def shallow_eql?(other)
+ return true if equal?(other)
other &&
name == other.name &&
payload == other.payload
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
index c44da1ce3e..a4fb6dd68e 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Bundler::Molinillo
# The version of Bundler::Molinillo.
- VERSION = '0.5.0'.freeze
+ VERSION = '0.5.7'.freeze
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
index a903b21239..d47cfa2928 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
@@ -48,7 +48,7 @@ module Bundler::Molinillo
if debug?
debug_info = yield
debug_info = debug_info.inspect unless debug_info.is_a?(String)
- output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
+ output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
end
end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
index 5707e890b7..1845966a75 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
@@ -52,7 +52,7 @@ module Bundler::Molinillo
@base = base
@states = []
@iteration_counter = 0
- @parent_of = {}
+ @parents_of = Hash.new { |h, k| h[k] = [] }
end
# Resolves the {#original_requested} dependencies into a full dependency
@@ -105,7 +105,7 @@ module Bundler::Molinillo
handle_missing_or_push_dependency_state(initial_state)
- debug { "Starting resolution (#{@started_at})" }
+ debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
resolver_ui.before_resolution
end
@@ -178,12 +178,14 @@ module Bundler::Molinillo
# Unwinds the states stack because a conflict has been encountered
# @return [void]
def unwind_for_conflict
- debug(depth) { "Unwinding for conflict: #{requirement}" }
+ debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" }
conflicts.tap do |c|
sliced_states = states.slice!((state_index_for_unwind + 1)..-1)
raise VersionConflict.new(c) unless state
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
state.conflicts = c
+ index = states.size - 1
+ @parents_of.each { |_, a| a.reject! { |i| i >= index } }
end
end
@@ -192,24 +194,29 @@ module Bundler::Molinillo
def state_index_for_unwind
current_requirement = requirement
existing_requirement = requirement_for_existing_name(name)
- until current_requirement.nil?
- current_state = find_state_for(current_requirement)
- return states.index(current_state) if state_any?(current_state)
- current_requirement = parent_of(current_requirement)
+ index = -1
+ [current_requirement, existing_requirement].each do |r|
+ until r.nil?
+ current_state = find_state_for(r)
+ if state_any?(current_state)
+ current_index = states.index(current_state)
+ index = current_index if current_index > index
+ break
+ end
+ r = parent_of(r)
+ end
end
- until existing_requirement.nil?
- existing_state = find_state_for(existing_requirement)
- return states.index(existing_state) if state_any?(existing_state)
- existing_requirement = parent_of(existing_requirement)
- end
- -1
+ index
end
# @return [Object] the requirement that led to `requirement` being added
# to the list of requirements.
def parent_of(requirement)
- @parent_of[requirement]
+ return unless requirement
+ return unless index = @parents_of[requirement].last
+ return unless parent_state = @states[index]
+ parent_state.requirement
end
# @return [Object] the requirement that led to a version of a possibility
@@ -349,20 +356,40 @@ module Bundler::Molinillo
# Ensures there are no orphaned successors to the given {vertex}.
# @param [DependencyGraph::Vertex] vertex the vertex to fix up.
# @return [void]
- def fixup_swapped_children(vertex)
+ def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity
payload = vertex.payload
- dep_names = dependencies_for(payload).map(&method(:name_for))
- vertex.successors.each do |succ|
- if !dep_names.include?(succ.name) && !succ.root? && succ.predecessors.to_a == [vertex]
- debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
- activated.detach_vertex_named(succ.name)
+ deps = dependencies_for(payload).group_by(&method(:name_for))
+ vertex.outgoing_edges.each do |outgoing_edge|
+ requirement = outgoing_edge.requirement
+ parent_index = @parents_of[requirement].last
+ succ = outgoing_edge.destination
+ matching_deps = Array(deps[succ.name])
+ dep_matched = matching_deps.include?(requirement)
+
+ # only push the current index when it was originally required by the
+ # same named spec
+ if parent_index && states[parent_index].name == name
+ @parents_of[requirement].push(states.size - 1)
+ end
- all_successor_names = succ.recursive_successors.map(&:name)
+ if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex]
+ debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
+ succ.requirements.each { |r| @parents_of.delete(r) }
- requirements.delete_if do |requirement|
- requirement_name = name_for(requirement)
- (requirement_name == succ.name) || all_successor_names.include?(requirement_name)
+ removed_names = activated.detach_vertex_named(succ.name).map(&:name)
+ requirements.delete_if do |r|
+ # the only removed vertices are those with no other requirements,
+ # so it's safe to delete only based upon name here
+ removed_names.include?(name_for(r))
end
+ elsif !dep_matched
+ debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" }
+ # also reset if we're removing the edge, but only if its parent has
+ # already been fixed up
+ @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty?
+
+ activated.delete_edge(outgoing_edge)
+ requirements.delete(requirement)
end
end
end
@@ -382,13 +409,18 @@ module Bundler::Molinillo
# @return [Boolean] whether the current spec is satisfied as a new
# possibility.
def new_spec_satisfied?
+ unless requirement_satisfied_by?(requirement, activated, possibility)
+ debug(depth) { 'Unsatisfied by requested spec' }
+ return false
+ end
+
locked_requirement = locked_requirement_named(name)
- requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
+
locked_spec_satisfied = !locked_requirement ||
requirement_satisfied_by?(locked_requirement, activated, possibility)
- debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
- requested_spec_satisfied && locked_spec_satisfied
+
+ locked_spec_satisfied
end
# @param [String] requirement_name the spec name to search for
@@ -404,7 +436,7 @@ module Bundler::Molinillo
# @return [void]
def activate_spec
conflicts.delete(name)
- debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
+ debug(depth) { "Activated #{name} at #{possibility}" }
activated.set_payload(name, possibility)
require_nested_dependencies_for(possibility)
end
@@ -418,7 +450,9 @@ module Bundler::Molinillo
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
nested_dependencies.each do |d|
activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d)
- @parent_of[d] = requirement
+ parent_index = states.size - 1
+ parents = @parents_of[d]
+ parents << parent_index if parents.empty?
end
push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
diff --git a/lib/bundler/vendor/net/http/faster.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb
index 74b94d1d99..e5e09080c2 100644
--- a/lib/bundler/vendor/net/http/faster.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb
@@ -24,3 +24,4 @@ class Net::BufferedIO #:nodoc:
end
end
end if RUBY_VERSION < '1.9'
+
diff --git a/lib/bundler/vendor/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
index e2805b1236..5195be2152 100644
--- a/lib/bundler/vendor/net/http/persistent.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
@@ -4,7 +4,7 @@ begin
rescue LoadError
# net/https or openssl
end if RUBY_VERSION < '1.9' # but only for 1.8
-require 'net/http/faster'
+require 'bundler/vendor/net-http-persistent/lib/net/http/faster'
require 'uri'
require 'cgi' # for escaping
@@ -18,27 +18,27 @@ autoload :OpenSSL, 'openssl'
##
# Persistent connections for Net::HTTP
#
-# Net::HTTP::Persistent maintains persistent connections across all the
+# Bundler::Persistent::Net::HTTP::Persistent maintains persistent connections across all the
# servers you wish to talk to. For each host:port you communicate with a
# single persistent connection is created.
#
-# Multiple Net::HTTP::Persistent objects will share the same set of
+# Multiple Bundler::Persistent::Net::HTTP::Persistent objects will share the same set of
# connections.
#
# For each thread you start a new connection will be created. A
-# Net::HTTP::Persistent connection will not be shared across threads.
+# Bundler::Persistent::Net::HTTP::Persistent connection will not be shared across threads.
#
# You can shut down the HTTP connections when done by calling #shutdown. You
-# should name your Net::HTTP::Persistent object if you intend to call this
+# should name your Bundler::Persistent::Net::HTTP::Persistent object if you intend to call this
# method.
#
# Example:
#
-# require 'net/http/persistent'
+# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
#
# uri = URI 'http://example.com/awesome/web/service'
#
-# http = Net::HTTP::Persistent.new 'my_app_name'
+# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name'
#
# # perform a GET
# response = http.request uri
@@ -149,19 +149,19 @@ autoload :OpenSSL, 'openssl'
#
# The recommended way to handle non-idempotent requests is the following:
#
-# require 'net/http/persistent'
+# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
#
# uri = URI 'http://example.com/awesome/web/service'
# post_uri = uri + 'create'
#
-# http = Net::HTTP::Persistent.new 'my_app_name'
+# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name'
#
# post = Net::HTTP::Post.new post_uri.path
# # ... fill in POST request
#
# begin
# response = http.request post_uri, post
-# rescue Net::HTTP::Persistent::Error
+# rescue Bundler::Persistent::Net::HTTP::Persistent::Error
#
# # POST failed, make a new request to verify the server did not process
# # the request
@@ -178,7 +178,7 @@ autoload :OpenSSL, 'openssl'
#
# === Connection Termination
#
-# If you are done using the Net::HTTP::Persistent instance you may shut down
+# If you are done using the Bundler::Persistent::Net::HTTP::Persistent instance you may shut down
# all the connections in the current thread with #shutdown. This is not
# recommended for normal use, it should only be used when it will be several
# minutes before you make another HTTP request.
@@ -188,7 +188,7 @@ autoload :OpenSSL, 'openssl'
# Ruby will automatically garbage collect and shutdown your HTTP connections
# when the thread terminates.
-class Net::HTTP::Persistent
+class Bundler::Persistent::Net::HTTP::Persistent
##
# The beginning of Time
@@ -201,9 +201,9 @@ class Net::HTTP::Persistent
HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
##
- # The version of Net::HTTP::Persistent you are using
+ # The version of Bundler::Persistent::Net::HTTP::Persistent you are using
- VERSION = '2.9.3'
+ VERSION = '2.9.4'
##
# Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
@@ -221,7 +221,7 @@ class Net::HTTP::Persistent
].compact
##
- # Error class for errors raised by Net::HTTP::Persistent. Various
+ # Error class for errors raised by Bundler::Persistent::Net::HTTP::Persistent. Various
# SystemCallErrors are re-raised with a human-readable message under this
# class.
@@ -241,7 +241,7 @@ class Net::HTTP::Persistent
# NOTE: This may not work on ruby > 1.9.
def self.detect_idle_timeout uri, max = 10
- uri = URI uri unless uri.is_a?(URI::Generic)
+ uri = URI uri unless URI::Generic === uri
uri += '/'
req = Net::HTTP::Head.new uri.request_uri
@@ -257,7 +257,7 @@ class Net::HTTP::Persistent
$stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
- unless response.is_a?(Net::HTTPOK) then
+ unless Net::HTTPOK === response then
raise Error, "bad response code #{response.code} detecting idle timeout"
end
@@ -463,7 +463,7 @@ class Net::HTTP::Persistent
attr_accessor :retry_change_requests
##
- # Creates a new Net::HTTP::Persistent.
+ # Creates a new Bundler::Persistent::Net::HTTP::Persistent.
#
# Set +name+ to keep your connections apart from everybody else's. Not
# required currently, but highly recommended. Your library name should be
@@ -594,7 +594,7 @@ class Net::HTTP::Persistent
use_ssl = uri.scheme.downcase == 'https'
if use_ssl then
- raise Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless
+ raise Bundler::Persistent::Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless
HAVE_OPENSSL
ssl_generation = @ssl_generation
@@ -728,7 +728,7 @@ class Net::HTTP::Persistent
} or not @reuse_ssl_sessions then
Net::HTTP
else
- Net::HTTP::Persistent::SSLReuse
+ Bundler::Persistent::Net::HTTP::Persistent::SSLReuse
end
end
@@ -1065,7 +1065,7 @@ class Net::HTTP::Persistent
# Returns the request.
def request_setup req_or_uri # :nodoc:
- req = if req_or_uri.is_a?(URI) then
+ req = if URI === req_or_uri then
Net::HTTP::Get.new req_or_uri.request_uri
else
req_or_uri
@@ -1092,7 +1092,7 @@ class Net::HTTP::Persistent
#
# Uses the current thread by default.
#
- # If you've used Net::HTTP::Persistent across multiple threads you should
+ # If you've used Bundler::Persistent::Net::HTTP::Persistent across multiple threads you should
# call this in each thread when you're done making HTTP requests.
#
# *NOTE*: Calling shutdown for another thread can be dangerous!
@@ -1227,4 +1227,5 @@ application:
end
-require 'net/http/persistent/ssl_reuse'
+require 'bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse'
+
diff --git a/lib/bundler/vendor/net/http/persistent/ssl_reuse.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb
index d73aa54996..1b6b789f6d 100644
--- a/lib/bundler/vendor/net/http/persistent/ssl_reuse.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb
@@ -7,7 +7,7 @@
# This class is an implementation detail and is subject to change or removal
# at any time.
-class Net::HTTP::Persistent::SSLReuse < Net::HTTP
+class Bundler::Persistent::Net::HTTP::Persistent::SSLReuse < Net::HTTP
@is_proxy_class = false
@proxy_addr = nil
@@ -126,3 +126,4 @@ class Net::HTTP::Persistent::SSLReuse < Net::HTTP
private :connect
end
+
diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb
index 9ed67a44e2..563e361bd3 100644
--- a/lib/bundler/vendor/thor/lib/thor.rb
+++ b/lib/bundler/vendor/thor/lib/thor.rb
@@ -1,7 +1,7 @@
require "set"
require "bundler/vendor/thor/lib/thor/base"
-class Bundler::Thor # rubocop:disable ClassLength
+class Bundler::Thor
class << self
# Allows for custom "Command" package naming.
#
@@ -9,7 +9,7 @@ class Bundler::Thor # rubocop:disable ClassLength
# name<String>
# options<Hash>
#
- def package_name(name, options = {})
+ def package_name(name, _ = {})
@package_name = name.nil? || name == "" ? nil : name
end
@@ -57,7 +57,9 @@ class Bundler::Thor # rubocop:disable ClassLength
command.usage = usage if usage
command.description = description if description
else
- @usage, @desc, @hide = usage, description, options[:hide] || false
+ @usage = usage
+ @desc = description
+ @hide = options[:hide] || false
end
end
@@ -156,6 +158,10 @@ class Bundler::Thor # rubocop:disable ClassLength
end
alias_method :option, :method_option
+ def disable_class_options
+ @disable_class_options = true
+ end
+
# Prints help information for the given command.
#
# ==== Parameters
@@ -170,7 +176,7 @@ class Bundler::Thor # rubocop:disable ClassLength
shell.say "Usage:"
shell.say " #{banner(command)}"
shell.say
- class_options_help(shell, nil => command.options.map { |_, o| o })
+ class_options_help(shell, nil => command.options.values)
if command.long_description
shell.say "Description:"
shell.print_wrapped(command.long_description, :indent => 2)
@@ -231,8 +237,9 @@ class Bundler::Thor # rubocop:disable ClassLength
define_method(subcommand) do |*args|
args, opts = Bundler::Thor::Arguments.split(args)
- args.unshift("help") if opts.include? "--help" or opts.include? "-h"
- invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
+ invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
+ invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
+ invoke subcommand_class, *invoke_args
end
end
alias_method :subtask, :subcommand
@@ -320,6 +327,7 @@ class Bundler::Thor # rubocop:disable ClassLength
end
protected
+
def stop_on_unknown_option #:nodoc:
@stop_on_unknown_option ||= Set.new
end
@@ -345,12 +353,14 @@ class Bundler::Thor # rubocop:disable ClassLength
opts.clear
end
else
- args, opts = given_args, nil
+ args = given_args
+ opts = nil
command = dynamic_command_class.new(meth)
end
opts = given_opts || opts || []
- config.merge!(:current_command => command, :command_options => command.options)
+ config[:current_command] = command
+ config[:command_options] = command.options
instance = new(args, opts, config)
yield instance if block_given?
@@ -380,17 +390,18 @@ class Bundler::Thor # rubocop:disable ClassLength
@usage ||= nil
@desc ||= nil
@long_desc ||= nil
+ @disable_class_options ||= nil
if @usage && @desc
base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command
- commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
- @usage, @desc, @long_desc, @method_options, @hide = nil
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options)
+ @usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil
true
elsif all_commands[meth] || meth == "method_missing"
true
else
- puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
- "Call desc if you want this method to be available as command or declare it inside a " <<
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
+ "Call desc if you want this method to be available as command or declare it inside a " \
"no_commands{} block. Invoked from #{caller[1].inspect}."
false
end
@@ -405,11 +416,7 @@ class Bundler::Thor # rubocop:disable ClassLength
# Retrieve the command name from given args.
def retrieve_command_name(args) #:nodoc:
meth = args.first.to_s unless args.empty?
- if meth && (map[meth] || meth !~ /^\-/)
- args.shift
- else
- nil
- end
+ args.shift if meth && (map[meth] || meth !~ /^\-/)
end
alias_method :retrieve_task_name, :retrieve_command_name
@@ -421,20 +428,20 @@ class Bundler::Thor # rubocop:disable ClassLength
# +normalize_command_name+ also converts names like +animal-prison+
# into +animal_prison+.
def normalize_command_name(meth) #:nodoc:
- return default_command.to_s.gsub("-", "_") unless meth
+ return default_command.to_s.tr("-", "_") unless meth
possibilities = find_command_possibilities(meth)
- if possibilities.size > 1
- fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
- elsif possibilities.size < 1
- meth = meth || default_command
+ raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1
+
+ if possibilities.empty?
+ meth ||= default_command
elsif map[meth]
meth = map[meth]
else
meth = possibilities.first
end
- meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar
+ meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
end
alias_method :normalize_task_name, :normalize_command_name
@@ -470,6 +477,7 @@ class Bundler::Thor # rubocop:disable ClassLength
map HELP_MAPPINGS => :help
desc "help [COMMAND]", "Describe available commands or one specific command"
+ disable_class_options
def help(command = nil, subcommand = false)
if command
if self.class.subcommands.include? command
diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb
index 5a82dfd45f..9f1c9f23e8 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions.rb
@@ -73,14 +73,15 @@ class Bundler::Thor
#
def initialize(args = [], options = {}, config = {})
self.behavior = case config[:behavior].to_s
- when "force", "skip"
- _cleanup_options_and_set(options, config[:behavior])
- :invoke
- when "revoke"
- :revoke
- else
- :invoke
- end
+ when "force", "skip"
+ _cleanup_options_and_set(options, config[:behavior])
+ :invoke
+ when "revoke"
+ :revoke
+ else
+ :invoke
+ end
+
super
self.destination_root = config[:destination_root]
end
@@ -129,7 +130,7 @@ class Bundler::Thor
# Receives a file or directory and search for it in the source paths.
#
- def find_in_source_paths(file) # rubocop:disable MethodLength
+ def find_in_source_paths(file)
possible_files = [file, file + TEMPLATE_EXTNAME]
relative_root = relative_to_original_destination_root(destination_root, false)
@@ -146,13 +147,13 @@ class Bundler::Thor
message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
end
- if source_paths.empty?
- message << "Currently you have no source paths."
- else
- message << "Your current source paths are: \n#{source_paths.join("\n")}"
- end
+ message << if source_paths.empty?
+ "Currently you have no source paths."
+ else
+ "Your current source paths are: \n#{source_paths.join("\n")}"
+ end
- fail Error, message
+ raise Error, message
end
# Do something in the root or on a provided subfolder. If a relative path
@@ -214,10 +215,10 @@ class Bundler::Thor
say_status :apply, path, verbose
shell.padding += 1 if verbose
- if is_uri
- contents = open(path, "Accept" => "application/x-thor-template") { |io| io.read }
+ contents = if is_uri
+ open(path, "Accept" => "application/x-thor-template", &:read)
else
- contents = open(path) { |io| io.read }
+ open(path, &:read)
end
instance_eval(contents, path)
@@ -250,9 +251,7 @@ class Bundler::Thor
say_status :run, desc, config.fetch(:verbose, true)
- unless options[:pretend]
- config[:capture] ? `#{command}` : system("#{command}")
- end
+ !options[:pretend] && config[:capture] ? `#{command}` : system(command.to_s)
end
# Executes a ruby script (taking into account WIN32 platform quirks).
@@ -308,7 +307,7 @@ class Bundler::Thor
def _cleanup_options_and_set(options, key) #:nodoc:
case options
when Array
- %w[--force -f --skip -s].each { |i| options.delete(i) }
+ %w(--force -f --skip -s).each { |i| options.delete(i) }
options << "--#{key}"
when Hash
[:force, :skip, "force", "skip"].each { |i| options.delete(i) }
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
index a0f5640333..ade3f85bde 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
@@ -84,7 +84,7 @@ class Bundler::Thor
def force_or_skip_or_conflict(force, skip, &block)
if force
say_status :force, :yellow
- block.call unless pretend?
+ yield unless pretend?
elsif skip
say_status :skip, :yellow
else
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb
index be437922b6..7577d12533 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb
@@ -14,7 +14,7 @@ class Bundler::Thor
#
# create_link "config/apache.conf", "/etc/apache.conf"
#
- def create_link(destination, *args, &block)
+ def create_link(destination, *args)
config = args.last.is_a?(Hash) ? args.pop : {}
source = args.first
action CreateLink.new(self, destination, source, config)
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
index 1a2e25da2f..f555f7b7e0 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
@@ -72,7 +72,7 @@ class Bundler::Thor
protected
- def execute! # rubocop:disable MethodLength
+ def execute!
lookup = Util.escape_globs(source)
lookup = config[:recursive] ? File.join(lookup, "**") : lookup
lookup = file_level_lookup(lookup)
@@ -85,7 +85,7 @@ class Bundler::Thor
case file_source
when /\.empty_directory$/
- dirname = File.dirname(file_destination).gsub(/\/\.$/, "")
+ dirname = File.dirname(file_destination).gsub(%r{/\.$}, "")
next if dirname == given_destination
base.empty_directory(dirname, config)
when /#{TEMPLATE_EXTNAME}$/
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
index cdc3768b4c..309cb31d9d 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
@@ -32,7 +32,8 @@ class Bundler::Thor
# config<Hash>:: give :verbose => false to not log the status.
#
def initialize(base, destination, config = {})
- @base, @config = base, {:verbose => true}.merge(config)
+ @base = base
+ @config = {:verbose => true}.merge(config)
self.destination = destination
end
@@ -80,11 +81,10 @@ class Bundler::Thor
# given_destination #=> baz
#
def destination=(destination)
- if destination
- @given_destination = convert_encoded_instructions(destination.to_s)
- @destination = ::File.expand_path(@given_destination, base.destination_root)
- @relative_destination = base.relative_to_original_destination_root(@destination)
- end
+ return unless destination
+ @given_destination = convert_encoded_instructions(destination.to_s)
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
+ @relative_destination = base.relative_to_original_destination_root(@destination)
end
# Filenames in the encoded form are converted. If you have a file:
@@ -113,7 +113,7 @@ class Bundler::Thor
on_conflict_behavior(&block)
else
say_status :create, :green
- block.call unless pretend?
+ yield unless pretend?
end
destination
@@ -121,7 +121,7 @@ class Bundler::Thor
# What to do when the destination file already exists.
#
- def on_conflict_behavior(&block)
+ def on_conflict_behavior
say_status :exist, :blue
end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
index 2bdc78f578..54272fc0c6 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
@@ -26,7 +26,7 @@ class Bundler::Thor
create_file destination, nil, config do
content = File.binread(source)
- content = block.call(content) if block
+ content = yield(content) if block
content
end
if config[:mode] == :preserve
@@ -49,7 +49,7 @@ class Bundler::Thor
#
# link_file "doc/README"
#
- def link_file(source, *args, &block)
+ def link_file(source, *args)
config = args.last.is_a?(Hash) ? args.pop : {}
destination = args.first || source
source = File.expand_path(find_in_source_paths(source.to_s))
@@ -82,7 +82,7 @@ class Bundler::Thor
render = open(source) { |input| input.binmode.read }
destination ||= if block_given?
- block.arity == 1 ? block.call(render) : block.call
+ block.arity == 1 ? yield(render) : yield
else
File.basename(source)
end
@@ -110,11 +110,11 @@ class Bundler::Thor
destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")
source = File.expand_path(find_in_source_paths(source.to_s))
- context = instance_eval("binding")
+ context = config.delete(:context) || instance_eval("binding")
create_file destination, nil, config do
- content = ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
- content = block.call(content) if block
+ content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
+ content = yield(content) if block
content
end
end
@@ -154,7 +154,7 @@ class Bundler::Thor
#
def prepend_to_file(path, *args, &block)
config = args.last.is_a?(Hash) ? args.pop : {}
- config.merge!(:after => /\A/)
+ config[:after] = /\A/
insert_into_file(path, *(args << config), &block)
end
alias_method :prepend_file, :prepend_to_file
@@ -176,7 +176,7 @@ class Bundler::Thor
#
def append_to_file(path, *args, &block)
config = args.last.is_a?(Hash) ? args.pop : {}
- config.merge!(:before => /\z/)
+ config[:before] = /\z/
insert_into_file(path, *(args << config), &block)
end
alias_method :append_file, :append_to_file
@@ -200,7 +200,7 @@ class Bundler::Thor
#
def inject_into_class(path, klass, *args, &block)
config = args.last.is_a?(Hash) ? args.pop : {}
- config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/)
+ config[:after] = /class #{klass}\n|class #{klass} .*\n/
insert_into_file(path, *(args << config), &block)
end
@@ -285,7 +285,7 @@ class Bundler::Thor
#
def remove_file(path, config = {})
return unless behavior == :invoke
- path = File.expand_path(path, destination_root)
+ path = File.expand_path(path, destination_root)
say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
::FileUtils.rm_rf(path) if !options[:pretend] && File.exist?(path)
@@ -301,8 +301,8 @@ class Bundler::Thor
@output_buffer.concat(string)
end
- def capture(*args, &block)
- with_output_buffer { block.call(*args) }
+ def capture(*args)
+ with_output_buffer { yield(*args) }
end
def with_output_buffer(buf = "") #:nodoc:
@@ -312,5 +312,16 @@ class Bundler::Thor
ensure
self.output_buffer = old_buffer
end
+
+ # Bundler::Thor::Actions#capture depends on what kind of buffer is used in ERB.
+ # Thus CapturableERB fixes ERB to use String buffer.
+ class CapturableERB < ERB
+ def set_eoutvar(compiler, eoutvar = "_erbout")
+ compiler.put_cmd = "#{eoutvar}.concat"
+ compiler.insert_cmd = "#{eoutvar}.concat"
+ compiler.pre_cmd = ["#{eoutvar} = ''"]
+ compiler.post_cmd = [eoutvar]
+ end
+ end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
index 91ab245ae1..781ee63140 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
@@ -22,11 +22,8 @@ class Bundler::Thor
# end
#
def insert_into_file(destination, *args, &block)
- if block_given?
- data, config = block, args.shift
- else
- data, config = args.shift, args.shift
- end
+ data = block_given? ? block : args.shift
+ config = args.shift
action InjectIntoFile.new(self, destination, data, config)
end
alias_method :inject_into_file, :insert_into_file
@@ -39,9 +36,9 @@ class Bundler::Thor
@behavior, @flag = if @config.key?(:after)
[:after, @config.delete(:after)]
- else
- [:before, @config.delete(:before)]
- end
+ else
+ [:before, @config.delete(:before)]
+ end
@replacement = data.is_a?(Proc) ? data.call : data
@flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
@@ -94,12 +91,11 @@ class Bundler::Thor
# Adds the content to the file.
#
def replace!(regexp, string, force)
- unless base.options[:pretend]
- content = File.binread(destination)
- if force || !content.include?(replacement)
- content.gsub!(regexp, string)
- File.open(destination, "wb") { |file| file.write(content) }
- end
+ return if base.options[:pretend]
+ content = File.binread(destination)
+ if force || !content.include?(replacement)
+ content.gsub!(regexp, string)
+ File.open(destination, "wb") { |file| file.write(content) }
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb
index c3667521a5..a95974a62d 100644
--- a/lib/bundler/vendor/thor/lib/thor/base.rb
+++ b/lib/bundler/vendor/thor/lib/thor/base.rb
@@ -14,11 +14,11 @@ class Bundler::Thor
autoload :Group, "bundler/vendor/thor/lib/thor/group"
# Shortcuts for help.
- HELP_MAPPINGS = %w[-h -? --help -D]
+ HELP_MAPPINGS = %w(-h -? --help -D)
# Bundler::Thor methods that should not be overwritten by the user.
- THOR_RESERVED_WORDS = %w[invoke shell options behavior root destination_root relative_root
- action add_file create_file in_root inside run run_ruby_script]
+ THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
+ action add_file create_file in_root inside run run_ruby_script)
TEMPLATE_EXTNAME = ".tt"
@@ -41,8 +41,8 @@ class Bundler::Thor
#
# config<Hash>:: Configuration for this Bundler::Thor class.
#
- def initialize(args = [], local_options = {}, config = {}) # rubocop:disable MethodLength
- parse_options = self.class.class_options
+ def initialize(args = [], local_options = {}, config = {})
+ parse_options = config[:current_command] && config[:current_command].disable_class_options ? {} : self.class.class_options
# The start method splits inbound arguments at the first argument
# that looks like an option (starts with - or --). It then calls
@@ -52,11 +52,13 @@ class Bundler::Thor
command_options = config.delete(:command_options) # hook for start
parse_options = parse_options.merge(command_options) if command_options
if local_options.is_a?(Array)
- array_options, hash_options = local_options, {}
+ array_options = local_options
+ hash_options = {}
else
# Handle the case where the class was explicitly instantiated
# with pre-parsed options.
- array_options, hash_options = [], local_options
+ array_options = []
+ hash_options = local_options
end
# Let Bundler::Thor::Options parse the options first, so it can remove
@@ -205,7 +207,7 @@ class Bundler::Thor
# ==== Errors
# ArgumentError:: Raised if you supply a required argument after a non required one.
#
- def argument(name, options = {}) # rubocop:disable MethodLength
+ def argument(name, options = {})
is_thor_reserved_word?(name, :argument)
no_commands { attr_accessor name }
@@ -219,11 +221,13 @@ class Bundler::Thor
remove_argument name
- arguments.each do |argument|
- next if argument.required?
- fail ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
- "the non-required argument #{argument.human_name.inspect}."
- end if required
+ if required
+ arguments.each do |argument|
+ next if argument.required?
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \
+ "the non-required argument #{argument.human_name.inspect}."
+ end
+ end
options[:required] = required
@@ -343,7 +347,7 @@ class Bundler::Thor
#
def all_commands
@all_commands ||= from_superclass(:all_commands, Bundler::Thor::CoreExt::OrderedHash.new)
- @all_commands.merge(commands)
+ @all_commands.merge!(commands)
end
alias_method :all_tasks, :all_commands
@@ -467,11 +471,8 @@ class Bundler::Thor
alias_method :public_task, :public_command
def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
- if has_namespace
- fail UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
- else
- fail UndefinedCommandError, "Could not find command #{command.inspect}."
- end
+ raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace
+ raise UndefinedCommandError, "Could not find command #{command.inspect}."
end
alias_method :handle_no_task_error, :handle_no_command_error
@@ -480,7 +481,7 @@ class Bundler::Thor
msg << "no arguments" if args.empty?
msg << "arguments " << args.inspect unless args.empty?
msg << "\nUsage: #{banner(command).inspect}"
- fail InvocationError, msg
+ raise InvocationError, msg
end
protected
@@ -513,14 +514,13 @@ class Bundler::Thor
padding = options.map { |o| o.aliases.size }.max.to_i * 4
options.each do |option|
- unless option.hide
- item = [option.usage(padding)]
- item.push(option.description ? "# #{option.description}" : "")
+ next if option.hide
+ item = [option.usage(padding)]
+ item.push(option.description ? "# #{option.description}" : "")
- list << item
- list << ["", "# Default: #{option.default}"] if option.show_default?
- list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
- end
+ list << item
+ list << ["", "# Default: #{option.default}"] if option.show_default?
+ list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
end
shell.say(group_name ? "#{group_name} options:" : "Options:")
@@ -531,7 +531,7 @@ class Bundler::Thor
# Raises an error if the word given is a Bundler::Thor reserved word.
def is_thor_reserved_word?(word, type) #:nodoc:
return false unless THOR_RESERVED_WORDS.include?(word.to_s)
- fail "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}"
+ raise "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}"
end
# Build an option and adds it to the given scope.
@@ -566,7 +566,7 @@ class Bundler::Thor
elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
commands[name.to_s] = command.clone
else
- fail ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
end
end
alias_method :find_and_refresh_task, :find_and_refresh_command
@@ -649,7 +649,7 @@ class Bundler::Thor
# SIGNATURE: The hook invoked by start.
def dispatch(command, given_args, given_opts, config) #:nodoc:
- fail NotImplementedError
+ raise NotImplementedError
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb
index 72c8348cb6..aacf2ef719 100644
--- a/lib/bundler/vendor/thor/lib/thor/command.rb
+++ b/lib/bundler/vendor/thor/lib/thor/command.rb
@@ -1,9 +1,9 @@
class Bundler::Thor
- class Command < Struct.new(:name, :description, :long_description, :usage, :options)
+ class Command < Struct.new(:name, :description, :long_description, :usage, :options, :disable_class_options)
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
- def initialize(name, description, long_description, usage, options = nil)
- super(name.to_s, description, long_description, usage, options || {})
+ def initialize(name, description, long_description, usage, options = nil, disable_class_options = false)
+ super(name.to_s, description, long_description, usage, options || {}, disable_class_options)
end
def initialize_copy(other) #:nodoc:
@@ -33,7 +33,7 @@ class Bundler::Thor
rescue ArgumentError => e
handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e)
rescue NoMethodError => e
- handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (fail e)
+ handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e)
end
# Returns the formatted usage by injecting given required arguments
@@ -50,7 +50,7 @@ class Bundler::Thor
# Add usage with required arguments
formatted << if klass && !klass.arguments.empty?
usage.to_s.gsub(/^#{name}/) do |match|
- match << " " << klass.arguments.map { |a| a.usage }.compact.join(" ")
+ match << " " << klass.arguments.map(&:usage).compact.join(" ")
end
else
usage.to_s
@@ -88,7 +88,7 @@ class Bundler::Thor
end
def sans_backtrace(backtrace, caller) #:nodoc:
- saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ /^kernel\// && RUBY_ENGINE =~ /rbx/) }
+ saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ %r{^kernel/} && RUBY_ENGINE =~ /rbx/) }
saned - caller
end
@@ -105,7 +105,7 @@ class Bundler::Thor
error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
end
end
- Task = Command # rubocop:disable ConstantName
+ Task = Command
# A command that is hidden in help messages but still invocable.
class HiddenCommand < Command
@@ -113,7 +113,7 @@ class Bundler::Thor
true
end
end
- HiddenTask = HiddenCommand # rubocop:disable ConstantName
+ HiddenTask = HiddenCommand
# A dynamic command that handles method missing scenarios.
class DynamicCommand < Command
@@ -129,5 +129,5 @@ class Bundler::Thor
end
end
end
- DynamicTask = DynamicCommand # rubocop:disable ConstantName
+ DynamicTask = DynamicCommand
end
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
index 6cf61db812..de8c4713b4 100644
--- a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
@@ -28,6 +28,14 @@ class Bundler::Thor
super(convert_key(key))
end
+ def fetch(key, *args)
+ super(convert_key(key), *args)
+ end
+
+ def key?(key)
+ super(convert_key(key))
+ end
+
def values_at(*indices)
indices.map { |key| self[convert_key(key)] }
end
@@ -60,7 +68,7 @@ class Bundler::Thor
# options.shebang # => "/usr/lib/local/ruby"
# options.test_framework?(:rspec) # => options[:test_framework] == :rspec
#
- def method_missing(method, *args, &block)
+ def method_missing(method, *args)
method = method.to_s
if method =~ /^(\w+)\?$/
if args.empty?
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb
index 19f3c3d43e..0f6e2e0af2 100644
--- a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb
@@ -1,10 +1,12 @@
class IO #:nodoc:
class << self
- def binread(file, *args)
- fail ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3
- File.open(file, "rb") do |f|
- f.read(*args)
+ unless method_defined? :binread
+ def binread(file, *args)
+ raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3
+ File.open(file, "rb") do |f|
+ f.read(*args)
+ end
end
- end unless method_defined? :binread
+ end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb
index 7e80672a07..76f1e43c65 100644
--- a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb
@@ -1,96 +1,127 @@
class Bundler::Thor
- module CoreExt #:nodoc:
- if RUBY_VERSION >= "1.9"
- class OrderedHash < ::Hash
- end
- else
- # This class is based on the Ruby 1.9 ordered hashes.
- #
- # It keeps the semantics and most of the efficiency of normal hashes
- # while also keeping track of the order in which elements were set.
- #
- class OrderedHash #:nodoc:
- include Enumerable
-
- Node = Struct.new(:key, :value, :next, :prev)
-
- def initialize
- @hash = {}
+ module CoreExt
+ class OrderedHash < ::Hash
+ if RUBY_VERSION < "1.9"
+ def initialize(*args, &block)
+ super
+ @keys = []
end
- def [](key)
- @hash[key] && @hash[key].value
+ def initialize_copy(other)
+ super
+ # make a deep copy of keys
+ @keys = other.keys
end
def []=(key, value)
- if node = @hash[key] # rubocop:disable AssignmentInCondition
- node.value = value
- else
- node = Node.new(key, value)
-
- if !defined?(@first) || @first.nil?
- @first = @last = node
- else
- node.prev = @last
- @last.next = node
- @last = node
- end
- end
-
- @hash[key] = node
- value
+ @keys << key unless key?(key)
+ super
end
def delete(key)
- if node = @hash[key] # rubocop:disable AssignmentInCondition
- prev_node = node.prev
- next_node = node.next
+ if key? key
+ index = @keys.index(key)
+ @keys.delete_at index
+ end
+ super
+ end
- next_node.prev = prev_node if next_node
- prev_node.next = next_node if prev_node
+ def delete_if
+ super
+ sync_keys!
+ self
+ end
- @first = next_node if @first == node
- @last = prev_node if @last == node
+ alias_method :reject!, :delete_if
- value = node.value
- end
-
- @hash.delete(key)
- value
+ def reject(&block)
+ dup.reject!(&block)
end
def keys
- map { |k, v| k }
+ @keys.dup
end
def values
- map { |k, v| v }
+ @keys.map { |key| self[key] }
+ end
+
+ def to_hash
+ self
+ end
+
+ def to_a
+ @keys.map { |key| [key, self[key]] }
+ end
+
+ def each_key
+ return to_enum(:each_key) unless block_given?
+ @keys.each { |key| yield(key) }
+ self
+ end
+
+ def each_value
+ return to_enum(:each_value) unless block_given?
+ @keys.each { |key| yield(self[key]) }
+ self
end
def each
- return unless defined?(@first) && @first
- yield [@first.key, @first.value]
- node = @first
- yield [node.key, node.value] while node = node.next # rubocop:disable AssignmentInCondition
+ return to_enum(:each) unless block_given?
+ @keys.each { |key| yield([key, self[key]]) }
self
end
- def merge(other)
- hash = self.class.new
+ def each_pair
+ return to_enum(:each_pair) unless block_given?
+ @keys.each { |key| yield(key, self[key]) }
+ self
+ end
- each do |key, value|
- hash[key] = value
- end
+ alias_method :select, :find_all
+
+ def clear
+ super
+ @keys.clear
+ self
+ end
+
+ def shift
+ k = @keys.first
+ v = delete(k)
+ [k, v]
+ end
- other.each do |key, value|
- hash[key] = value
+ def merge!(other_hash)
+ if block_given?
+ other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
+ else
+ other_hash.each { |k, v| self[k] = v }
end
+ self
+ end
+
+ alias_method :update, :merge!
+
+ def merge(other_hash, &block)
+ dup.merge!(other_hash, &block)
+ end
- hash
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
+ def replace(other)
+ super
+ @keys = other.keys
+ self
end
- def empty?
- @hash.empty?
+ def inspect
+ "#<#{self.class} #{super}>"
+ end
+
+ private
+
+ def sync_keys!
+ @keys.delete_if { |k| !key?(k) }
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb
index fc34c11268..2f816081f3 100644
--- a/lib/bundler/vendor/thor/lib/thor/error.rb
+++ b/lib/bundler/vendor/thor/lib/thor/error.rb
@@ -3,7 +3,7 @@ class Bundler::Thor
# errors have their backtrace suppressed and are nicely shown to the user.
#
# Errors that are caused by the developer, like declaring a method which
- # overwrites a thor keyword, it SHOULD NOT raise a Bundler::Thor::Error. This way, we
+ # overwrites a thor keyword, SHOULD NOT raise a Bundler::Thor::Error. This way, we
# ensure that developer errors are shown with full backtrace.
class Error < StandardError
end
@@ -11,11 +11,11 @@ class Bundler::Thor
# Raised when a command was not found.
class UndefinedCommandError < Error
end
- UndefinedTaskError = UndefinedCommandError # rubocop:disable ConstantName
+ UndefinedTaskError = UndefinedCommandError
class AmbiguousCommandError < Error
end
- AmbiguousTaskError = AmbiguousCommandError # rubocop:disable ConstantName
+ AmbiguousTaskError = AmbiguousCommandError
# Raised when a command was found, but not invoked properly.
class InvocationError < Error
diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb
index 13d168ad62..c95b708caa 100644
--- a/lib/bundler/vendor/thor/lib/thor/group.rb
+++ b/lib/bundler/vendor/thor/lib/thor/group.rb
@@ -4,7 +4,7 @@ require "bundler/vendor/thor/lib/thor/base"
# is that it invokes all commands at once. It also include some methods that allows
# invocations to be done at the class method, which are not available to Bundler::Thor
# commands.
-class Bundler::Thor::Group # rubocop:disable ClassLength
+class Bundler::Thor::Group
class << self
# The description for this Bundler::Thor::Group. If none is provided, but a source root
# exists, tries to find the USAGE one folder above it, otherwise searches
@@ -53,7 +53,7 @@ class Bundler::Thor::Group # rubocop:disable ClassLength
# The namespace/class given will have its options showed on the help
# usage. Check invoke_from_option for more information.
#
- def invoke(*names, &block) # rubocop:disable MethodLength
+ def invoke(*names, &block)
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, true)
@@ -62,7 +62,7 @@ class Bundler::Thor::Group # rubocop:disable ClassLength
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
- def _invoke_#{name.to_s.gsub(/\W/, "_")}
+ def _invoke_#{name.to_s.gsub(/\W/, '_')}
klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
if klass
@@ -107,21 +107,21 @@ class Bundler::Thor::Group # rubocop:disable ClassLength
# invoked. The block receives two parameters, an instance of the current
# class and the klass to be invoked.
#
- def invoke_from_option(*names, &block) # rubocop:disable MethodLength
+ def invoke_from_option(*names, &block)
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, :white)
names.each do |name|
unless class_options.key?(name)
- fail ArgumentError, "You have to define the option #{name.inspect} " <<
- "before setting invoke_from_option."
+ raise ArgumentError, "You have to define the option #{name.inspect} " \
+ "before setting invoke_from_option."
end
invocations[name] = true
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
- def _invoke_from_option_#{name.to_s.gsub(/\W/, "_")}
+ def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
return unless options[#{name.inspect}]
value = options[#{name.inspect}]
@@ -188,7 +188,7 @@ class Bundler::Thor::Group # rubocop:disable ClassLength
group_options[human_name] ||= []
group_options[human_name] += klass.class_options.values.select do |class_option|
base_options[class_option.name.to_sym].nil? && class_option.group.nil? &&
- !group_options.values.flatten.any? { |i| i.name == class_option.name }
+ !group_options.values.flatten.any? { |i| i.name == class_option.name }
end
yield klass if block_given?
@@ -204,11 +204,11 @@ class Bundler::Thor::Group # rubocop:disable ClassLength
end
alias_method :printable_tasks, :printable_commands
- def handle_argument_error(command, error, args, arity) #:nodoc:
+ def handle_argument_error(command, error, _args, arity) #:nodoc:
msg = "#{basename} #{command.name} takes #{arity} argument"
msg << "s" if arity > 1
msg << ", but it should not."
- fail error, msg
+ raise error, msg
end
protected
@@ -267,9 +267,9 @@ protected
if block
case block.arity
when 3
- block.call(self, klass, command)
+ yield(self, klass, command)
when 2
- block.call(self, klass)
+ yield(self, klass)
when 1
instance_exec(klass, &block)
end
diff --git a/lib/bundler/vendor/thor/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb
index 684df2c616..866d2212a7 100644
--- a/lib/bundler/vendor/thor/lib/thor/invocation.rb
+++ b/lib/bundler/vendor/thor/lib/thor/invocation.rb
@@ -108,8 +108,8 @@ class Bundler::Thor
command, args, opts, config = args
klass, command = _retrieve_class_and_command(name, command)
- fail "Missing Bundler::Thor class for invoke #{name}" unless klass
- fail "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base
+ raise "Missing Bundler::Thor class for invoke #{name}" unless klass
+ raise "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base
args, opts, config = _parse_initialization_options(args, opts, config)
klass.send(:dispatch, command, args, opts, config) do |instance|
@@ -150,10 +150,9 @@ class Bundler::Thor
# use the given name and return self as class. Otherwise, call
# prepare_for_invocation in the current class.
def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
- case
- when name.nil?
+ if name.nil?
[self.class, nil]
- when self.class.all_commands[name.to_s]
+ elsif self.class.all_commands[name.to_s]
[self.class, name.to_s]
else
klass, command = self.class.prepare_for_invocation(nil, name)
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
index 84957903cd..dfe7398583 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
@@ -10,8 +10,8 @@ class Bundler::Thor
type = options[:type]
- fail ArgumentError, "#{class_name} name can't be nil." if name.nil?
- fail ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
+ raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
+ raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
@name = name.to_s
@description = options[:desc]
@@ -44,11 +44,8 @@ class Bundler::Thor
protected
def validate!
- if required? && !default.nil?
- fail ArgumentError, "An argument cannot be required and have default value."
- elsif @enum && !@enum.is_a?(Array)
- fail ArgumentError, "An argument cannot have an enum other than an array."
- end
+ raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
+ raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
end
def valid_type?(type)
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
index c7bb648e31..1fd790f4b7 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
@@ -1,6 +1,6 @@
class Bundler::Thor
class Arguments #:nodoc: # rubocop:disable ClassLength
- NUMERIC = /(\d*\.\d+|\d+)/
+ NUMERIC = /[-+]?(\d*\.\d+|\d+)/
# Receives an array of args and returns two arrays, one with arguments
# and one with switches.
@@ -24,7 +24,8 @@ class Bundler::Thor
# Takes an array of Bundler::Thor::Argument objects.
#
def initialize(arguments = [])
- @assigns, @non_assigned_required = {}, []
+ @assigns = {}
+ @non_assigned_required = []
@switches = arguments
arguments.each do |argument|
@@ -49,7 +50,7 @@ class Bundler::Thor
@assigns
end
- def remaining # rubocop:disable TrivialAccessors
+ def remaining
@pile
end
@@ -73,7 +74,7 @@ class Bundler::Thor
end
def unshift(arg)
- if arg.kind_of?(Array)
+ if arg.is_a?(Array)
@pile = arg + @pile
else
@pile.unshift(arg)
@@ -99,6 +100,7 @@ class Bundler::Thor
while current_is_value? && peek.include?(":")
key, value = shift.split(":", 2)
+ raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key
hash[key] = value
end
hash
@@ -128,13 +130,13 @@ class Bundler::Thor
return shift if peek.is_a?(Numeric)
unless peek =~ NUMERIC && $& == peek
- fail MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
+ raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
end
value = $&.index(".") ? shift.to_f : shift.to_i
if @switches.is_a?(Hash) && switch = @switches[name]
if switch.enum && !switch.enum.include?(value)
- fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
+ raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
end
end
value
@@ -150,9 +152,9 @@ class Bundler::Thor
nil
else
value = shift
- if @switches.is_a?(Hash) && switch = @switches[name] # rubocop:disable AssignmentInCondition
+ if @switches.is_a?(Hash) && switch = @switches[name]
if switch.enum && !switch.enum.include?(value)
- fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
+ raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
end
end
value
@@ -162,14 +164,12 @@ class Bundler::Thor
# Raises an error if @non_assigned_required array is not empty.
#
def check_requirement!
- unless @non_assigned_required.empty?
- names = @non_assigned_required.map do |o|
- o.respond_to?(:switch_name) ? o.switch_name : o.human_name
- end.join("', '")
-
- class_name = self.class.name.split("::").last.downcase
- fail RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
- end
+ return if @non_assigned_required.empty?
+ names = @non_assigned_required.map do |o|
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
+ end.join("', '")
+ class_name = self.class.name.split("::").last.downcase
+ raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
index eb893617f4..032493075d 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/option.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
@@ -40,31 +40,33 @@ class Bundler::Thor
#
# By default all options are optional, unless :required is given.
#
- def self.parse(key, value) # rubocop:disable MethodLength
+ def self.parse(key, value)
if key.is_a?(Array)
name, *aliases = key
else
- name, aliases = key, []
+ name = key
+ aliases = []
end
name = name.to_s
default = value
type = case value
- when Symbol
- default = nil
- if VALID_TYPES.include?(value)
- value
- elsif required = (value == :required) # rubocop:disable AssignmentInCondition
- :string
- end
- when TrueClass, FalseClass
- :boolean
- when Numeric
- :numeric
- when Hash, Array, String
- value.class.name.downcase.to_sym
- end
+ when Symbol
+ default = nil
+ if VALID_TYPES.include?(value)
+ value
+ elsif required = (value == :required) # rubocop:disable AssignmentInCondition
+ :string
+ end
+ when TrueClass, FalseClass
+ :boolean
+ when Numeric
+ :numeric
+ when Hash, Array, String
+ value.class.name.downcase.to_sym
+ end
+
new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
end
@@ -86,7 +88,7 @@ class Bundler::Thor
sample = "[#{sample}]" unless required?
if boolean?
- sample << ", [#{dasherize("no-" + human_name)}]" unless name == "force"
+ sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
end
if aliases.empty?
@@ -107,7 +109,26 @@ class Bundler::Thor
protected
def validate!
- fail ArgumentError, "An option cannot be boolean and required." if boolean? && required?
+ raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
+ validate_default_type!
+ end
+
+ def validate_default_type!
+ default_type = case @default
+ when nil
+ return
+ when TrueClass, FalseClass
+ required? ? :string : :boolean
+ when Numeric
+ :numeric
+ when Symbol
+ :string
+ when Hash, Array, String
+ @default.class.name.downcase.to_sym
+ end
+
+ # TODO: This should raise an ArgumentError in a future version of Bundler::Thor
+ warn "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
end
def dasherized?
@@ -119,7 +140,7 @@ class Bundler::Thor
end
def dasherize(str)
- (str.length > 1 ? "--" : "-") + str.gsub("_", "-")
+ (str.length > 1 ? "--" : "-") + str.tr("_", "-")
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
index deac6a0c16..3ce8f55f94 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
@@ -14,7 +14,7 @@ class Bundler::Thor
when true
"--#{key}"
when Array
- "--#{key} #{value.map { |v| v.inspect }.join(' ')}"
+ "--#{key} #{value.map(&:inspect).join(' ')}"
when Hash
"--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}"
when nil, false
@@ -40,7 +40,9 @@ class Bundler::Thor
@non_assigned_required.delete(hash_options[key])
end
- @shorts, @switches, @extra = {}, {}, []
+ @shorts = {}
+ @switches = {}
+ @extra = []
options.each do |option|
@switches[option.switch_name] = option
@@ -52,7 +54,7 @@ class Bundler::Thor
end
end
- def remaining # rubocop:disable TrivialAccessors
+ def remaining
@extra
end
@@ -119,7 +121,7 @@ class Bundler::Thor
def check_unknown!
# an unknown option starts with - or -- and has no more --'s afterward.
unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
- fail UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
+ raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
end
protected
@@ -207,7 +209,7 @@ class Bundler::Thor
elsif option.lazy_default
return option.lazy_default
else
- fail MalformattedArgumentError, "No value provided for option '#{switch}'"
+ raise MalformattedArgumentError, "No value provided for option '#{switch}'"
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb
index f0d7bfe2e0..4e96f7730a 100644
--- a/lib/bundler/vendor/thor/lib/thor/runner.rb
+++ b/lib/bundler/vendor/thor/lib/thor/runner.rb
@@ -11,10 +11,18 @@ require "pathname"
class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength
map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
+ def self.banner(command, all = false, subcommand = false)
+ "thor " + command.formatted_usage(self, all, subcommand)
+ end
+
+ def self.exit_on_failure?
+ true
+ end
+
# Override Bundler::Thor#help so it can give information about any class and any method.
#
def help(meth = nil)
- if meth && !self.respond_to?(meth)
+ if meth && !respond_to?(meth)
initialize_thorfiles(meth)
klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth)
self.class.handle_no_command_error(command, false) if klass.nil?
@@ -45,16 +53,18 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng
# command in said directory.
begin
if File.directory?(File.expand_path(name))
- base, package = File.join(name, "main.thor"), :directory
- contents = open(base) { |input| input.read }
+ base = File.join(name, "main.thor")
+ package = :directory
+ contents = open(base, &:read)
else
- base, package = name, :file
- contents = open(name) { |input| input.read }
+ base = name
+ package = :file
+ contents = open(name, &:read)
end
rescue OpenURI::HTTPError
raise Error, "Error opening URI '#{name}'"
rescue Errno::ENOENT
- fail Error, "Error opening file '#{name}'"
+ raise Error, "Error opening file '#{name}'"
end
say "Your Bundler::Thorfile contains:"
@@ -108,9 +118,9 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng
desc "uninstall NAME", "Uninstall a named Bundler::Thor module"
def uninstall(name)
- fail Error, "Can't find module '#{name}'" unless thor_yaml[name]
+ raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
say "Uninstalling #{name}."
- FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}"))
+ FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s))
thor_yaml.delete(name)
save_yaml(thor_yaml)
@@ -120,7 +130,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng
desc "update NAME", "Update a Bundler::Thor file from its original location"
def update(name)
- fail Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
+ raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
say "Updating '#{name}' from #{thor_yaml[name][:location]}"
@@ -138,9 +148,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng
filename = install(thor_yaml[name][:location])
end
- unless filename == old_filename
- File.delete(File.join(thor_root, old_filename))
- end
+ File.delete(File.join(thor_root, old_filename)) unless filename == old_filename
end
desc "installed", "List the installed Bundler::Thor modules and commands"
@@ -168,10 +176,6 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng
private
- def self.banner(command, all = false, subcommand = false)
- "thor " + command.formatted_usage(self, all, subcommand)
- end
-
def thor_root
Bundler::Thor::Util.thor_root
end
@@ -198,10 +202,6 @@ private
File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
end
- def self.exit_on_failure?
- true
- end
-
# Load the Bundler::Thorfiles. If relevant_to is supplied, looks for specific files
# in the thor_root instead of loading them all.
#
@@ -263,11 +263,11 @@ private
def thorfiles_relevant_to(meth)
lookup = [meth, meth.split(":")[0...-1].join(":")]
- files = thor_yaml.select do |k, v|
+ files = thor_yaml.select do |_, v|
v[:namespaces] && !(v[:namespaces] & lookup).empty?
end
- files.map { |k, v| File.join(thor_root, "#{v[:filename]}") }
+ files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) }
end
# Display information about the given klasses. If with_module is given,
@@ -276,7 +276,7 @@ private
def display_klasses(with_modules = false, show_internal = false, klasses = Bundler::Thor::Base.subclasses)
klasses -= [Bundler::Thor, Bundler::Thor::Runner, Bundler::Thor::Group] unless show_internal
- fail Error, "No Bundler::Thor commands available" if klasses.empty?
+ raise Error, "No Bundler::Thor commands available" if klasses.empty?
show_modules if with_modules && !thor_yaml.empty?
list = Hash.new { |h, k| h[k] = [] }
@@ -306,8 +306,8 @@ private
alias_method :display_tasks, :display_commands
def show_modules #:nodoc:
- info = []
- labels = %w[Modules Namespaces]
+ info = []
+ labels = %w(Modules Namespaces)
info << labels
info << ["-" * labels[0].size, "-" * labels[1].size]
diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb
index 91afdce2aa..e945549324 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell.rb
@@ -9,7 +9,7 @@ class Bundler::Thor
# it will use a colored log, otherwise it will use a basic one without color.
#
def shell
- @shell ||= if ENV["THOR_SHELL"] && ENV["THOR_SHELL"].size > 0
+ @shell ||= if ENV["THOR_SHELL"] && !ENV["THOR_SHELL"].empty?
Bundler::Thor::Shell.const_get(ENV["THOR_SHELL"])
elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"]
Bundler::Thor::Shell::Basic
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
index 278ffa3df0..52b6dfd225 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
@@ -3,14 +3,17 @@ require "io/console" if RUBY_VERSION > "1.9.2"
class Bundler::Thor
module Shell
- class Basic # rubocop:disable ClassLength
+ class Basic
attr_accessor :base
attr_reader :padding
# Initialize base, mute and padding to nil.
#
def initialize #:nodoc:
- @base, @mute, @padding, @always_force = nil, false, 0, false
+ @base = nil
+ @mute = false
+ @padding = 0
+ @always_force = false
end
# Mute everything that's inside given block
@@ -24,7 +27,7 @@ class Bundler::Thor
# Check if base is muted
#
- def mute? # rubocop:disable TrivialAccessors
+ def mute?
@mute
end
@@ -34,6 +37,15 @@ class Bundler::Thor
@padding = [0, value].max
end
+ # Sets the output padding while executing a block and resets it.
+ #
+ def indent(count = 1)
+ orig_padding = padding
+ self.padding = padding + count
+ yield
+ self.padding = orig_padding
+ end
+
# Asks something to the user and receives a response.
#
# If asked to limit the correct responses, you can pass in an
@@ -148,7 +160,9 @@ class Bundler::Thor
def print_table(array, options = {}) # rubocop:disable MethodLength
return if array.empty?
- formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth]
+ formats = []
+ indent = options[:indent].to_i
+ colwidth = options[:colwidth]
options[:truncate] = terminal_width if options[:truncate] == true
formats << "%-#{colwidth + 2}s" if colwidth
@@ -161,12 +175,12 @@ class Bundler::Thor
start.upto(colcount - 1) do |index|
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
maximas << maxima
- if index == colcount - 1
- # Don't output 2 trailing spaces when printing the last column
- formats << "%-s"
- else
- formats << "%-#{maxima + 2}s"
- end
+ formats << if index == colcount - 1
+ # Don't output 2 trailing spaces when printing the last column
+ "%-s"
+ else
+ "%-#{maxima + 2}s"
+ end
end
formats[0] = formats[0].insert(0, " " * indent)
@@ -178,15 +192,15 @@ class Bundler::Thor
row.each_with_index do |column, index|
maxima = maximas[index]
- if column.is_a?(Numeric)
+ f = if column.is_a?(Numeric)
if index == row.size - 1
# Don't output 2 trailing spaces when printing the last column
- f = "%#{maxima}s"
+ "%#{maxima}s"
else
- f = "%#{maxima}s "
+ "%#{maxima}s "
end
else
- f = formats[index]
+ formats[index]
end
sentence << f % column.to_s
end
@@ -211,7 +225,7 @@ class Bundler::Thor
paras = message.split("\n\n")
paras.map! do |unwrapped|
- unwrapped.strip.gsub(/\n/, " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
+ unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
end
paras.each do |para|
@@ -230,7 +244,7 @@ class Bundler::Thor
# destination<String>:: the destination file to solve conflicts
# block<Proc>:: an optional block that returns the value to be used in diff
#
- def file_collision(destination) # rubocop:disable MethodLength
+ def file_collision(destination)
return true if @always_force
options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
@@ -249,7 +263,7 @@ class Bundler::Thor
return @always_force = true
when is?(:quit)
say "Aborting..."
- fail SystemExit
+ raise SystemExit
when is?(:diff)
show_diff(destination, yield) if block_given?
say "Retrying..."
@@ -262,10 +276,10 @@ class Bundler::Thor
# This code was copied from Rake, available under MIT-LICENSE
# Copyright (c) 2003, 2004 Jim Weirich
def terminal_width
- if ENV["THOR_COLUMNS"]
- result = ENV["THOR_COLUMNS"].to_i
+ result = if ENV["THOR_COLUMNS"]
+ ENV["THOR_COLUMNS"].to_i
else
- result = unix? ? dynamic_width : 80
+ unix? ? dynamic_width : 80
end
result < 10 ? 80 : result
rescue
@@ -284,7 +298,7 @@ class Bundler::Thor
# Apply color to the given string with optional bold. Disabled in the
# Bundler::Thor::Shell::Basic class.
#
- def set_color(string, *args) #:nodoc:
+ def set_color(string, *) #:nodoc:
string
end
@@ -353,11 +367,11 @@ class Bundler::Thor
end
def dynamic_width_stty
- %x(stty size 2>/dev/null).split[1].to_i
+ `stty size 2>/dev/null`.split[1].to_i
end
def dynamic_width_tput
- %x(tput cols 2>/dev/null).to_i
+ `tput cols 2>/dev/null`.to_i
end
def unix?
@@ -370,7 +384,7 @@ class Bundler::Thor
if chars.length <= width
chars.join
else
- ( chars[0, width - 3].join) + "..."
+ chars[0, width - 3].join + "..."
end
end
end
@@ -381,7 +395,8 @@ class Bundler::Thor
end
else
def as_unicode
- old, $KCODE = $KCODE, "U"
+ old = $KCODE
+ $KCODE = "U"
yield
ensure
$KCODE = old
@@ -391,7 +406,7 @@ class Bundler::Thor
def ask_simply(statement, color, options)
default = options[:default]
message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
- message = prepare_message(message, color)
+ message = prepare_message(message, *color)
result = Bundler::Thor::LineEditor.readline(message, options)
return unless result
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
index 1e2d26cfc5..da289cb50c 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/color.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
@@ -134,7 +134,7 @@ class Bundler::Thor
# for diff.
#
def diff_lcs_loaded? #:nodoc:
- return true if defined?(Diff::LCS)
+ return true if defined?(Diff::LCS)
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
@diff_lcs_loaded = begin
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
index e1ea0de599..83d2054988 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/html.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
@@ -51,13 +51,13 @@ class Bundler::Thor
def set_color(string, *colors)
if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
html_colors = colors.map { |color| lookup_color(color) }
- "<span style=\"#{html_colors.join("; ")};\">#{string}</span>"
+ "<span style=\"#{html_colors.join('; ')};\">#{string}</span>"
else
color, bold = colors
html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
styles = [html_color]
styles << BOLD if bold
- "<span style=\"#{styles.join("; ")};\">#{string}</span>"
+ "<span style=\"#{styles.join('; ')};\">#{string}</span>"
end
end
@@ -68,7 +68,7 @@ class Bundler::Thor
#
# TODO: Implement #ask for Bundler::Thor::Shell::HTML
def ask(statement, color = nil)
- fail NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML"
+ raise NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML"
end
protected
@@ -111,7 +111,7 @@ class Bundler::Thor
# for diff.
#
def diff_lcs_loaded? #:nodoc:
- return true if defined?(Diff::LCS)
+ return true if defined?(Diff::LCS)
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
@diff_lcs_loaded = begin
diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb
index f4e98fc19f..5d03177a28 100644
--- a/lib/bundler/vendor/thor/lib/thor/util.rb
+++ b/lib/bundler/vendor/thor/lib/thor/util.rb
@@ -64,7 +64,7 @@ class Bundler::Thor
new_constants = Bundler::Thor::Base.subclasses.dup
Bundler::Thor::Base.subclasses.replace(old_constants)
- new_constants.map! { |c| c.namespace }
+ new_constants.map!(&:namespace)
new_constants.compact!
new_constants
end
@@ -72,7 +72,7 @@ class Bundler::Thor
# Returns the thor classes declared inside the given class.
#
def thor_classes_in(klass)
- stringfied_constants = klass.constants.map { |c| c.to_s }
+ stringfied_constants = klass.constants.map(&:to_s)
Bundler::Thor::Base.subclasses.select do |subclass|
next unless subclass.name
stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ""))
@@ -103,7 +103,7 @@ class Bundler::Thor
#
def camel_case(str)
return str if str !~ /_/ && str =~ /[A-Z]+.*/
- str.split("_").map { |i| i.capitalize }.join
+ str.split("_").map(&:capitalize).join
end
# Receives a namespace and tries to retrieve a Bundler::Thor or Bundler::Thor::Group class
@@ -135,7 +135,8 @@ class Bundler::Thor
klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":"))
end
unless klass # look for a Bundler::Thor::Group with the right name
- klass, command = Bundler::Thor::Util.find_by_namespace(namespace), nil
+ klass = Bundler::Thor::Util.find_by_namespace(namespace)
+ command = nil
end
if !klass && fallback # try a command in the default namespace
command = namespace
@@ -163,7 +164,7 @@ class Bundler::Thor
end
end
- def user_home # rubocop:disable MethodLength
+ def user_home
@@user_home ||= if ENV["HOME"]
ENV["HOME"]
elsif ENV["USERPROFILE"]
@@ -188,7 +189,7 @@ class Bundler::Thor
# Returns the root where thor files are located, depending on the OS.
#
def thor_root
- File.join(user_home, ".thor").gsub(/\\/, "/")
+ File.join(user_home, ".thor").tr('\\', "/")
end
# Returns the files in the thor root. On Windows thor_root will be something
@@ -216,7 +217,7 @@ class Bundler::Thor
# Return the path to the ruby interpreter taking into account multiple
# installations and windows extensions.
#
- def ruby_command # rubocop:disable MethodLength
+ def ruby_command
@ruby_command ||= begin
ruby_name = RbConfig::CONFIG["ruby_install_name"]
ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name)
diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb
index 74b020a5ab..a6d838b103 100644
--- a/lib/bundler/vendor/thor/lib/thor/version.rb
+++ b/lib/bundler/vendor/thor/lib/thor/version.rb
@@ -1,3 +1,3 @@
class Bundler::Thor
- VERSION = "0.19.1"
+ VERSION = "0.19.4"
end
diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb
index d5ae513c59..729ac6b6f5 100644
--- a/lib/bundler/vendored_persistent.rb
+++ b/lib/bundler/vendored_persistent.rb
@@ -6,7 +6,12 @@ begin
rescue LoadError
# some Ruby builds don't have OpenSSL
end
-
-vendor = File.expand_path("../vendor", __FILE__)
-$:.unshift(vendor) unless $:.include?(vendor)
-require "net/http/persistent"
+module Bundler
+ module Persistent
+ module Net
+ module HTTP
+ end
+ end
+ end
+end
+require "bundler/vendor/net-http-persistent/lib/net/http/persistent"
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 8fa8d18e84..35c3896482 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -7,5 +7,18 @@ module Bundler
# We're doing this because we might write tests that deal
# with other versions of bundler and we are unsure how to
# handle this better.
- VERSION = "1.13.0.rc.2" unless defined?(::Bundler::VERSION)
+ VERSION = "1.14.6" unless defined?(::Bundler::VERSION)
+
+ def self.overwrite_loaded_gem_version
+ begin
+ require "rubygems"
+ rescue LoadError
+ return
+ end
+ return unless bundler_spec = Gem.loaded_specs["bundler"]
+ return if bundler_spec.version == VERSION
+ bundler_spec.version = Bundler::VERSION
+ end
+ private_class_method :overwrite_loaded_gem_version
+ overwrite_loaded_gem_version
end
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
index 3ef5addcee..c6b96d815a 100644
--- a/lib/bundler/worker.rb
+++ b/lib/bundler/worker.rb
@@ -25,11 +25,8 @@ module Bundler
@request_queue = Queue.new
@response_queue = Queue.new
@func = func
- @threads = Array.new(size) do |i|
- Thread.start { process_queue(i) }.tap do |thread|
- thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
- end
- end
+ @size = size
+ @threads = nil
trap("INT") { abort_threads }
end
@@ -37,6 +34,7 @@ module Bundler
#
# @param obj [String] mostly it is name of spec that should be downloaded
def enq(obj)
+ create_threads unless @threads
@request_queue.enq obj
end
@@ -70,13 +68,38 @@ module Bundler
# Stop the worker threads by sending a poison object down the request queue
# so as worker threads after retrieving it, shut themselves down
def stop_threads
+ return unless @threads
@threads.each { @request_queue.enq POISON }
@threads.each(&:join)
+ @threads = nil
end
def abort_threads
+ return unless @threads
+ Bundler.ui.debug("\n#{caller.join("\n")}")
@threads.each(&:exit)
exit 1
end
+
+ def create_threads
+ creation_errors = []
+
+ @threads = Array.new(@size) do |i|
+ begin
+ Thread.start { process_queue(i) }.tap do |thread|
+ thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
+ end
+ rescue ThreadError => e
+ creation_errors << e
+ nil
+ end
+ end.compact
+
+ return if creation_errors.empty?
+
+ message = "Failed to create threads for the #{name} worker: #{creation_errors.map(&:to_s).uniq.join(", ")}"
+ raise ThreadCreationError, message if @threads.empty?
+ Bundler.ui.info message
+ end
end
end
diff --git a/man/bundle-check.ronn b/man/bundle-check.ronn
new file mode 100644
index 0000000000..b299da422f
--- /dev/null
+++ b/man/bundle-check.ronn
@@ -0,0 +1,26 @@
+bundle-check(1) -- Verifies if dependencies are satisfied by installed gems
+===========================================================================
+
+## SYNOPSIS
+
+`bundle check` [--dry-run]
+ [--gemfile=FILE]
+ [--path=PATH]
+
+## DESCRIPTION
+
+`check` searches the local machine for each of the gems requested in the
+Gemfile. If all gems are found, Bundler prints a success message and exits with
+a status of 0.
+
+If not, the first missing gem is listed and Bundler exits status 1.
+
+## OPTIONS
+
+* `--dry-run`:
+ Locks the `Gemfile(5)` before running the command.
+* `--gemfile`:
+ Use the specified gemfile instead of the `Gemfile(5)`
+* `--path`:
+ Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).
+ Bundler will remember this value for future installs on this machine.
diff --git a/man/bundle-clean.ronn b/man/bundle-clean.ronn
new file mode 100644
index 0000000000..de23991782
--- /dev/null
+++ b/man/bundle-clean.ronn
@@ -0,0 +1,18 @@
+bundle-clean(1) -- Cleans up unused gems in your bundler directory
+==================================================================
+
+## SYNOPSIS
+
+`bundle clean` [--dry-run] [--force]
+
+## DESCRIPTION
+
+This command will remove all unused gems in your bundler directory. This is
+useful when you have made many changes to your gem dependencies.
+
+## OPTIONS
+
+* `--dry-run`:
+ Print the changes, but do not clean the unused gems.
+* `--force`:
+ Force a clean even if `--path` is not set.
diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn
index 3ea6d10973..926adcbb09 100644
--- a/man/bundle-config.ronn
+++ b/man/bundle-config.ronn
@@ -65,7 +65,7 @@ The options that can be configured are:
The location to install the specified gems to. This defaults to Rubygems'
setting. Bundler shares this location with Rubygems, `gem install ...` will
have gem installed there, too. Therefore, gems installed without a
- `--path ...` setting will show up by calling `gem list`. Accodingly, gems
+ `--path ...` setting will show up by calling `gem list`. Accordingly, gems
installed to other locations will not get listed.
* `without`:
@@ -142,7 +142,7 @@ learn more about their operation in [bundle install(1)][bundle-install].
and key in PEM format.
* `cache_path` (`BUNDLE_CACHE_PATH`): The directory that bundler will place
cached gems in when running <code>bundle package</code>, and that bundler
- will look in when installing gems.
+ will look in when installing gems. Defaults to `vendor/bundle`.
* `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`): When set, Gemfiles
containing multiple sources will produce errors instead of warnings. Use
`bundle config --delete disable_multisource` to unset.
@@ -155,6 +155,73 @@ learn more about their operation in [bundle install(1)][bundle-install].
The number of redirects allowed for network requests. Defaults to `5`.
* `timeout` (`BUNDLE_TIMEOUT`):
The seconds allowed before timing out for network requests. Defaults to `10`.
+* `force_ruby_platform` (`BUNDLE_FORCE_RUBY_PLATFORM`):
+ Ignore the current machine's platform and install only `ruby` platform gems.
+ As a result, gems with native extensions will be compiled from source.
+* `specific_platform` (`BUNDLE_SPECIFIC_PLATFORM`):
+ Allow bundler to resolve for the specific running platform and store it in
+ the lockfile, instead of only using a generic platform.
+ A specific platform is the exact platform triple reported by
+ `Gem::Platform.local`, such as `x86_64-darwin-16` or `universal-java-1.8`.
+ On the other hand, generic platforms are those such as `ruby`, `mswin`, or
+ `java`. In this example, `x86_64-darwin-16` would map to `ruby` and
+ `universal-java-1.8` to `java`.
+* `disable_checksum_validation` (`BUNDLE_DISABLE_CHECKSUM_VALIDATION`):
+ Allow installing gems even if they do not match the checksum provided by
+ RubyGems.
+* `disable_version_check` (`BUNDLE_DISABLE_VERSION_CHECK`):
+ Stop Bundler from checking if a newer Bundler version is available on
+ rubygems.org.
+* `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`):
+ Allow Bundler to use cached data when installing without network access.
+* `auto_install` (`BUNDLE_AUTO_INSTALL`):
+ Automatically run `bundle install` when gems are missing.
+* `cache_all_platforms` (`BUNDLE_CACHE_ALL_PLATFORMS`):
+ Cache gems for all platforms.
+* `cache_all` (`BUNDLE_CACHE_ALL`):
+ Cache all gems, including path and git gems.
+* `clean` (`BUNDLE_CLEAN`):
+ Whether Bundler should run `bundle clean` automatically after
+ `bundle install`.
+* `console` (`BUNDLE_CONSOLE`):
+ The console that `bundle console` starts. Defaults to `irb`.
+* `disable_exec_load` (`BUNDLE_DISABLE_EXEC_LOAD`):
+ Stop Bundler from using `load` to launch an executable in-process in
+ `bundle exec`.
+* `disable_local_branch_check` (`BUNDLE_DISABLE_LOCAL_BRANCH_CHECK`):
+ Allow Bundler to use a local git override without a branch specified in the
+ Gemfile.
+* `disable_shared_gems` (`BUNDLE_DISABLE_SHARED_GEMS`):
+ Stop Bundler from accessing gems installed to RubyGems' normal location.
+* `jobs` (`BUNDLE_JOBS`):
+ The number of gems Bundler can install in parallel. Defaults to 1.
+* `major_deprecations` (`BUNDLE_MAJOR_DEPRECATIONS`):
+ Whether Bundler should print deprecation warnings for behavior that will
+ be changed in the next major version.
+* `no_install` (`BUNDLE_NO_INSTALL`):
+ Whether `bundle package` should skip installing gems.
+* `no_prune` (`BUNDLE_NO_PRUNE`):
+ Whether Bundler should leave outdated gems unpruned when caching.
+* `only_update_to_newer_versions` (`BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS`):
+ During `bundle update`, only resolve to newer versions of the gems in the
+ lockfile.
+* `plugins` (`BUNDLE_PLUGINS`):
+ Enable Bundler's experimental plugin system.
+* `shebang` (`BUNDLE_SHEBANG`):
+ The program name that should be invoked for generated binstubs. Defaults to
+ the ruby install name used to generate the binstub.
+* `silence_root_warning` (`BUNDLE_SILENCE_ROOT_WARNING`):
+ Silence the warning Bundler prints when installing gems as root.
+* `ssl_verify_mode` (`BUNDLE_SSL_VERIFY_MODE`):
+ The SSL verification mode Bundler uses when making HTTPS requests.
+ Defaults to verify peer.
+* `system_bindir` (`BUNDLE_SYSTEM_BINDIR`):
+ The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
+* `user_agent` (`BUNDLE_USER_AGENT`):
+ The custom user agent fragment Bundler includes in API requests.
+* `gem.push_key` (`BUNDLE_GEM__PUSH_KEY`):
+ Sets the `--key` paramter for `gem push` when using the `rake release`
+ command with a private gemstash server.
In general, you should set these settings per-application by using the applicable
flag to the [bundle install(1)][bundle-install] or [bundle package(1)][bundle-package] command.
@@ -204,10 +271,23 @@ mirror to fetch gems.
bundle config mirror.SOURCE_URL MIRROR_URL
-For example, to use a mirror of rubygems.org hosted at
+For example, to use a mirror of rubygems.org hosted at rubygems-mirror.org:
bundle config mirror.http://rubygems.org http://rubygems-mirror.org
+Each mirror also provides a fallback timeout setting. If the mirror does not
+respond within the fallback timeout, Bundler will try to use the original
+server instead of the mirror.
+
+ bundle config mirror.SOURCE_URL.fallback_timeout TIMEOUT
+
+For example, to fall back to rubygems.org after 3 seconds:
+
+ bundle config mirror.https://rubygems.org.fallback_timeout 3
+
+The default fallback timeout is 0.1 seconds, but the setting can currently
+only accept whole seconds (for example, 1, 15, or 30).
+
## CREDENTIALS FOR GEM SOURCES
Bundler allows you to configure credentials for any gem source, which allows
@@ -223,3 +303,16 @@ For example, to save the credentials of user `claudette` for the gem source at
Or you can set the credentials as an environment variable like this:
export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
+
+For gems with a git source with HTTP(S) URL you can specify credentials like so:
+
+ bundle config https://github.com/bundler/bundler.git username:password
+
+Or you can set the credentials as an environment variable like so:
+
+ export BUNDLE_GITHUB__COM=username:password
+
+This is especially useful for private repositories on hosts such as Github,
+where you can use personal OAuth tokens:
+
+ export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
diff --git a/man/bundle-exec.ronn b/man/bundle-exec.ronn
index ba6844c5c2..c9ab2309e4 100644
--- a/man/bundle-exec.ronn
+++ b/man/bundle-exec.ronn
@@ -63,6 +63,15 @@ It also modifies Rubygems:
making system executables work
* Add all gems in the bundle into Gem.loaded_specs
+### Loading
+
+By default, when attempting to `bundle exec` to a file with a ruby shebang,
+Bundler will `Kernel.load` that file instead of using `Kernel.exec`. For the
+vast majority of cases, this is a performance improvement. In a rare few cases,
+this could cause some subtle side-effects (such as dependence on the exact
+contents of `$0` or `__FILE__`) and the optimization can be disabled by enabling
+the `disable_exec_load` setting.
+
### Shelling out
Any Ruby code that opens a subshell (like `system`, backticks, or `%x{}`) will
diff --git a/man/bundle-info.ronn b/man/bundle-info.ronn
new file mode 100644
index 0000000000..47e457aa3c
--- /dev/null
+++ b/man/bundle-info.ronn
@@ -0,0 +1,17 @@
+bundle-info(1) -- Show information for the given gem in your bundle
+=========================================================================
+
+## SYNOPSIS
+
+`bundle info` [GEM]
+ [--path]
+
+## DESCRIPTION
+
+Print the basic information about the provided GEM such as homepage, version,
+path and summary.
+
+## OPTIONS
+
+* `--path`:
+Print the path of the given gem
diff --git a/man/bundle-init.ronn b/man/bundle-init.ronn
new file mode 100644
index 0000000000..e5dfd420e9
--- /dev/null
+++ b/man/bundle-init.ronn
@@ -0,0 +1,18 @@
+bundle-init(1) -- Generates a Gemfile into the current working directory
+========================================================================
+
+## SYNOPSIS
+
+`bundle init` [--gemspec=FILE]
+
+## DESCRIPTION
+
+Init generates a default `Gemfile(5)` in the current working directory. When
+adding a `Gemfile(5)` to a gem with a gemspec, the `--gemspec` option will
+automatically add each dependency listed in the gemspec file to the newly
+created `Gemfile(5)`.
+
+## OPTIONS
+
+* `--gemspec`:
+ Use the specified .gemspec to create the `Gemfile(5)`
diff --git a/man/bundle-inject.ronn b/man/bundle-inject.ronn
new file mode 100644
index 0000000000..02f5dd8bd2
--- /dev/null
+++ b/man/bundle-inject.ronn
@@ -0,0 +1,22 @@
+bundle-inject(1) -- Add named gem(s) with version requirements to Gemfile
+=========================================================================
+
+## SYNOPSIS
+
+`bundle inject` [GEM] [VERSION]
+
+## DESCRIPTION
+
+Adds the named gem(s) with their version requirements to the resolved
+`Gemfile(5)`.
+
+This command will add the gem to both your `Gemfile(5)` and Gemfile.lock if it
+isn't listed yet.
+
+Example:
+
+ bundle install
+ bundle inject 'rack' '> 0'
+
+This will inject the 'rack' gem with a version greater than 0 in your
+`Gemfile(5)` and Gemfile.lock
diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn
index 92d1d8d42c..3a03090512 100644
--- a/man/bundle-install.ronn
+++ b/man/bundle-install.ronn
@@ -110,7 +110,7 @@ time `bundle install` is run, use `bundle config` (see bundle-config(1)).
The location to install the specified gems to. This defaults to Rubygems'
setting. Bundler shares this location with Rubygems, `gem install ...` will
have gem installed there, too. Therefore, gems installed without a
- `--path ...` setting will show up by calling `gem list`. Accodingly, gems
+ `--path ...` setting will show up by calling `gem list`. Accordingly, gems
installed to other locations will not get listed.
* `--quiet`:
diff --git a/man/bundle-lock.ronn b/man/bundle-lock.ronn
index 8afc50af8a..31ecd3c877 100644
--- a/man/bundle-lock.ronn
+++ b/man/bundle-lock.ronn
@@ -7,6 +7,14 @@ bundle-lock(1) -- Creates / Updates a lockfile without installing
[--local]
[--print]
[--lockfile=PATH]
+ [--full-index]
+ [--add-platform]
+ [--remove-platform]
+ [--patch]
+ [--minor]
+ [--major]
+ [--strict]
+ [--conservative]
## DESCRIPTION
@@ -30,6 +38,31 @@ Lock the gems specified in Gemfile.
* `--lockfile=<path>`:
The path where the lockfile should be written to.
+* `--full-index`:
+ Fall back to using the single-file index of all gems.
+
+* `--add-platform`:
+ Add a new platform to the lockfile, re-resolving for the addition of that
+ platform.
+
+* `--remove-platform`:
+ Remove a platform from the lockfile.
+
+* `--patch`:
+ If updating, prefer updating only to next patch version.
+
+* `--minor`:
+ If updating, prefer updating only to next minor version.
+
+* `--major`:
+ If updating, prefer updating to next major version (default).
+
+* `--strict`:
+ If updating, do not allow any gem to be updated past latest --patch | --minor | --major.
+
+* `--conservative`:
+ If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated.
+
## UPDATING ALL GEMS
If you run `bundle lock` with `--update` option without list of gems, bundler will
@@ -45,3 +78,17 @@ For instance, you only want to update `nokogiri`, run `bundle lock --update noko
Bundler will update `nokogiri` and any of its dependencies, but leave the rest of the
gems that you specified locked to the versions in the `Gemfile.lock`.
+
+## SUPPORTING OTHER PLATFORMS
+
+If you want your bundle to support platforms other than the one you're running
+locally, you can run `bundle lock --add-platform PLATFORM` to add PLATFORM to
+the lockfile, force bundler to re-resolve and consider the new platform when
+picking gems, all without needing to have a machine that matches PLATFORM handy
+to install those platform-specific gems on.
+
+For a full explanation of gem platforms, see `gem help platform`.
+
+## PATCH LEVEL OPTIONS
+
+See [bundle update(1)][bundle-update] for details.
diff --git a/man/bundle-open.ronn b/man/bundle-open.ronn
new file mode 100644
index 0000000000..497beac93f
--- /dev/null
+++ b/man/bundle-open.ronn
@@ -0,0 +1,19 @@
+bundle-open(1) -- Opens the source directory for a gem in your bundle
+=====================================================================
+
+## SYNOPSIS
+
+`bundle open` [GEM]
+
+## DESCRIPTION
+
+Opens the source directory of the provided GEM in your editor.
+
+For this to work the `EDITOR` or `BUNDLER_EDITOR` environment variable has to
+be set.
+
+Example:
+
+ bundle open 'rack'
+
+Will open the source directory for the 'rack' gem in your bundle.
diff --git a/man/bundle-outdated.ronn b/man/bundle-outdated.ronn
new file mode 100644
index 0000000000..47792e61b2
--- /dev/null
+++ b/man/bundle-outdated.ronn
@@ -0,0 +1,107 @@
+bundle-outdated(1) -- List installed gems with newer versions available
+=======================================================================
+
+## SYNOPSIS
+
+`bundle outdated` [GEM] [--local]
+ [--pre]
+ [--source]
+ [--strict]
+ [--parseable | --porcelain]
+ [--group=GROUP]
+ [--groups]
+ [--update-strict]
+ [--patch|--minor|--major]
+ [--filter-major]
+ [--filter-minor]
+ [--filter-patch]
+
+## DESCRIPTION
+
+Outdated lists the names and versions of gems that have a newer version available
+in the given source. Calling outdated with [GEM [GEM]] will only check for newer
+versions of the given gems. Prerelease gems are ignored by default. If your gems
+are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
+
+## OPTIONS
+
+* `--local`:
+ Do not attempt to fetch gems remotely and use the gem cache instead.
+
+* `--pre`:
+ Check for newer pre-release gems.
+
+* `--source`:
+ Check against a specific source.
+
+* `--strict`:
+ Only list newer versions allowed by your Gemfile requirements.
+
+* `--parseable`:
+ Use minimal formatting for more parseable output.
+
+* `--group`:
+ List gems from a specific group.
+
+* `--groups`:
+ List gems organized by groups.
+
+* `--update-strict`:
+ Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor| --major.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--filter-major`:
+ Only list major newer versions.
+
+* `--filter-minor`:
+ Only list minor newer versions.
+
+* `--filter-patch`:
+ Only list patch newer versions.
+
+## PATCH LEVEL OPTIONS
+
+See [bundle update(1)][bundle-update] for details.
+
+One difference between the patch level options in `bundle update` and here is the `--strict` option.
+`--strict` was already an option on outdated before the patch level options were added. `--strict`
+wasn't altered, and the `--update-strict` option on `outdated` reflects what `--strict` does on
+`bundle update`.
+
+## FILTERING OUTPUT
+
+The 3 filtering options do not affect the resolution of versions, merely what versions are shown
+in the output.
+
+If the regular output shows the following:
+
+ * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+ * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+ * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+
+`--filter-major` would only show:
+
+ * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+
+`--filter-minor` would only show:
+
+ * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+
+`--filter-patch` would only show:
+
+ * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+
+Filter options can be combined. `--filter-minor` and `--filter-patch` would show:
+
+ * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+ * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+
+Combining all three `filter` options would be the same result as providing none of them.
diff --git a/man/bundle-show.ronn b/man/bundle-show.ronn
new file mode 100644
index 0000000000..714b6067aa
--- /dev/null
+++ b/man/bundle-show.ronn
@@ -0,0 +1,20 @@
+bundle-show(1) -- Shows all the gems in your bundle, or the path to a gem
+=========================================================================
+
+## SYNOPSIS
+
+`bundle show` [GEM]
+ [--paths]
+
+## DESCRIPTION
+
+Without the [GEM] option, `show` will print a list of the names and versions of
+all gems that are required by your `Gemfile(5)`.
+
+Calling show with [GEM] will list the exact location of that gem on your
+machine.
+
+## OPTIONS
+
+* `--paths`:
+ List the paths of all gems that are required by your `Gemfile(5)`.
diff --git a/man/bundle-update.ronn b/man/bundle-update.ronn
index 47d70f661c..7986a17bdc 100644
--- a/man/bundle-update.ronn
+++ b/man/bundle-update.ronn
@@ -3,7 +3,18 @@ bundle-update(1) -- Update your gems to the latest available versions
## SYNOPSIS
-`bundle update` <*gems> [--group=NAME] [--source=NAME] [--local] [--ruby]
+`bundle update` <*gems> [--group=NAME]
+ [--source=NAME]
+ [--local]
+ [--ruby]
+ [--bundler[=VERSION]]
+ [--full-index]
+ [--jobs=JOBS]
+ [--quiet]
+ [--force]
+ [--patch|--minor|--major]
+ [--strict]
+ [--conservative]
## DESCRIPTION
@@ -37,6 +48,33 @@ gem.
* `--bundler`:
Update the locked version of bundler to the invoked bundler version.
+* `--full-index`:
+ Fall back to using the single-file index of all gems.
+
+* `--jobs`:
+ Specify the number of jobs to run in parallel.
+
+* `--quiet`:
+ Only output warnings and errors.
+
+* `--force`:
+ Force downloading every gem.
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--strict`:
+ Do not allow any gem to be updated past latest `--patch` | `--minor` | `--major`.
+
+* `--conservative`:
+ Use bundle install conservative update behavior and do not allow shared dependencies to be updated.
+
## UPDATING ALL GEMS
If you run `bundle update` with no parameters, bundler will ignore
@@ -146,14 +184,125 @@ In this case, the two gems have their own set of dependencies, but they share
`bundle update thin` will update `rack` even though it's _also_ a dependency of
`rack-perftools_profiler`.
-`In short`, when you update a gem using `bundle update`, bundler will update all
-dependencies of that gem, including those that are also dependencies of another gem.
+In short, by default, when you update a gem using `bundle update`, bundler will
+update all dependencies of that gem, including those that are also dependencies
+of another gem.
+
+To prevent updating shared dependencies, prior to version 1.14 the only option
+was the `CONSERVATIVE UPDATING` behavior in [bundle install(1)][bundle-install]:
In this scenario, updating the `thin` version manually in the Gemfile(5),
and then running [bundle install(1)][bundle-install] will only update `daemons` and `eventmachine`,
but not `rack`. For more information, see the `CONSERVATIVE UPDATING` section
of [bundle install(1)][bundle-install].
+Starting with 1.14, specifying the `--conservative` option will also prevent shared
+dependencies from being updated.
+
+## PATCH LEVEL OPTIONS
+
+Version 1.14 introduced 4 patch-level options that will influence how gem
+versions are resolved. One of the following options can be used: `--patch`,
+`--minor` or `--major`. `--strict` can be added to further influence resolution.
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--strict`:
+ Do not allow any gem to be updated past latest `--patch` | `--minor` | `--major`.
+
+When Bundler is resolving what versions to use to satisfy declared
+requirements in the Gemfile or in parent gems, it looks up all
+available versions, filters out any versions that don't satisfy
+the requirement, and then, by default, sorts them from newest to
+oldest, considering them in that order.
+
+Providing one of the patch level options (e.g. `--patch`) changes the
+sort order of the satisfying versions, causing Bundler to consider the
+latest `--patch` or `--minor` version available before other versions.
+Note that versions outside the stated patch level could still be
+resolved to if necessary to find a suitable dependency graph.
+
+For example, if gem 'foo' is locked at 1.0.2, with no gem requirement
+defined in the Gemfile, and versions 1.0.3, 1.0.4, 1.1.0, 1.1.1, 2.0.0
+all exist, the default order of preference by default (`--major`) will
+be "2.0.0, 1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2".
+
+If the `--patch` option is used, the order of preference will change to
+"1.0.4, 1.0.3, 1.0.2, 1.1.1, 1.1.0, 2.0.0".
+
+If the `--minor` option is used, the order of preference will change to
+"1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2, 2.0.0".
+
+Combining the `--strict` option with any of the patch level options
+will remove any versions beyond the scope of the patch level option,
+to ensure that no gem is updated that far.
+
+To continue the previous example, if both `--patch` and `--strict`
+options are used, the available versions for resolution would be
+"1.0.4, 1.0.3, 1.0.2". If `--minor` and `--strict` are used, it would
+be "1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2".
+
+Gem requirements as defined in the Gemfile will still be the first
+determining factor for what versions are available. If the gem
+requirement for `foo` in the Gemfile is '~> 1.0', that will accomplish
+the same thing as providing the `--minor` and `--strict` options.
+
+## PATCH LEVEL EXAMPLES
+
+Given the following gem specifications:
+
+ foo 1.4.3, requires: ~> bar 2.0
+ foo 1.4.4, requires: ~> bar 2.0
+ foo 1.4.5, requires: ~> bar 2.1
+ foo 1.5.0, requires: ~> bar 2.1
+ foo 1.5.1, requires: ~> bar 3.0
+ bar with versions 2.0.3, 2.0.4, 2.1.0, 2.1.1, 3.0.0
+
+Gemfile:
+
+ gem 'foo'
+
+Gemfile.lock:
+
+ foo (1.4.3)
+ bar (~> 2.0)
+ bar (2.0.3)
+
+Cases:
+
+ # Command Line Result
+ ------------------------------------------------------------
+ 1 bundle update --patch 'foo 1.4.5', 'bar 2.1.1'
+ 2 bundle update --patch foo 'foo 1.4.5', 'bar 2.1.1'
+ 3 bundle update --minor 'foo 1.5.1', 'bar 3.0.0'
+ 4 bundle update --minor --strict 'foo 1.5.0', 'bar 2.1.1'
+ 5 bundle update --patch --strict 'foo 1.4.4', 'bar 2.0.4'
+
+In case 1, bar is upgraded to 2.1.1, a minor version increase, because
+the dependency from foo 1.4.5 required it.
+
+In case 2, only foo is requested to be unlocked, but bar is also
+allowed to move because it's not a declared dependency in the Gemfile.
+
+In case 3, bar goes up a whole major release, because a minor increase
+is preferred now for foo, and when it goes to 1.5.1, it requires 3.0.0
+of bar.
+
+In case 4, foo is preferred up to a minor version, but 1.5.1 won't work
+because the --strict flag removes bar 3.0.0 from consideration since
+it's a major increment.
+
+In case 5, both foo and bar have any minor or major increments removed
+from consideration because of the --strict flag, so the most they can
+move is up to 1.4.4 and 2.0.4.
+
## RECOMMENDED WORKFLOW
In general, when working with an application managed with bundler, you should
diff --git a/man/bundle-viz.ronn b/man/bundle-viz.ronn
new file mode 100644
index 0000000000..865bbf6ee3
--- /dev/null
+++ b/man/bundle-viz.ronn
@@ -0,0 +1,30 @@
+bundle-viz(1) -- Generates a visual dependency graph for your Gemfile
+=====================================================================
+
+## SYNOPSIS
+
+`bundle viz` [--file=FILE]
+ [--format=FORMAT]
+ [--requirements]
+ [--version]
+ [--without=GROUP GROUP]
+
+## DESCRIPTION
+
+`viz` generates a PNG file of the current `Gemfile(5)` as a dependency graph.
+`viz` requires the ruby-graphviz gem (and its dependencies).
+
+The associated gems must also be installed via `bundle install(1)`.
+
+## OPTIONS
+
+* `--file`:
+ The name to use for the generated file. See `--format` option
+* `--format`:
+ This is output format option. Supported format is png, jpg, svg, dot ...
+* `--requirements`:
+ Set to show the version of each required dependency.
+* `--version`:
+ Set to show each gem version.
+* `--without`:
+ Exclude gems that are part of the specified named group.
diff --git a/man/bundle.ronn b/man/bundle.ronn
index 72faac048b..59fed42304 100644
--- a/man/bundle.ronn
+++ b/man/bundle.ronn
@@ -27,20 +27,20 @@ We divide `bundle` subcommands into primary commands and utilities.
## PRIMARY COMMANDS
-* [bundle install(1)][bundle-install]:
+* [`bundle install(1)`][bundle-install]:
Install the gems specified by the `Gemfile` or `Gemfile.lock`
-* [bundle update(1)][bundle-update]:
+* [`bundle update(1)`][bundle-update]:
Update dependencies to their latest versions
-* [bundle package(1)][bundle-package]:
+* [`bundle package(1)`][bundle-package]:
Package the .gem files required by your application into the
`vendor/cache` directory
-* [bundle exec(1)][bundle-exec]:
+* [`bundle exec(1)`][bundle-exec]:
Execute a script in the context of the current bundle
-* [bundle config(1)][bundle-config]:
+* [`bundle config(1)`][bundle-config]:
Specify and read configuration options for bundler
* `bundle help(1)`:
@@ -55,7 +55,7 @@ We divide `bundle` subcommands into primary commands and utilities.
* `bundle show(1)`:
Show the source location of a particular gem in the bundle
-* `bundle outdated(1)`:
+* [`bundle outdated(1)`][bundle-outdated]:
Show all of the outdated gems in the current bundle
* `bundle console(1)`:
@@ -64,7 +64,7 @@ We divide `bundle` subcommands into primary commands and utilities.
* `bundle open(1)`:
Open an installed gem in the editor
-* `bundle lock(1)`:
+* [`bundle lock(1)`][bundle-lock]:
Generate a lockfile for your dependencies
* `bundle viz(1)`:
@@ -73,10 +73,10 @@ We divide `bundle` subcommands into primary commands and utilities.
* `bundle init(1)`:
Generate a simple `Gemfile`, placed in the current directory
-* [bundle gem(1)][bundle-gem]:
+* [`bundle gem(1)`][bundle-gem]:
Create a simple gem, suitable for development with bundler
-* [bundle platform(1)][bundle-platform]:
+* [`bundle platform(1)`][bundle-platform]:
Display platform compatibility information
* `bundle clean(1)`:
@@ -91,6 +91,24 @@ When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES,
Bundler will try to find an executable on your path named `bundler-<command>`
and execute it, passing down any extra arguments to it.
+## BUNDLER TRAMPOLINING
+
+Bundler includes a feature called trampolining, designed to allow a single
+developer to work on multiple projects, each on different Bundler versions.
+The trampoline will infer the correct version of Bundler to use for each project
+and load that version instead of the version directly invoked (which is almost
+always the newest version installed locally).
+
+Bundler by default will use the Bundler version in the current directory to
+determine the version to trampoline to, reading from the `BUNDLED WITH` section.
+However, if the `BUNDLER_VERSION` environment variable is set, that version will
+override the lockfile inference and can be used in directories without a
+lockfile.
+
+Until the target version is Bundler 2 or later, `BUNDLE_TRAMPOLINE_FORCE` must
+be set for the trampoline to be used. Additionally, `BUNDLE_TRAMPOLINE_DISABLE` can
+be set to completely disable the trampoline.
+
## OBSOLETE
These commands are obsolete and should no longer be used
diff --git a/man/gemfile.5.ronn b/man/gemfile.5.ronn
index da2157e971..dead032ced 100644
--- a/man/gemfile.5.ronn
+++ b/man/gemfile.5.ronn
@@ -30,7 +30,7 @@ Sources are checked for gems following the heuristics described in
will print a warning after installing the gem indicating which source was used,
and listing the other sources where the gem is available. A specific source can
be selected for gems that need to use a non-standard repository, suppressing
-this warning, by using the [`:source` option](#SOURCE-source-) or a
+this warning, by using the [`:source` option](#SOURCE) or a
[`source` block](#BLOCK-FORM-OF-SOURCE-GIT-PATH-GROUP-and-PLATFORMS).
### CREDENTIALS
@@ -173,62 +173,38 @@ There are a number of `Gemfile` platforms:
* `ruby`:
C Ruby (MRI) or Rubinius, but `NOT` Windows
- * `ruby_18`:
- _ruby_ `AND` version 1.8
- * `ruby_19`:
- _ruby_ `AND` version 1.9
- * `ruby_20`:
- _ruby_ `AND` version 2.0
- * `ruby_21`:
- _ruby_ `AND` version 2.1
- * `ruby_22`:
- _ruby_ `AND` version 2.2
- * `ruby_23`:
- _ruby_ `AND` version 2.3
* `mri`:
Same as _ruby_, but not Rubinius
- * `mri_18`:
- _mri_ `AND` version 1.8
- * `mri_19`:
- _mri_ `AND` version 1.9
- * `mri_20`:
- _mri_ `AND` version 2.0
- * `mri_21`:
- _mri_ `AND` version 2.1
- * `mri_22`:
- _mri_ `AND` version 2.2
- * `mri_23`:
- _mri_ `AND` version 2.3
+ * `mingw`:
+ Windows 32 bit 'mingw32' platform (aka RubyInstaller)
+ * `x64_mingw`:
+ Windows 64 bit 'mingw32' platform (aka RubyInstaller x64)
* `rbx`:
Same as _ruby_, but only Rubinius (not MRI)
* `jruby`:
JRuby
* `mswin`:
Windows
+
+You can restrict further by platform and version for all platforms *except* for
+`rbx`, `jruby`, and `mswin`.
+
+To specify a version in addition to a platform, append the version number without
+the delimiter to the platform. For example, to specify that a gem should only be
+used on platforms with Ruby 2.3, use:
+
+ ruby_23
+
+The full list of platforms and supported versions includes:
+
+ * `ruby`:
+ 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+ * `mri`:
+ 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
* `mingw`:
- Windows 32 bit 'mingw32' platform (aka RubyInstaller)
- * `mingw_18`:
- _mingw_ `AND` version 1.8
- * `mingw_19`:
- _mingw_ `AND` version 1.9
- * `mingw_20`:
- _mingw_ `AND` version 2.0
- * `mingw_21`:
- _mingw_ `AND` version 2.1
- * `mingw_22`:
- _mingw_ `AND` version 2.2
- * `mingw_23`:
- _mingw_ `AND` version 2.3
+ 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
* `x64_mingw`:
- Windows 64 bit 'mingw32' platform (aka RubyInstaller x64)
- * `x64_mingw_20`:
- _x64_mingw_ `AND` version 2.0
- * `x64_mingw_21`:
- _x64_mingw_ `AND` version 2.1
- * `x64_mingw_22`:
- _x64_mingw_ `AND` version 2.2
- * `x64_mingw_23`:
- _x64_mingw_ `AND` version 2.3
+ 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
As with groups, you can specify one or more platforms:
@@ -257,7 +233,7 @@ back on global sources using the ordering described in [SOURCE PRIORITY][].
Selecting a specific source repository this way also suppresses the ambiguous
gem warning described above in
-[GLOBAL SOURCES (#source)](#GLOBAL-SOURCES-source-).
+[GLOBAL SOURCES (#source)](#GLOBAL-SOURCES).
### GIT
diff --git a/man/index.txt b/man/index.txt
index 9e46cebaa8..e3b38c46db 100644
--- a/man/index.txt
+++ b/man/index.txt
@@ -6,3 +6,10 @@ bundle-exec bundle-exec.1
bundle-config bundle-config.1
bundle-platform bundle-platform.1
bundle-gem bundle-gem.1
+bundle-clean bundle-clean.1
+bundle-check bundle-check.1
+bundle-init bundle-init.1
+bundle-inject bundle-inject.1
+bundle-open bundle-open.1
+bundle-show bundle-show.1
+bundle-viz bundle-viz.1
diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb
index 2ff9920614..268c0d99ac 100644
--- a/spec/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler_spec.rb
@@ -3,7 +3,7 @@
require "spec_helper"
require "bundler"
-describe Bundler do
+RSpec.describe Bundler do
describe "#load_gemspec_uncached" do
let(:app_gemspec_path) { tmp("test.gemspec") }
subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
@@ -143,12 +143,12 @@ describe Bundler do
describe "configuration" do
context "disable_shared_gems" do
- it "should unset GEM_PATH with nil" do
+ it "should unset GEM_PATH with empty string" do
env = {}
settings = { :disable_shared_gems => true }
Bundler.send(:configure_gem_path, env, settings)
expect(env.keys).to include("GEM_PATH")
- expect(env["GEM_PATH"]).to be_nil
+ expect(env["GEM_PATH"]).to eq ""
end
end
end
@@ -156,7 +156,7 @@ describe Bundler do
describe "#rm_rf" do
context "the directory is world writable" do
let(:bundler_ui) { Bundler.ui }
- it "should show a fridenly error" do
+ it "should raise a friendly error" do
allow(File).to receive(:exist?).and_return(true)
allow(FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
allow(File).to receive(:world_writable?).and_return(true)
@@ -170,4 +170,43 @@ EOF
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
+ 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
end
diff --git a/spec/bundler/cli_spec.rb b/spec/bundler/cli_spec.rb
index ac704fb41d..ec1d384851 100644
--- a/spec/bundler/cli_spec.rb
+++ b/spec/bundler/cli_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/cli"
-describe "bundle executable" do
+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
@@ -61,9 +61,72 @@ describe "bundle executable" do
expect(out).to start_with("Running `bundle config --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(err + out).to eq("Could not find command \"fail\".")
+ end
+ end
+
+ let(:bundler_version) { "1.1" }
+ let(:latest_version) { nil }
+ before do
+ 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) { "2.0" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(err + out).to eq(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To update, run `gem install bundler`
+Could not find command "fail".
+ EOS
+ end
+
+ context "and disable_version_check is set" do
+ before { bundle! "config disable_version_check true" }
+ include_examples "no warning"
+ end
+
+ context "and is a pre-release" do
+ let(:latest_version) { "2.0.0.pre.4" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(err + out).to eq(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To update, run `gem install bundler --pre`
+Could not find command "fail".
+ EOS
+ end
+ end
+ end
+ end
end
-describe "bundler executable" do
+RSpec.describe "bundler executable" do
it "shows the bundler version just as the `bundle` executable does" do
bundler "--version"
expect(out).to eq("Bundler version #{Bundler::VERSION}")
diff --git a/spec/bundler/compact_index_client/updater_spec.rb b/spec/bundler/compact_index_client/updater_spec.rb
new file mode 100644
index 0000000000..c1cae31956
--- /dev/null
+++ b/spec/bundler/compact_index_client/updater_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+require "spec_helper"
+require "net/http"
+require "bundler/compact_index_client"
+require "bundler/compact_index_client/updater"
+
+RSpec.describe Bundler::CompactIndexClient::Updater do
+ subject(:updater) { described_class.new(fetcher) }
+
+ let(:fetcher) { double(:fetcher) }
+
+ context "when the ETag header is missing" do
+ # Regression test for https://github.com/bundler/bundler/issues/5463
+
+ let(:response) { double(:response, :body => "") }
+ let(:local_path) { Pathname("/tmp/localpath") }
+ let(:remote_path) { double(:remote_path) }
+
+ 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
+end
diff --git a/spec/bundler/definition_spec.rb b/spec/bundler/definition_spec.rb
index c72f50f0d1..73d44a93ab 100644
--- a/spec/bundler/definition_spec.rb
+++ b/spec/bundler/definition_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/definition"
-describe Bundler::Definition do
+RSpec.describe Bundler::Definition do
describe "#lock" do
before do
allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
@@ -137,7 +137,7 @@ describe Bundler::Definition do
describe "initialize" do
context "gem version promoter" do
context "with lockfile" do
- before :each do
+ before do
install_gemfile <<-G
source "file://#{gem_repo1}"
gem "foo"
@@ -159,25 +159,119 @@ describe Bundler::Definition do
end
end
- def mock_source_list
- Class.new do
- def all_sources
- []
- end
+ context "eager unlock" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
- def path_sources
- []
- end
+ 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
- def rubygems_remotes
- []
- 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,
+ Bundler::SourceList.new,
+ unlock_hash_for_bundle_install
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked.include?("shared_dep")).to be_truthy
+ end
- def replace_sources!(arg)
- nil
- end
- end.new
+ 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,
+ Bundler::SourceList.new,
+ :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/dsl_spec.rb b/spec/bundler/dsl_spec.rb
index 00d36dd55f..0561cb7ddc 100644
--- a/spec/bundler/dsl_spec.rb
+++ b/spec/bundler/dsl_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Dsl do
+RSpec.describe Bundler::Dsl do
before do
@rubygems = double("rubygems")
allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
@@ -93,8 +93,8 @@ describe Bundler::Dsl do
end
describe "#gem" do
- [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :mri, :mri_18, :mri_19,
- :mri_20, :mri_21, :mri_22, :mri_23, :jruby, :rbx].each do |platform|
+ [: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].each do |platform|
it "allows #{platform} as a valid platform" do
subject.gem("foo", :platform => platform)
end
diff --git a/spec/bundler/endpoint_specification_spec.rb b/spec/bundler/endpoint_specification_spec.rb
index 6718b24971..0b8da840d2 100644
--- a/spec/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/endpoint_specification_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::EndpointSpecification do
+RSpec.describe Bundler::EndpointSpecification do
let(:name) { "foo" }
let(:version) { "1.0.0" }
let(:platform) { Gem::Platform::RUBY }
@@ -50,4 +50,17 @@ describe Bundler::EndpointSpecification do
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
end
diff --git a/spec/bundler/env_spec.rb b/spec/bundler/env_spec.rb
index 1be6cf46bc..269c323ac6 100644
--- a/spec/bundler/env_spec.rb
+++ b/spec/bundler/env_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/settings"
-describe Bundler::Env do
+RSpec.describe Bundler::Env do
let(:env) { described_class.new }
let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
@@ -49,9 +49,17 @@ describe Bundler::Env do
end
end
+ context "when there no Gemfile and print_gemfile is true" do
+ let(:output) { env.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
- <<-GEMSPEC.gsub(/^\s+/, "")
+ strip_whitespace(<<-GEMSPEC)
Gem::Specification.new do |gem|
gem.name = "foo"
gem.author = "Fumofu"
@@ -68,7 +76,7 @@ describe Bundler::Env do
end
it "prints the gemspec" do
- output = env.report(:print_gemspecs => true).gsub(/^\s+/, "")
+ output = env.report(:print_gemspecs => true)
expect(output).to include("foo.gemspec")
expect(output).to include(gemspec)
diff --git a/spec/bundler/environment_preserver_spec.rb b/spec/bundler/environment_preserver_spec.rb
index 496646d654..41d2650055 100644
--- a/spec/bundler/environment_preserver_spec.rb
+++ b/spec/bundler/environment_preserver_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::EnvironmentPreserver do
+RSpec.describe Bundler::EnvironmentPreserver do
let(:preserver) { described_class.new(env, ["foo"]) }
describe "#backup" do
diff --git a/spec/bundler/fetcher/base_spec.rb b/spec/bundler/fetcher/base_spec.rb
index bd1c03660c..38b69429bc 100644
--- a/spec/bundler/fetcher/base_spec.rb
+++ b/spec/bundler/fetcher/base_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Fetcher::Base do
+RSpec.describe Bundler::Fetcher::Base do
let(:downloader) { double(:downloader) }
let(:remote) { double(:remote) }
let(:display_uri) { "http://sample_uri.com" }
diff --git a/spec/bundler/fetcher/compact_index_spec.rb b/spec/bundler/fetcher/compact_index_spec.rb
index f6c6ba2ee1..e653c1ea43 100644
--- a/spec/bundler/fetcher/compact_index_spec.rb
+++ b/spec/bundler/fetcher/compact_index_spec.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Fetcher::CompactIndex do
+RSpec.describe Bundler::Fetcher::CompactIndex do
let(:downloader) { double(:downloader) }
- let(:remote) { double(:remote, :cache_slug => "lsjdf") }
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
@@ -15,7 +15,7 @@ describe Bundler::Fetcher::CompactIndex do
it "has only one thread open at the end of the run" do
compact_index.specs_for_names(["lskdjf"])
- thread_count = Thread.list.select {|thread| thread.status == "run" }.count
+ thread_count = Thread.list.count {|thread| thread.status == "run" }
expect(thread_count).to eq 1
end
@@ -25,6 +25,46 @@ describe Bundler::Fetcher::CompactIndex do
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
+ before { stub_const("OpenSSL::OPENSSL_FIPS", true) }
+
+ 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 }
diff --git a/spec/bundler/fetcher/dependency_spec.rb b/spec/bundler/fetcher/dependency_spec.rb
index bf7749d07a..134ca1bc57 100644
--- a/spec/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/fetcher/dependency_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Fetcher::Dependency do
+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" }
@@ -262,13 +262,13 @@ describe Bundler::Fetcher::Dependency do
let(:uri) { URI("http://gem-api.com") }
context "with gem names" do
- let(:gem_names) { [%w(foo bar), %w(bundler rubocop)] }
+ 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=foo%2Cbar%2Cbundler%2Crubocop"
+ "http://gem-api.com/api/v1/dependencies?gems=bar%2Cbundler%2Cfoo%2Crubocop"
)
end
end
diff --git a/spec/bundler/fetcher/downloader_spec.rb b/spec/bundler/fetcher/downloader_spec.rb
index 8371bcfa6c..8b26b8e415 100644
--- a/spec/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/fetcher/downloader_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Fetcher::Downloader do
+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") }
diff --git a/spec/bundler/fetcher/index_spec.rb b/spec/bundler/fetcher/index_spec.rb
index f81a655c24..b17e0d1727 100644
--- a/spec/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/fetcher/index_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Fetcher::Index do
+RSpec.describe Bundler::Fetcher::Index do
let(:downloader) { nil }
let(:remote) { nil }
let(:display_uri) { "http://sample_uri.com" }
diff --git a/spec/bundler/fetcher_spec.rb b/spec/bundler/fetcher_spec.rb
index 015eb81fd9..5244fc2b18 100644
--- a/spec/bundler/fetcher_spec.rb
+++ b/spec/bundler/fetcher_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/fetcher"
-describe Bundler::Fetcher do
+RSpec.describe Bundler::Fetcher do
let(:uri) { URI("https://example.com") }
let(:remote) { double("remote", :uri => uri, :original_uri => nil) }
diff --git a/spec/bundler/friendly_errors_spec.rb b/spec/bundler/friendly_errors_spec.rb
index 1d88403c0e..19799d5495 100644
--- a/spec/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/friendly_errors_spec.rb
@@ -4,7 +4,7 @@ require "bundler"
require "bundler/friendly_errors"
require "cgi"
-describe Bundler, "friendly errors" do
+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|
@@ -44,14 +44,194 @@ describe Bundler, "friendly errors" do
end
end
- it "rescues Thor::AmbiguousTaskError and raises SystemExit" do
+ 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 Thor::AmbiguousTaskError.new("")
+ 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")
diff --git a/spec/bundler/gem_helper_spec.rb b/spec/bundler/gem_helper_spec.rb
index 4e1af32c11..498ed89447 100644
--- a/spec/bundler/gem_helper_spec.rb
+++ b/spec/bundler/gem_helper_spec.rb
@@ -3,7 +3,7 @@ require "spec_helper"
require "rake"
require "bundler/gem_helper"
-describe Bundler::GemHelper do
+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") }
@@ -160,9 +160,10 @@ describe Bundler::GemHelper do
it "gem is installed" do
mock_build_message app_name, app_version
mock_confirm_message "#{app_name} (#{app_version}) installed."
- subject.install_gem
+ subject.install_gem(nil, :local)
expect(app_gem_path).to exist
- expect(`gem list`).to include("#{app_name} (#{app_version})")
+ gem_command! :list
+ expect(out).to include("#{app_name} (#{app_version})")
end
end
diff --git a/spec/bundler/gem_version_promoter_spec.rb b/spec/bundler/gem_version_promoter_spec.rb
index 9e5a7bb581..c7620e2620 100644
--- a/spec/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/gem_version_promoter_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::GemVersionPromoter do
+RSpec.describe Bundler::GemVersionPromoter do
context "conservative resolver" do
def versions(result)
result.flatten.map(&:version).map(&:to_s)
diff --git a/spec/bundler/index_spec.rb b/spec/bundler/index_spec.rb
index da8e5731ca..09b09e08fa 100644
--- a/spec/bundler/index_spec.rb
+++ b/spec/bundler/index_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Index do
+RSpec.describe Bundler::Index do
let(:specs) { [] }
subject { described_class.build {|i| i.use(specs) } }
@@ -26,4 +26,12 @@ describe Bundler::Index do
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/installer/gem_installer_spec.rb b/spec/bundler/installer/gem_installer_spec.rb
new file mode 100644
index 0000000000..de5189eecd
--- /dev/null
+++ b/spec/bundler/installer/gem_installer_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+require "spec_helper"
+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("build.dummy").and_return("--with-dummy-config=dummy")
+ allow(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/installer/parallel_installer_spec.rb b/spec/bundler/installer/parallel_installer_spec.rb
new file mode 100644
index 0000000000..7d2c441399
--- /dev/null
+++ b/spec/bundler/installer/parallel_installer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+require "spec_helper"
+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/install/parallel/spec_installation_spec.rb b/spec/bundler/installer/spec_installation_spec.rb
index 7bbd2bd0e6..1e368ab7c5 100644
--- a/spec/install/parallel/spec_installation_spec.rb
+++ b/spec/bundler/installer/spec_installation_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/installer/parallel_installer"
-describe Bundler::ParallelInstaller::SpecInstallation do
+RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
let!(:dep) do
a_spec = Object.new
def a_spec.name
@@ -58,20 +58,5 @@ describe Bundler::ParallelInstaller::SpecInstallation do
expect(spec.dependencies_installed?(all_specs)).to be_falsey
end
end
-
- context "when dependencies that are not on the overall installation list are the only ones not installed" do
- it "raises an error" do
- dependencies = []
- dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => true, :all_dependencies => [], :type => :production)
- all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
- # Add dependency which is not in all_specs
- dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => false, :all_dependencies => [], :type => :production)
- dependencies << instance_double("SpecInstallation", :spec => "delta", :name => "delta", :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 raise_error(Bundler::LockfileError, /Your Gemfile.lock is corrupt\. The following.*'beta' 'delta'/)
- end
- end
end
end
diff --git a/spec/bundler/lockfile_parser_spec.rb b/spec/bundler/lockfile_parser_spec.rb
index 98d7b68c6e..17bb447194 100644
--- a/spec/bundler/lockfile_parser_spec.rb
+++ b/spec/bundler/lockfile_parser_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/lockfile_parser"
-describe Bundler::LockfileParser do
+RSpec.describe Bundler::LockfileParser do
let(:lockfile_contents) { strip_whitespace(<<-L) }
GIT
remote: https://github.com/alloy/peiji-san.git
diff --git a/spec/bundler/mirror_spec.rb b/spec/bundler/mirror_spec.rb
index eb0ccf0bdf..9051a80465 100644
--- a/spec/bundler/mirror_spec.rb
+++ b/spec/bundler/mirror_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/mirror"
-describe Bundler::Settings::Mirror do
+RSpec.describe Bundler::Settings::Mirror do
let(:mirror) { Bundler::Settings::Mirror.new }
it "returns zero when fallback_timeout is not set" do
@@ -131,10 +131,20 @@ describe Bundler::Settings::Mirror do
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
-describe Bundler::Settings::Mirrors do
+RSpec.describe Bundler::Settings::Mirrors do
let(:localhost_uri) { URI("http://localhost:9292") }
context "with a just created mirror" do
@@ -283,7 +293,7 @@ describe Bundler::Settings::Mirrors do
end
end
-describe Bundler::Settings::TCPSocketProbe do
+RSpec.describe Bundler::Settings::TCPSocketProbe do
let(:probe) { Bundler::Settings::TCPSocketProbe.new }
context "with a listening TCP Server" do
diff --git a/spec/bundler/plugin/api/source_spec.rb b/spec/bundler/plugin/api/source_spec.rb
index d62127a604..4dbb993b89 100644
--- a/spec/bundler/plugin/api/source_spec.rb
+++ b/spec/bundler/plugin/api/source_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::API::Source do
+RSpec.describe Bundler::Plugin::API::Source do
let(:uri) { "uri://to/test" }
let(:type) { "spec_type" }
diff --git a/spec/bundler/plugin/api_spec.rb b/spec/bundler/plugin/api_spec.rb
index 0eba52301a..e40b9adb0f 100644
--- a/spec/bundler/plugin/api_spec.rb
+++ b/spec/bundler/plugin/api_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::API do
+RSpec.describe Bundler::Plugin::API do
context "plugin declarations" do
before do
stub_const "UserPluginClass", Class.new(Bundler::Plugin::API)
diff --git a/spec/bundler/plugin/dsl_spec.rb b/spec/bundler/plugin/dsl_spec.rb
index 9a694833ff..cd15b6ea9d 100644
--- a/spec/bundler/plugin/dsl_spec.rb
+++ b/spec/bundler/plugin/dsl_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::DSL do
+RSpec.describe Bundler::Plugin::DSL do
DSL = Bundler::Plugin::DSL
subject(:dsl) { Bundler::Plugin::DSL.new }
diff --git a/spec/bundler/plugin/index_spec.rb b/spec/bundler/plugin/index_spec.rb
index 5a754f355c..24b9a408ff 100644
--- a/spec/bundler/plugin/index_spec.rb
+++ b/spec/bundler/plugin/index_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::Index do
+RSpec.describe Bundler::Plugin::Index do
Index = Bundler::Plugin::Index
before do
@@ -31,7 +31,7 @@ describe Bundler::Plugin::Index do
expect(new_index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
end
- it "load_paths are persistant" do
+ 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
@@ -95,7 +95,7 @@ describe Bundler::Plugin::Index do
allow(File).to receive(:open).and_yield(file)
end
- it "should not save it with next registed hook" do
+ 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
diff --git a/spec/bundler/plugin/installer_spec.rb b/spec/bundler/plugin/installer_spec.rb
index 9d6eb1c55b..2454ef00f9 100644
--- a/spec/bundler/plugin/installer_spec.rb
+++ b/spec/bundler/plugin/installer_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::Installer do
+RSpec.describe Bundler::Plugin::Installer do
subject(:installer) { Bundler::Plugin::Installer.new }
describe "cli install" do
diff --git a/spec/bundler/plugin/source_list_spec.rb b/spec/bundler/plugin/source_list_spec.rb
index 774156b27c..86cc4ac4ed 100644
--- a/spec/bundler/plugin/source_list_spec.rb
+++ b/spec/bundler/plugin/source_list_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin::SourceList do
+RSpec.describe Bundler::Plugin::SourceList do
SourceList = Bundler::Plugin::SourceList
before do
diff --git a/spec/bundler/plugin_spec.rb b/spec/bundler/plugin_spec.rb
index 540278036c..5bbb7384c8 100644
--- a/spec/bundler/plugin_spec.rb
+++ b/spec/bundler/plugin_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Plugin do
+RSpec.describe Bundler::Plugin do
Plugin = Bundler::Plugin
let(:installer) { double(:installer) }
diff --git a/spec/bundler/psyched_yaml_spec.rb b/spec/bundler/psyched_yaml_spec.rb
index 4b7715b482..18e40d6b5a 100644
--- a/spec/bundler/psyched_yaml_spec.rb
+++ b/spec/bundler/psyched_yaml_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/psyched_yaml"
-describe "Bundler::YamlLibrarySyntaxError" do
+RSpec.describe "Bundler::YamlLibrarySyntaxError" do
it "is raised on YAML parse errors" do
expect { YAML.parse "{foo" }.to raise_error(Bundler::YamlLibrarySyntaxError)
end
diff --git a/spec/bundler/remote_specification_spec.rb b/spec/bundler/remote_specification_spec.rb
index 28d78a82c7..69a2ab3d67 100644
--- a/spec/bundler/remote_specification_spec.rb
+++ b/spec/bundler/remote_specification_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::RemoteSpecification do
+RSpec.describe Bundler::RemoteSpecification do
let(:name) { "foo" }
let(:version) { Gem::Version.new("1.0.0") }
let(:platform) { Gem::Platform::RUBY }
@@ -128,8 +128,8 @@ describe Bundler::RemoteSpecification do
end
describe "#__swap__" do
- let(:spec) { double(:spec) }
- let(:new_spec) { double(:new_spec) }
+ let(:spec) { double(:spec, :dependencies => []) }
+ let(:new_spec) { double(:new_spec, :runtime_dependencies => []) }
before { subject.instance_variable_set(:@_remote_specification, spec) }
@@ -158,32 +158,30 @@ describe Bundler::RemoteSpecification do
describe "method missing" do
context "and is present in Gem::Specification" do
- let(:remote_spec) { double(:remote_spec) }
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
before do
- allow_any_instance_of(Gem::Specification).to receive(:respond_to?).and_return(true)
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(remote_spec).to receive(:send).with(:missing_method_call).once
- subject.missing_method_call
+ 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) }
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
before do
- allow_any_instance_of(Gem::Specification).to receive(:respond_to?).and_return(false)
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(remote_spec).to receive(:respond_to?).with(:missing_method_call, false).once
- subject.respond_to?(:missing_method_call)
+ expect(subject.respond_to?(:authors)).to be_truthy
end
end
end
diff --git a/spec/bundler/retry_spec.rb b/spec/bundler/retry_spec.rb
index 665ba9f2df..525f05d327 100644
--- a/spec/bundler/retry_spec.rb
+++ b/spec/bundler/retry_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Retry do
+RSpec.describe Bundler::Retry do
it "return successful result if no errors" do
attempts = 0
result = Bundler::Retry.new(nil, nil, 3).attempt do
@@ -65,10 +65,10 @@ describe Bundler::Retry do
end
end
- context "with debugging on" do
+ 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("")
+ expect(Bundler.ui).to receive(:info).with("").twice
expect(Bundler.ui).to receive(:warn).with(failure_message, false)
expect do
diff --git a/spec/bundler/ruby_dsl_spec.rb b/spec/bundler/ruby_dsl_spec.rb
index ac73747ac7..3e0ec9d7f0 100644
--- a/spec/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/ruby_dsl_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/ruby_dsl"
-describe Bundler::RubyDsl do
+RSpec.describe Bundler::RubyDsl do
class MockDSL
include Bundler::RubyDsl
diff --git a/spec/bundler/ruby_version_spec.rb b/spec/bundler/ruby_version_spec.rb
index abcd0303d0..f77ec606fc 100644
--- a/spec/bundler/ruby_version_spec.rb
+++ b/spec/bundler/ruby_version_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/ruby_version"
-describe "Bundler::RubyVersion and its subclasses" do
+RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:version) { "2.0.0" }
let(:patchlevel) { "645" }
let(:engine) { "jruby" }
@@ -35,6 +35,14 @@ describe "Bundler::RubyVersion and its subclasses" do
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 }
diff --git a/spec/bundler/rubygems_integration_spec.rb b/spec/bundler/rubygems_integration_spec.rb
index fbc49c414c..37eb499e38 100644
--- a/spec/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/rubygems_integration_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::RubygemsIntegration do
+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
diff --git a/spec/bundler/settings_spec.rb b/spec/bundler/settings_spec.rb
index 0f7d2a0138..020897882c 100644
--- a/spec/bundler/settings_spec.rb
+++ b/spec/bundler/settings_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/settings"
-describe Bundler::Settings do
+RSpec.describe Bundler::Settings do
subject(:settings) { described_class.new(bundled_app) }
describe "#set_local" do
@@ -51,9 +51,41 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
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
@@ -96,6 +128,18 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
end
+ describe "#temporary" do
+ it "reset after used" do
+ Bundler.settings[: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
+ end
+
describe "#set_global" do
context "when it's not possible to write to the file" do
it "raises an PermissionError with explanation" do
diff --git a/spec/bundler/shared_helpers_spec.rb b/spec/bundler/shared_helpers_spec.rb
index 4c0d61cf0a..8bd7215666 100644
--- a/spec/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/shared_helpers_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::SharedHelpers do
+RSpec.describe Bundler::SharedHelpers do
let(:ext_lock_double) { double(:ext_lock) }
before do
@@ -234,7 +234,9 @@ describe Bundler::SharedHelpers do
shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
- before { allow(File).to receive(:expand_path).and_return(ruby_lib_path) }
+ 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
@@ -324,7 +326,6 @@ describe Bundler::SharedHelpers do
let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
before do
- allow(File).to receive(:expand_path).and_return(ruby_lib_path)
ENV["RUBYLIB"] = ruby_lib_path
end
@@ -387,6 +388,27 @@ describe Bundler::SharedHelpers do
)
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
diff --git a/spec/bundler/source/git/git_proxy_spec.rb b/spec/bundler/source/git/git_proxy_spec.rb
index ce6b79b2b2..34fe21e9fb 100644
--- a/spec/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/source/git/git_proxy_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Source::Git::GitProxy do
+RSpec.describe Bundler::Source::Git::GitProxy do
let(:uri) { "https://github.com/bundler/bundler.git" }
subject { described_class.new(Pathname("path"), uri, "HEAD") }
diff --git a/spec/bundler/source/rubygems/remote_spec.rb b/spec/bundler/source/rubygems/remote_spec.rb
index a541bf1468..54394fc0ca 100644
--- a/spec/bundler/source/rubygems/remote_spec.rb
+++ b/spec/bundler/source/rubygems/remote_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/source/rubygems/remote"
-describe Bundler::Source::Rubygems::Remote do
+RSpec.describe Bundler::Source::Rubygems::Remote do
def remote(uri)
Bundler::Source::Rubygems::Remote.new(uri)
end
diff --git a/spec/bundler/source/rubygems_spec.rb b/spec/bundler/source/rubygems_spec.rb
index 9fb4bf0ad3..b8f9f09c20 100644
--- a/spec/bundler/source/rubygems_spec.rb
+++ b/spec/bundler/source/rubygems_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Source::Rubygems do
+RSpec.describe Bundler::Source::Rubygems do
before do
allow(Bundler).to receive(:root) { Pathname.new("root") }
end
diff --git a/spec/bundler/source_list_spec.rb b/spec/bundler/source_list_spec.rb
index 3657ac0927..6a23c8bcbf 100644
--- a/spec/bundler/source_list_spec.rb
+++ b/spec/bundler/source_list_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::SourceList do
+RSpec.describe Bundler::SourceList do
before do
allow(Bundler).to receive(:root) { Pathname.new "./tmp/bundled_app" }
diff --git a/spec/bundler/source_spec.rb b/spec/bundler/source_spec.rb
index 4e99411a17..08d1698fcd 100644
--- a/spec/bundler/source_spec.rb
+++ b/spec/bundler/source_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::Source do
+RSpec.describe Bundler::Source do
class ExampleSource < Bundler::Source
end
@@ -61,7 +61,7 @@ describe Bundler::Source 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[32mwas < 1.5\e[0m)")
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6\e[32m (was < 1.5)\e[0m")
end
end
@@ -71,6 +71,32 @@ describe Bundler::Source do
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
diff --git a/spec/bundler/spec_set_spec.rb b/spec/bundler/spec_set_spec.rb
index 29d81cb30c..8f7c27f065 100644
--- a/spec/bundler/spec_set_spec.rb
+++ b/spec/bundler/spec_set_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::SpecSet do
+RSpec.describe Bundler::SpecSet do
let(:specs) do
[
build_spec("a", "1.0"),
diff --git a/spec/bundler/ssl_certs/certificate_manager_spec.rb b/spec/bundler/ssl_certs/certificate_manager_spec.rb
index cc8c8a49a1..51552f7234 100644
--- a/spec/bundler/ssl_certs/certificate_manager_spec.rb
+++ b/spec/bundler/ssl_certs/certificate_manager_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/ssl_certs/certificate_manager"
-describe Bundler::SSLCerts::CertificateManager do
+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") }
diff --git a/spec/bundler/ui_spec.rb b/spec/bundler/ui_spec.rb
new file mode 100644
index 0000000000..41c037527b
--- /dev/null
+++ b/spec/bundler/ui_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+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
+end
diff --git a/spec/bundler/uri_credentials_filter_spec.rb b/spec/bundler/uri_credentials_filter_spec.rb
index 70f71cecac..1dd01b4be0 100644
--- a/spec/bundler/uri_credentials_filter_spec.rb
+++ b/spec/bundler/uri_credentials_filter_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe Bundler::URICredentialsFilter do
+RSpec.describe Bundler::URICredentialsFilter do
subject { described_class }
describe "#credential_filtered_uri" do
diff --git a/spec/bundler/worker_spec.rb b/spec/bundler/worker_spec.rb
new file mode 100644
index 0000000000..fbfe6ddab3
--- /dev/null
+++ b/spec/bundler/worker_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require "spec_helper"
+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/yaml_serializer_spec.rb b/spec/bundler/yaml_serializer_spec.rb
index bf86d2a076..361159e0da 100644
--- a/spec/bundler/yaml_serializer_spec.rb
+++ b/spec/bundler/yaml_serializer_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/yaml_serializer"
-describe Bundler::YAMLSerializer do
+RSpec.describe Bundler::YAMLSerializer do
subject(:serializer) { Bundler::YAMLSerializer }
describe "#dump" do
diff --git a/spec/cache/cache_path_spec.rb b/spec/cache/cache_path_spec.rb
index 594162886a..ec6d6e312a 100644
--- a/spec/cache/cache_path_spec.rb
+++ b/spec/cache/cache_path_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle package" do
+RSpec.describe "bundle package" do
before do
gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/cache/gems_spec.rb b/spec/cache/gems_spec.rb
index 474233a83c..7828c87fec 100644
--- a/spec/cache/gems_spec.rb
+++ b/spec/cache/gems_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle cache" do
+RSpec.describe "bundle cache" do
describe "when there are only gemsources" do
before :each do
gemfile <<-G
@@ -238,7 +238,7 @@ describe "bundle cache" do
gem "platform_specific"
G
- expect(cached_gem("platform_specific-1.0-#{Gem::Platform.local}")).to exist
+ expect(cached_gem("platform_specific-1.0-#{Bundler.local_platform}")).to exist
expect(cached_gem("platform_specific-1.0-java")).to exist
end
diff --git a/spec/cache/git_spec.rb b/spec/cache/git_spec.rb
index c15ee26c25..b256c8d90f 100644
--- a/spec/cache/git_spec.rb
+++ b/spec/cache/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "git base name" do
+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")
@@ -14,7 +14,7 @@ describe "git base name" do
end
%w(cache package).each do |cmd|
- describe "bundle #{cmd} with git" do
+ 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)
diff --git a/spec/cache/path_spec.rb b/spec/cache/path_spec.rb
index 4233b3e88b..bbce448759 100644
--- a/spec/cache/path_spec.rb
+++ b/spec/cache/path_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
%w(cache package).each do |cmd|
- describe "bundle #{cmd} with path" do
+ 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")
diff --git a/spec/cache/platform_spec.rb b/spec/cache/platform_spec.rb
index 259eff0e15..ed80c949aa 100644
--- a/spec/cache/platform_spec.rb
+++ b/spec/cache/platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle cache with multiple platforms" do
+RSpec.describe "bundle cache with multiple platforms" do
before :each do
gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb
index 0f8c5af391..cb0999348e 100644
--- a/spec/commands/binstubs_spec.rb
+++ b/spec/commands/binstubs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle binstubs <gem>" do
+RSpec.describe "bundle binstubs <gem>" do
context "when the gem exists in the lockfile" do
it "sets up the binstub" do
install_gemfile <<-G
@@ -157,7 +157,7 @@ describe "bundle binstubs <gem>" do
it "includes the standalone path" do
bundle "binstubs rack --standalone"
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"
+ expect(standalone_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
end
end
diff --git a/spec/commands/check_spec.rb b/spec/commands/check_spec.rb
index 42d6459760..532be07c3f 100644
--- a/spec/commands/check_spec.rb
+++ b/spec/commands/check_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle check" do
+RSpec.describe "bundle check" do
it "returns success when the Gemfile is satisfied" do
install_gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/commands/clean_spec.rb b/spec/commands/clean_spec.rb
index 72d422c3a2..8fd4b6fbe8 100644
--- a/spec/commands/clean_spec.rb
+++ b/spec/commands/clean_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle clean" do
+RSpec.describe "bundle clean" do
def should_have_gems(*gems)
gems.each do |g|
expect(vendored_gems("gems/#{g}")).to exist
@@ -567,7 +567,7 @@ describe "bundle clean" do
expect(exitstatus).to eq(0) if exitstatus
end
- it "doesn't remove gems in dry-run mode" do
+ it "doesn't remove gems in dry-run mode with path set" do
gemfile <<-G
source "file://#{gem_repo1}"
@@ -595,6 +595,36 @@ describe "bundle clean" do
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 --path vendor/bundle --no-clean"
+
+ 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}"
diff --git a/spec/commands/config_spec.rb b/spec/commands/config_spec.rb
index 15c1c365c8..640ec06e17 100644
--- a/spec/commands/config_spec.rb
+++ b/spec/commands/config_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe ".bundle/config" do
+RSpec.describe ".bundle/config" do
before :each do
gemfile <<-G
source "file://#{gem_repo1}"
@@ -319,7 +319,7 @@ E
end
end
-describe "setting gemfile via config" do
+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|
diff --git a/spec/commands/console_spec.rb b/spec/commands/console_spec.rb
index 131b47368b..de14b6db5f 100644
--- a/spec/commands/console_spec.rb
+++ b/spec/commands/console_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle console" do
+RSpec.describe "bundle console" do
before :each do
install_gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/commands/doctor_spec.rb b/spec/commands/doctor_spec.rb
index 8debeb55e4..7c6e48ce19 100644
--- a/spec/commands/doctor_spec.rb
+++ b/spec/commands/doctor_spec.rb
@@ -4,7 +4,7 @@ require "stringio"
require "bundler/cli"
require "bundler/cli/doctor"
-describe "bundle doctor" do
+RSpec.describe "bundle doctor" do
before(:each) do
@stdout = StringIO.new
diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb
index 42afbd24ba..3c5f2ca1dd 100644
--- a/spec/commands/exec_spec.rb
+++ b/spec/commands/exec_spec.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle exec" do
+RSpec.describe "bundle exec" do
+ let(:system_gems_to_install) { %w(rack-1.0.0 rack-0.9.1) }
before :each do
- system_gems "rack-1.0.0", "rack-0.9.1"
+ system_gems(system_gems_to_install)
end
it "activates the correct gem" do
@@ -271,35 +272,35 @@ describe "bundle exec" do
with_fake_man do
bundle "#{exec} --help cat"
end
- expect(out).to include(%(["#{root}/lib/bundler/man/bundle-exec"]))
+ 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}/lib/bundler/man/bundle-exec"]))
+ 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}/lib/bundler/man/bundle-exec"]))
+ 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}/lib/bundler/man/bundle-exec"]))
+ 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}/lib/bundler/man/bundle-exec"]))
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
end
end
end
@@ -513,6 +514,21 @@ describe "bundle exec" do
end
end
+ context "the executable is empty" 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 raises" do
let(:executable) { super() << "\nraise 'ERROR'" }
let(:exit_code) { 1 }
@@ -539,7 +555,7 @@ describe "bundle exec" do
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 or available on this machine.\e[0m
+\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
@@ -627,4 +643,68 @@ __FILE__: #{path.to_s.inspect}
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
+ 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) { "2.0.3" }
+ 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
+ RB
+ file.chmod(0o777)
+
+ aggregate_failures do
+ expect(bundle!("exec #{file}", :system_bundler => true, :artifice => nil)).to eq(expected)
+ expect(bundle!("exec bundle exec #{file}", :system_bundler => true, :artifice => nil)).to eq(expected)
+ expect(bundle!("exec ruby #{file}", :system_bundler => true, :artifice => nil)).to eq(expected)
+ end
+
+ # sanity check that we get the newer, custom version without bundler
+ sys_exec(file.to_s)
+ expect(err).to include("custom openssl should not be loaded")
+ end
+ end
+ end
end
diff --git a/spec/commands/help_spec.rb b/spec/commands/help_spec.rb
index d59346f615..790d26fda4 100644
--- a/spec/commands/help_spec.rb
+++ b/spec/commands/help_spec.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle help" do
+RSpec.describe "bundle help" do
# Rubygems 1.4+ no longer load gem plugins so this test is no longer needed
- rubygems_under_14 = Gem::Requirement.new("< 1.4").satisfied_by?(Gem::Version.new(Gem::VERSION))
- it "complains if older versions of bundler are installed", :if => rubygems_under_14 do
+ it "complains if older versions of bundler are installed", :rubygems => "< 1.4" do
system_gems "bundler-0.8.1"
bundle "help"
@@ -16,14 +15,14 @@ describe "bundle help" do
with_fake_man do
bundle "help gemfile"
end
- expect(out).to eq(%(["#{root}/lib/bundler/man/gemfile.5"]))
+ 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}/lib/bundler/man/bundle-install"]))
+ 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
@@ -34,8 +33,8 @@ describe "bundle help" do
end
it "still outputs the old help for commands that do not have man pages yet" do
- bundle "help check"
- expect(out).to include("Check searches the local machine")
+ 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
@@ -55,28 +54,28 @@ describe "bundle help" do
with_fake_man do
bundle "install --help"
end
- expect(out).to eq(%(["#{root}/lib/bundler/man/bundle-install"]))
+ 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}/lib/bundler/man/bundle-install"]))
+ 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}/lib/bundler/man/bundle-install"]))
+ 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}/lib/bundler/man/bundle-install"]))
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
end
it "has helpful output when using --help flag for a non-existent command" do
@@ -90,11 +89,11 @@ describe "bundle help" do
with_fake_man do
bundle "--help"
end
- expect(out).to eq(%(["#{root}/lib/bundler/man/bundle"]))
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
with_fake_man do
bundle "-h"
end
- expect(out).to eq(%(["#{root}/lib/bundler/man/bundle"]))
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
end
end
diff --git a/spec/commands/info_spec.rb b/spec/commands/info_spec.rb
new file mode 100644
index 0000000000..b37de765e4
--- /dev/null
+++ b/spec/commands/info_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+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" 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/commands/init_spec.rb b/spec/commands/init_spec.rb
index 70d1143055..03a46a8bfa 100644
--- a/spec/commands/init_spec.rb
+++ b/spec/commands/init_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle init" do
+RSpec.describe "bundle init" do
it "generates a Gemfile" do
bundle :init
expect(bundled_app("Gemfile")).to exist
diff --git a/spec/commands/inject_spec.rb b/spec/commands/inject_spec.rb
index d0112915ca..dd5e22498b 100644
--- a/spec/commands/inject_spec.rb
+++ b/spec/commands/inject_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle inject" do
+RSpec.describe "bundle inject" do
before :each do
gemfile <<-G
source "file://#{gem_repo1}"
@@ -42,6 +42,16 @@ describe "bundle inject" do
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 "when frozen" do
before do
bundle "install"
diff --git a/spec/commands/install_spec.rb b/spec/commands/install_spec.rb
index eb78ced86e..49eee01910 100644
--- a/spec/commands/install_spec.rb
+++ b/spec/commands/install_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with gem sources" do
+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
@@ -216,7 +216,7 @@ describe "bundle install with gem sources" do
G
run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 #{Gem::Platform.local}")
+ expect(out).to eq("1.0.0 #{Bundler.local_platform}")
end
it "falls back on plain ruby" do
@@ -314,14 +314,14 @@ describe "bundle install with gem sources" do
end
it "gracefully handles error when rubygems server is unavailable" do
- install_gemfile <<-G
+ install_gemfile <<-G, :artifice => nil
source "file://#{gem_repo1}"
source "http://localhost:9384"
gem 'foo'
G
- bundle :install
+ bundle :install, :artifice => nil
expect(out).to include("Could not fetch specs from http://localhost:9384/")
expect(out).not_to include("file://")
end
diff --git a/spec/commands/licenses_spec.rb b/spec/commands/licenses_spec.rb
index dd9f261eb3..0ee1a46945 100644
--- a/spec/commands/licenses_spec.rb
+++ b/spec/commands/licenses_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle licenses" do
+RSpec.describe "bundle licenses" do
before :each do
install_gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/commands/lock_spec.rb b/spec/commands/lock_spec.rb
index fc605dc5a6..52c281a6ae 100644
--- a/spec/commands/lock_spec.rb
+++ b/spec/commands/lock_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle lock" do
+RSpec.describe "bundle lock" do
def strip_lockfile(lockfile)
strip_whitespace(lockfile).sub(/\n\Z/, "")
end
@@ -10,9 +10,11 @@ describe "bundle lock" do
strip_lockfile bundled_app(file).read
end
+ let(:repo) { gem_repo1 }
+
before :each do
gemfile <<-G
- source "file://#{gem_repo1}"
+ source "file://#{repo}"
gem "rails"
gem "with_license"
gem "foo"
@@ -20,7 +22,7 @@ describe "bundle lock" do
@lockfile = strip_lockfile <<-L
GEM
- remote: file:#{gem_repo1}/
+ remote: file:#{repo}/
specs:
actionmailer (2.3.2)
activesupport (= 2.3.2)
@@ -77,7 +79,7 @@ describe "bundle lock" do
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"
+ bundle! "lock --update"
expect(read_lockfile).to eq(@lockfile)
end
@@ -85,7 +87,7 @@ describe "bundle lock" do
it "does not fetch remote specs when using the --local option" do
bundle "lock --update --local"
- expect(out).to include("available on this machine.")
+ expect(out).to include("sources listed in your Gemfile")
end
it "writes to a custom location using --lockfile" do
@@ -104,6 +106,54 @@ describe "bundle lock" do
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"
@@ -111,6 +161,12 @@ describe "bundle lock" do
expect(lockfile.platforms).to eq([java, local, mingw])
end
+ it "supports adding the `ruby` platform" do
+ bundle! "lock --add-platform ruby"
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to eq([local, "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")
@@ -223,4 +279,28 @@ describe "bundle lock" do
#{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/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb
index 6e80aa7a60..6ce19124f5 100644
--- a/spec/commands/newgem_spec.rb
+++ b/spec/commands/newgem_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle gem" do
+RSpec.describe "bundle gem" do
def reset!
super
global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
@@ -17,7 +17,7 @@ describe "bundle gem" do
end
def execute_bundle_gem(gem_name, flag = "", to_remove_push_guard = true)
- bundle "gem #{gem_name} #{flag}"
+ 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
@@ -25,6 +25,7 @@ describe "bundle gem" do
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
@@ -36,6 +37,8 @@ describe "bundle gem" do
[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"
@@ -100,6 +103,13 @@ describe "bundle gem" 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
@@ -110,25 +120,33 @@ describe "bundle gem" 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 user.name present" do
+ 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 user.name is absent" do
+ context "git config github.user is absent" do
before do
- `git config --unset user.name`
+ sys_exec("git config --unset github.user")
reset!
in_app_root
bundle "gem #{gem_name}"
@@ -137,10 +155,42 @@ describe "bundle gem" do
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 avaiable" do
+ let(:gem_name) { "test_gem" }
+
+ # This spec cannot have `git` avaiable in the test env
+ before do
+ bundle_bin = File.expand_path("../../../exe/bundle", __FILE__)
+ load_paths = [lib, spec]
+ load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
+
+ sys_exec "PATH=\"\" #{Gem.ruby} #{load_path_str} #{bundle_bin} 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
system_gems ["rake-10.0.2"]
@@ -407,11 +457,11 @@ describe "bundle gem" do
end
it "requires 'test-gem'" do
- expect(bundled_app("test_gem/test/test_helper.rb").read).to include("require 'test_gem'")
+ 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'")
+ 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
@@ -435,7 +485,7 @@ describe "bundle gem" do
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
- t.test_files = FileList['test/**/*_test.rb']
+ t.test_files = FileList["test/**/*_test.rb"]
end
task :default => :test
@@ -648,11 +698,11 @@ describe "bundle gem" do
end
it "requires 'test/gem'" do
- expect(bundled_app("test-gem/test/test_helper.rb").read).to match(%r{require 'test/gem'})
+ 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'/)
+ 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
@@ -667,7 +717,7 @@ describe "bundle gem" do
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
- t.test_files = FileList['test/**/*_test.rb']
+ t.test_files = FileList["test/**/*_test.rb"]
end
task :default => :test
@@ -733,6 +783,29 @@ describe "bundle gem" do
expect(bundled_app("a--a/a--a.gemspec")).to exist
end
+
+ it "fails gracefully with a ." do
+ bundle "gem foo.gemspec"
+ expect(out).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(out).to end_with("Invalid gem name ^ -- `^` is an invalid constant name")
+ end
+
+ it "fails gracefully with a space" do
+ bundle "gem 'foo bar'"
+ expect(out).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(out).to eq(<<-E.strip)
+ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"]
+Usage: "bundle gem GEM [OPTIONS]"
+ E
+ end
end
describe "#ensure_safe_gem_name" do
@@ -812,4 +885,26 @@ describe "bundle gem" do
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
+ output = bundle "gem conflict-foobar"
+ expect(output).to include("Errno::EEXIST")
+ expect(exitstatus).to eql(32) if exitstatus
+ end
+ end
+
+ context "on conflicts with a previously created directory" do
+ it "should fail gracefully" do
+ in_app_root do
+ FileUtils.mkdir_p("conflict-foobar/Gemfile")
+ end
+ output = bundle "gem conflict-foobar"
+ expect(output).to include("Errno::EISDIR")
+ expect(exitstatus).to eql(32) if exitstatus
+ end
+ end
end
diff --git a/spec/commands/open_spec.rb b/spec/commands/open_spec.rb
index 5507398382..6872e859d2 100644
--- a/spec/commands/open_spec.rb
+++ b/spec/commands/open_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle open" do
+RSpec.describe "bundle open" do
before :each do
install_gemfile <<-G
source "file://#{gem_repo1}"
@@ -67,6 +67,13 @@ describe "bundle open" do
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}"
diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb
index 6420c28ac7..b927a0be2b 100644
--- a/spec/commands/outdated_spec.rb
+++ b/spec/commands/outdated_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle outdated" do
+RSpec.describe "bundle outdated" do
before :each do
build_repo2 do
build_git "foo", :path => lib_path("foo")
@@ -72,7 +72,136 @@ describe "bundle outdated" do
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
+
+ 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)
@@ -195,6 +324,62 @@ describe "bundle outdated" do
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
@@ -257,6 +442,33 @@ describe "bundle outdated" do
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
@@ -302,7 +514,7 @@ describe "bundle outdated" do
shared_examples_for "no version updates are detected" do
it "does not detect any version updates" do
subject
- expect(out).to include("Bundle up to date!")
+ 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")
@@ -342,59 +554,163 @@ describe "bundle outdated" do
it_behaves_like "no version updates are detected"
end
- describe "with --major option" do
- subject { bundle "outdated --major" }
+ 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 --minor option" do
- subject { bundle "outdated --minor" }
+ 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 --patch option" do
- subject { bundle "outdated --patch" }
+ 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 --minor --patch options" do
- subject { bundle "outdated --minor --patch" }
+ 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 --major --minor options" do
- subject { bundle "outdated --major --minor" }
+ 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 --major --patch options" do
- subject { bundle "outdated --major --patch" }
+ 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 --major --minor --patch options" do
- subject { bundle "outdated --major --minor --patch" }
+ 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
end
diff --git a/spec/commands/package_spec.rb b/spec/commands/package_spec.rb
index a72b94a0b9..86c09db3ca 100644
--- a/spec/commands/package_spec.rb
+++ b/spec/commands/package_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle package" do
+RSpec.describe "bundle package" do
context "with --gemfile" do
it "finds the gemfile" do
gemfile bundled_app("NotGemfile"), <<-G
@@ -222,7 +222,7 @@ describe "bundle package" do
end
end
-describe "bundle install with gem sources" do
+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
diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb
index 84352c5427..45af035959 100644
--- a/spec/commands/show_spec.rb
+++ b/spec/commands/show_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle show" do
+RSpec.describe "bundle show" do
context "with a standard Gemfile" do
before :each do
install_gemfile <<-G
@@ -161,4 +161,31 @@ describe "bundle show" do
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/commands/update_spec.rb b/spec/commands/update_spec.rb
index 8a9867d1e9..4992e428da 100644
--- a/spec/commands/update_spec.rb
+++ b/spec/commands/update_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle update" do
+RSpec.describe "bundle update" do
before :each do
build_repo2
@@ -159,8 +159,16 @@ describe "bundle update" do
bundle "update"
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)
expect(exitstatus).not_to eq(0) if exitstatus
end
+
+ it "should suggest different command when frozen is set globally" do
+ bundler "config --global frozen 1"
+ bundle "update"
+ 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 config --delete frozen`./m)
+ end
end
describe "with --source option" do
@@ -254,7 +262,7 @@ describe "bundle update" do
end
end
-describe "bundle update in more complicated situations" do
+RSpec.describe "bundle update in more complicated situations" do
before :each do
build_repo2
end
@@ -295,7 +303,7 @@ describe "bundle update in more complicated situations" do
end
end
-describe "bundle update without a Gemfile.lock" do
+RSpec.describe "bundle update without a Gemfile.lock" do
it "should not explode" do
build_repo2
@@ -311,7 +319,7 @@ describe "bundle update without a Gemfile.lock" do
end
end
-describe "bundle update when a gem depends on a newer version of bundler" do
+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|
@@ -338,7 +346,7 @@ describe "bundle update when a gem depends on a newer version of bundler" do
end
end
-describe "bundle update" do
+RSpec.describe "bundle update" do
it "shows the previous version of the gem when updated from rubygems source" do
build_repo2
@@ -370,7 +378,7 @@ describe "bundle update" do
end
end
-describe "bundle update --ruby" do
+RSpec.describe "bundle update --ruby" do
before do
install_gemfile <<-G
::RUBY_VERSION = '2.1.3'
@@ -480,92 +488,170 @@ describe "bundle update --ruby" do
end
# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
-describe "bundle update conservative" 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"
+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
- 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)
+
+ # 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
- # 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
+ context "patch preferred" do
+ it "single gem updates dependent gem to minor" do
+ bundle "update --patch foo"
- # 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
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
+ end
- context "patch preferred" do
- it "single gem updates dependent gem to minor" do
- bundle "update --patch foo"
+ it "update all" do
+ bundle "update --patch"
- expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
+ end
end
- it "update all" do
- bundle "update --patch"
+ 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.4.5", "bar 2.1.1", "qux 1.0.1"
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ end
end
- it "warns on minor or major increment elsewhere" ## include in prior test
+ 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"
+
+ expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ end
+ end
end
- context "minor preferred" do
- it "single gem updates dependent gem to major" do
- bundle "update --minor foo"
+ 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'
- expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ 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 "warns on major increment elsewhere" ## include in prior test
+ it "should eagerly unlock isolated dependency" do
+ bundle "update isolated_owner"
- it "warns when something unlocked doesn't update at all"
- end
+ 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
- context "strict" do
- it "patch preferred" do
- bundle "update --patch foo bar --strict"
+ it "should not eagerly unlock with --conservative" do
+ bundle "update --conservative shared_owner_a isolated_owner"
- expect(the_bundle).to include_gems "foo 1.4.4", "bar 2.0.5", "qux 1.0.0"
+ 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 "minor preferred" do
- bundle "update --minor --strict"
+ 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 "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ 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"
expect(out).to eq "Provide only one of the following options: minor, patch"
end
end
-
- context "other commands" do
- it "Installer could support --dry-run flag for install and update"
-
- it "outdated should conform its flags to the resolver flags"
- end
end
diff --git a/spec/commands/viz_spec.rb b/spec/commands/viz_spec.rb
index f3fab9272c..123e7bef62 100644
--- a/spec/commands/viz_spec.rb
+++ b/spec/commands/viz_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle viz", :ruby => "1.9.3", :if => Bundler.which("dot") do
+RSpec.describe "bundle viz", :ruby => "1.9.3", :if => Bundler.which("dot") do
let(:graphviz_lib) do
graphviz_glob = base_system_gems.join("gems/ruby-graphviz*/lib")
Dir[graphviz_glob].first
diff --git a/spec/install/allow_offline_install_spec.rb b/spec/install/allow_offline_install_spec.rb
index 44100ca97b..1bca055c9f 100644
--- a/spec/install/allow_offline_install_spec.rb
+++ b/spec/install/allow_offline_install_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with :allow_offline_install" do
+RSpec.describe "bundle install with :allow_offline_install" do
before do
bundle "config allow_offline_install true"
end
diff --git a/spec/install/binstubs_spec.rb b/spec/install/binstubs_spec.rb
index a7e0b847cc..a1a9ab167d 100644
--- a/spec/install/binstubs_spec.rb
+++ b/spec/install/binstubs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" 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
diff --git a/spec/install/bundler_spec.rb b/spec/install/bundler_spec.rb
index 9f06a48086..c1ce57e60e 100644
--- a/spec/install/bundler_spec.rb
+++ b/spec/install/bundler_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
describe "with bundler dependencies" do
before(:each) do
build_repo2 do
@@ -44,17 +44,14 @@ describe "bundle install" do
In Gemfile:
bundler (= 0.9.2)
- rails (= 3.0) was resolved to 3.0, which depends on
- bundler (>= 0.9.0.pre)
-
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)', which is required by gem 'rails (= 3.0)', in any of the sources.
+ Could not find gem 'bundler (= 0.9.2)' in any of the sources
E
- expect(out).to include(nice_error)
+ expect(out).to eq(nice_error)
end
it "works for gems with multiple versions in its dependencies" do
diff --git a/spec/install/deploy_spec.rb b/spec/install/deploy_spec.rb
index c73cff5e80..4a4f6e912f 100644
--- a/spec/install/deploy_spec.rb
+++ b/spec/install/deploy_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "install with --deployment or --frozen" do
+RSpec.describe "install with --deployment or --frozen" do
before do
gemfile <<-G
source "file://#{gem_repo1}"
@@ -22,15 +22,15 @@ describe "install with --deployment or --frozen" do
it "disallows --deployment --system" do
bundle "install --deployment --system"
expect(out).to include("You have specified both --deployment")
- expect(out).to include("Please choose.")
+ 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 a path to install your gems to")
- expect(out).to include("You have specified both --deployment")
- expect(out).to include("Please choose.")
+ 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
@@ -77,10 +77,8 @@ describe "install with --deployment or --frozen" do
gem "bar", :path => "#{lib_path("nested")}"
G
- bundle :install
- bundle "install --deployment"
-
- expect(exitstatus).to eq(0) if exitstatus
+ bundle! :install
+ bundle! "install --deployment"
end
it "works when there are credentials in the source URL" do
@@ -267,22 +265,21 @@ describe "install with --deployment or --frozen" do
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")}"
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
G
- bundle :install
+ bundle! :install
expect(the_bundle).to include_gems "foo 1.0"
- bundle "package --all"
+ bundle! "package --all"
expect(bundled_app("vendor/cache/foo")).to be_directory
- bundle "install --local"
+ bundle! "install --local"
expect(out).to include("Using foo 1.0 from source at")
expect(out).to include("vendor/cache/foo")
- expect(exitstatus).to eq(0) if exitstatus
simulate_new_machine
- bundle "install --deployment"
+ bundle! "install --deployment --verbose"
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")
diff --git a/spec/install/failure_spec.rb b/spec/install/failure_spec.rb
new file mode 100644
index 0000000000..738b2cf1bd
--- /dev/null
+++ b/spec/install/failure_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+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:#{gem_repo2}"
+ gem "rails"
+ G
+ expect(out).to end_with(<<-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'` 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/install/force_spec.rb b/spec/install/force_spec.rb
index 2027660cba..6d852b3bf1 100644
--- a/spec/install/force_spec.rb
+++ b/spec/install/force_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
describe "with --force" do
before :each do
gemfile <<-G
diff --git a/spec/install/gemfile/eval_gemfile_spec.rb b/spec/install/gemfile/eval_gemfile_spec.rb
index 2660ac98c2..4f5a109e32 100644
--- a/spec/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/install/gemfile/eval_gemfile_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with gemfile that uses eval_gemfile" do
+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"
@@ -26,6 +26,24 @@ describe "bundle install with gemfile that uses eval_gemfile" do
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
+ end
+
context "Gemfile uses gemspec paths after eval-ing a Gemfile" do
before { create_file "other/Gemfile-other" }
diff --git a/spec/install/gemfile/gemspec_spec.rb b/spec/install/gemfile/gemspec_spec.rb
index 9a6bd5f1e8..e2534be1ad 100644
--- a/spec/install/gemfile/gemspec_spec.rb
+++ b/spec/install/gemfile/gemspec_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install from an existing gemspec" do
+RSpec.describe "bundle install from an existing gemspec" do
before(:each) do
build_gem "bar", :to_system => true
build_gem "bar-dev", :to_system => true
@@ -131,6 +131,55 @@ describe "bundle install from an existing gemspec" do
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
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ 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
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ 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
+
+ install_gem "platform_specific-1.0-java"
+
+ 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|
@@ -143,6 +192,26 @@ describe "bundle install from an existing gemspec" do
expect(@err).not_to match(/ahh/)
end
+ it "allows the gemspec to activate other gems" do
+ # 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"
@@ -162,6 +231,31 @@ describe "bundle install from an existing gemspec" do
expect(the_bundle).to include_gems "foo 1.0.0"
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 --deployment"
+
+ 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
@@ -409,4 +503,37 @@ describe "bundle install from an existing gemspec" do
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, :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/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb
index f70ea5b43a..d2d46279aa 100644
--- a/spec/install/gemfile/git_spec.rb
+++ b/spec/install/gemfile/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with git sources" do
+RSpec.describe "bundle install with git sources" do
describe "when floating on master" do
before :each do
build_git "foo" do |s|
@@ -209,6 +209,94 @@ describe "bundle install with git sources" do
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
@@ -997,7 +1085,12 @@ describe "bundle install with git sources" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- expect(out).to include("An error occurred while installing foo (1.0)")
+ expect(out).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
@@ -1022,20 +1115,20 @@ describe "bundle install with git sources" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- run <<-R
+ run! <<-R
require 'foo'
puts FOO
R
installed_time = out
- expect(installed_time).to match(/\d+\.\d+/)
+ 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
+ run! <<-R
require 'foo'
puts FOO
R
diff --git a/spec/install/gemfile/groups_spec.rb b/spec/install/gemfile/groups_spec.rb
index cb052b6c7a..a3a5eeefdf 100644
--- a/spec/install/gemfile/groups_spec.rb
+++ b/spec/install/gemfile/groups_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with groups" do
+RSpec.describe "bundle install with groups" do
describe "installing with no options" do
before :each do
install_gemfile <<-G
diff --git a/spec/install/gemfile/path_spec.rb b/spec/install/gemfile/path_spec.rb
index 0a73335225..a1c41aebbb 100644
--- a/spec/install/gemfile/path_spec.rb
+++ b/spec/install/gemfile/path_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with explicit source paths" do
+RSpec.describe "bundle install with explicit source paths" do
it "fetches gems" do
build_lib "foo"
diff --git a/spec/install/gemfile/platform_spec.rb b/spec/install/gemfile/platform_spec.rb
index 58129bb313..c6eaec7ca6 100644
--- a/spec/install/gemfile/platform_spec.rb
+++ b/spec/install/gemfile/platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install across platforms" do
+RSpec.describe "bundle install across platforms" do
it "maintains the same lockfile if all gems are compatible across platforms" do
lockfile <<-G
GEM
@@ -88,6 +88,32 @@ describe "bundle install across platforms" do
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" 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}"
@@ -105,7 +131,7 @@ describe "bundle install across platforms" do
end
end
-describe "bundle install with platform conditionals" do
+RSpec.describe "bundle install with platform conditionals" do
it "installs gems tagged w/ the current platforms" do
install_gemfile <<-G
source "file://#{gem_repo1}"
@@ -184,7 +210,7 @@ describe "bundle install with platform conditionals" do
gemfile <<-G
source "file://#{gem_repo1}"
- gem "some_gem", platform: :rbx
+ gem "some_gem", :platform => :rbx
G
bundle "install --local"
@@ -204,9 +230,26 @@ describe "bundle install with platform conditionals" do
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
end
-describe "when a gem has no architecture" do
+RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do
simulate_platform mswin
@@ -216,7 +259,7 @@ describe "when a gem has no architecture" do
gem "rcov"
G
- bundle :install, :fakeweb => "windows"
+ bundle :install, :artifice => "windows"
expect(the_bundle).to include_gems "rcov 1.0.0"
end
end
diff --git a/spec/install/gemfile/ruby_spec.rb b/spec/install/gemfile/ruby_spec.rb
index 1adbf10833..b9d9683758 100644
--- a/spec/install/gemfile/ruby_spec.rb
+++ b/spec/install/gemfile/ruby_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "ruby requirement" do
+RSpec.describe "ruby requirement" do
def locked_ruby_version
Bundler::RubyVersion.from_string(Bundler::LockfileParser.new(lockfile).ruby_version)
end
@@ -86,4 +86,24 @@ describe "ruby requirement" do
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/install/gemfile/sources_spec.rb b/spec/install/gemfile/sources_spec.rb
index fd11c3feab..c5375b4abf 100644
--- a/spec/install/gemfile/sources_spec.rb
+++ b/spec/install/gemfile/sources_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with gems on multiple sources" do
+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
@@ -449,4 +449,70 @@ describe "bundle install with gems on multiple sources" do
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/install/gemfile/specific_platform_spec.rb b/spec/install/gemfile/specific_platform_spec.rb
index 3e12f94c86..cc6c82c0ff 100644
--- a/spec/install/gemfile/specific_platform_spec.rb
+++ b/spec/install/gemfile/specific_platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with specific_platform enabled" do
+RSpec.describe "bundle install with specific_platform enabled" do
before do
bundle "config specific_platform true"
@@ -39,6 +39,13 @@ describe "bundle install with specific_platform enabled" do
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
@@ -67,6 +74,19 @@ describe "bundle install with specific_platform enabled" do
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)
diff --git a/spec/install/gemfile_spec.rb b/spec/install/gemfile_spec.rb
index 98abc30c86..bc49053081 100644
--- a/spec/install/gemfile_spec.rb
+++ b/spec/install/gemfile_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
context "with duplicated gems" do
it "will display a warning" do
install_gemfile <<-G
@@ -66,4 +66,33 @@ describe "bundle install" do
expect(out).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
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
end
diff --git a/spec/install/gems/compact_index_spec.rb b/spec/install/gems/compact_index_spec.rb
index a800a6ad7b..3ba858ed08 100644
--- a/spec/install/gems/compact_index_spec.rb
+++ b/spec/install/gems/compact_index_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "compact index api" do
+RSpec.describe "compact index api" do
let(:source_hostname) { "localgemserver.test" }
let(:source_uri) { "http://#{source_hostname}" }
@@ -144,7 +144,7 @@ describe "compact index api" do
gem "rcov"
G
- bundle! :install, :fakeweb => "windows"
+ 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
@@ -696,18 +696,36 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "rack 1.0.0"
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("The checksum for the downloaded `rack-1.0.0.gem` did not match the checksum given by the API.").
- and include("This means that the contents of the gem appear to be different from what was uploaded, and could be an indicator of a security issue.").
- and match(/\(The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/).
- and include("Bundler cannot continue installing rack (1.0.0).")
+ 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: `#{system_gem_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
@@ -727,4 +745,28 @@ The checksum of /versions does not match the checksum provided by the server! So
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 corrupted API dependencies")
+Downloading rails-2.3.2 revealed dependencies not in the API (#{deps.join(", ")}).
+Installing with `--full-index` should fix the problem.
+ E
+ end
end
diff --git a/spec/install/gems/dependency_api_spec.rb b/spec/install/gems/dependency_api_spec.rb
index a7c1aedea3..d2de0d358b 100644
--- a/spec/install/gems/dependency_api_spec.rb
+++ b/spec/install/gems/dependency_api_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "gemcutter's dependency API" do
+RSpec.describe "gemcutter's dependency API" do
let(:source_hostname) { "localgemserver.test" }
let(:source_uri) { "http://#{source_hostname}" }
@@ -124,7 +124,7 @@ describe "gemcutter's dependency API" do
gem "rcov"
G
- bundle :install, :fakeweb => "windows"
+ 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
diff --git a/spec/install/gems/env_spec.rb b/spec/install/gems/env_spec.rb
index d7d3a3230e..9b1d8e5424 100644
--- a/spec/install/gems/env_spec.rb
+++ b/spec/install/gems/env_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with ENV conditionals" do
+RSpec.describe "bundle install with ENV conditionals" do
describe "when just setting an ENV key as a string" do
before :each do
gemfile <<-G
diff --git a/spec/install/gems/flex_spec.rb b/spec/install/gems/flex_spec.rb
index 2f900893cd..2c2d3c16a1 100644
--- a/spec/install/gems/flex_spec.rb
+++ b/spec/install/gems/flex_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle flex_install" do
+RSpec.describe "bundle flex_install" do
it "installs the gems as expected" do
install_gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/install/gems/mirror_spec.rb b/spec/install/gems/mirror_spec.rb
index e7b4317f05..798156fb12 100644
--- a/spec/install/gems/mirror_spec.rb
+++ b/spec/install/gems/mirror_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with a mirror configured" do
+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
diff --git a/spec/install/gems/native_extensions_spec.rb b/spec/install/gems/native_extensions_spec.rb
index 6134663fcc..7531768b5f 100644
--- a/spec/install/gems/native_extensions_spec.rb
+++ b/spec/install/gems/native_extensions_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "installing a gem with native extensions" do
+RSpec.describe "installing a gem with native extensions" do
it "installs" do
build_repo2 do
build_gem "c_extension" do |s|
@@ -47,4 +47,46 @@ describe "installing a gem with native extensions" do
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")
+ expect(out).to include("Using c_extension 1.0")
+
+ run! "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
end
diff --git a/spec/install/gems/post_install_spec.rb b/spec/install/gems/post_install_spec.rb
index 596fa6412f..c3ea3e7c51 100644
--- a/spec/install/gems/post_install_spec.rb
+++ b/spec/install/gems/post_install_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+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
diff --git a/spec/install/gems/resolving_spec.rb b/spec/install/gems/resolving_spec.rb
index 0204a222f9..c227993448 100644
--- a/spec/install/gems/resolving_spec.rb
+++ b/spec/install/gems/resolving_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with install-time dependencies" do
+RSpec.describe "bundle install with install-time dependencies" do
it "installs gems with implicit rake dependencies" do
install_gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/install/gems/standalone_spec.rb b/spec/install/gems/standalone_spec.rb
index 7a6749dea9..496844ae14 100644
--- a/spec/install/gems/standalone_spec.rb
+++ b/spec/install/gems/standalone_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-shared_examples "bundle install --standalone" do
+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}" }
@@ -298,16 +298,16 @@ shared_examples "bundle install --standalone" do
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"
+ expect(extension_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
end
end
end
-describe "bundle install --standalone" do
+RSpec.describe "bundle install --standalone" do
include_examples("bundle install --standalone")
end
-describe "bundle install --standalone run in a subdirectory" do
+RSpec.describe "bundle install --standalone run in a subdirectory" do
before do
subdir = bundled_app("bob")
FileUtils.mkdir_p(subdir)
diff --git a/spec/install/gems/sudo_spec.rb b/spec/install/gems/sudo_spec.rb
index 66b9901831..13abffc14e 100644
--- a/spec/install/gems/sudo_spec.rb
+++ b/spec/install/gems/sudo_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "when using sudo", :sudo => true do
+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
diff --git a/spec/install/gems/win32_spec.rb b/spec/install/gems/win32_spec.rb
index 69ea035f5e..cdad9a8821 100644
--- a/spec/install/gems/win32_spec.rb
+++ b/spec/install/gems/win32_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install with win32-generated lockfile" do
+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"
diff --git a/spec/install/gemspecs_spec.rb b/spec/install/gemspecs_spec.rb
index 8f719bf601..97eaf149c1 100644
--- a/spec/install/gemspecs_spec.rb
+++ b/spec/install/gemspecs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
describe "when a gem has a YAML gemspec" do
before :each do
build_repo2 do
diff --git a/spec/install/git_spec.rb b/spec/install/git_spec.rb
index f35a543509..a555822012 100644
--- a/spec/install/git_spec.rb
+++ b/spec/install/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
context "git sources" do
it "displays the revision hash of the gem repository" do
build_git "foo", "1.0", :path => lib_path("foo")
diff --git a/spec/install/path_spec.rb b/spec/install/path_spec.rb
index 03c42f008c..ad6071d29f 100644
--- a/spec/install/path_spec.rb
+++ b/spec/install/path_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
describe "with --path" do
before :each do
build_gem "rack", "1.0.0", :to_system => true do |s|
@@ -38,7 +38,7 @@ describe "bundle install" do
it "disallows --path vendor/bundle --system" do
bundle "install --path vendor/bundle --system"
- expect(out).to include("Please choose.")
+ expect(out).to include("Please choose only one option.")
expect(exitstatus).to eq(15) if exitstatus
end
@@ -128,6 +128,34 @@ describe "bundle install" do
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", :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 --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 --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
diff --git a/spec/install/post_bundle_message_spec.rb b/spec/install/post_bundle_message_spec.rb
index 10c71f0a51..4453e4190f 100644
--- a/spec/install/post_bundle_message_spec.rb
+++ b/spec/install/post_bundle_message_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "post bundle message" do
+RSpec.describe "post bundle message" do
before :each do
gemfile <<-G
source "file://#{gem_repo1}"
@@ -14,7 +14,7 @@ describe "post bundle message" do
G
end
- let(:bundle_show_message) { "Use `bundle show [gemname]` to see where a bundled gem is installed." }
+ let(:bundle_show_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." }
let(:bundle_deployment_message) { "Bundled gems are installed into ./vendor" }
let(:bundle_complete_message) { "Bundle complete!" }
let(:bundle_updated_message) { "Bundle updated!" }
@@ -104,7 +104,22 @@ describe "post bundle message" do
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 or available on this machine.")
+ 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 with reference to cache if available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ install_gemfile <<-G
+ source "file://#{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 or in gems cached in vendor/cache.")
end
end
end
diff --git a/spec/install/prereleases_spec.rb b/spec/install/prereleases_spec.rb
index cbf99ca96f..6c32094d90 100644
--- a/spec/install/prereleases_spec.rb
+++ b/spec/install/prereleases_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle install" do
+RSpec.describe "bundle install" do
describe "when prerelease gems are available" do
it "finds prereleases" do
install_gemfile <<-G
diff --git a/spec/install/security_policy_spec.rb b/spec/install/security_policy_spec.rb
index 7e2c320b80..ab531bdad6 100644
--- a/spec/install/security_policy_spec.rb
+++ b/spec/install/security_policy_spec.rb
@@ -5,7 +5,7 @@ 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.
-describe "policies with unsigned gems" do
+RSpec.describe "policies with unsigned gems" do
before do
build_security_repo
gemfile <<-G
@@ -44,7 +44,7 @@ describe "policies with unsigned gems" do
end
end
-describe "policies with signed gems and no CA" do
+RSpec.describe "policies with signed gems and no CA" do
before do
build_security_repo
gemfile <<-G
diff --git a/spec/install/yanked_spec.rb b/spec/install/yanked_spec.rb
index ab96d4fcee..d42978ce4c 100644
--- a/spec/install/yanked_spec.rb
+++ b/spec/install/yanked_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-context "when installing a bundle that includes yanked gems" do
+RSpec.context "when installing a bundle that includes yanked gems" do
before(:each) do
build_repo4 do
build_gem "foo", "9.0.0"
@@ -41,3 +41,32 @@ context "when installing a bundle that includes yanked gems" do
expect(out).to include("Could not find gem 'foo (= 10.0.0)' in any of the gem sources")
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/lock/git_spec.rb b/spec/lock/git_spec.rb
index 93473db052..b36f61338d 100644
--- a/spec/lock/git_spec.rb
+++ b/spec/lock/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle lock with git gems" do
+RSpec.describe "bundle lock with git gems" do
before :each do
build_git "foo"
diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb
index e6810b405f..7d768e337c 100644
--- a/spec/lock/lockfile_spec.rb
+++ b/spec/lock/lockfile_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "the lockfile format" do
+RSpec.describe "the lockfile format" do
include Bundler::GemHelpers
it "generates a simple lockfile for a single source, gem" do
@@ -600,6 +600,36 @@ describe "the lockfile format" do
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 --all"
+ bundle! "install --local"
+
+ 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"
diff --git a/spec/other/bundle_ruby_spec.rb b/spec/other/bundle_ruby_spec.rb
index 4b2ebf4cfd..09fa2c223b 100644
--- a/spec/other/bundle_ruby_spec.rb
+++ b/spec/other/bundle_ruby_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle_ruby" do
+RSpec.describe "bundle_ruby" do
context "without patchlevel" do
it "returns the ruby version" do
gemfile <<-G
diff --git a/spec/other/cli_dispatch_spec.rb b/spec/other/cli_dispatch_spec.rb
index 05dac51559..8b34a457ef 100644
--- a/spec/other/cli_dispatch_spec.rb
+++ b/spec/other/cli_dispatch_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle command names" do
+RSpec.describe "bundle command names" do
it "work when given fully" do
bundle "install"
expect(err).to lack_errors
diff --git a/spec/other/ext_spec.rb b/spec/other/ext_spec.rb
index 7e2e712827..2d6ab941b8 100644
--- a/spec/other/ext_spec.rb
+++ b/spec/other/ext_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Gem::Specification#match_platform" do
+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)
@@ -16,7 +16,7 @@ describe "Gem::Specification#match_platform" do
end
end
-describe "Bundler::GemHelpers#generic" do
+RSpec.describe "Bundler::GemHelpers#generic" do
include Bundler::GemHelpers
it "converts non-windows platforms into ruby" do
@@ -47,9 +47,7 @@ describe "Bundler::GemHelpers#generic" do
end
end
-describe "Gem::SourceIndex#refresh!" do
- rubygems_1_7 = Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.7.0")
-
+RSpec.describe "Gem::SourceIndex#refresh!" do
before do
install_gemfile <<-G
source "file://#{gem_repo1}"
@@ -57,12 +55,12 @@ describe "Gem::SourceIndex#refresh!" do
G
end
- it "does not explode when called", :if => rubygems_1_7 do
+ 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", :unless => rubygems_1_7 do
+ it "does not explode when called", :rubygems => "< 1.7" do
run "Gem.source_index.refresh!"
run "Gem::SourceIndex.from_gems_in([]).refresh!"
end
diff --git a/spec/other/major_deprecation_spec.rb b/spec/other/major_deprecation_spec.rb
index 6505023d13..465d769538 100644
--- a/spec/other/major_deprecation_spec.rb
+++ b/spec/other/major_deprecation_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "major deprecations" do
+RSpec.describe "major deprecations" do
let(:warnings) { out } # change to err in 2.0
context "in a .99 version" do
@@ -97,7 +97,7 @@ describe "major deprecations" do
G
bundle :install, :binstubs => true
- expect(warnings).to have_major_deprecation a_string_including("the --binstubs option will be removed")
+ expect(warnings).to have_major_deprecation a_string_including("The --binstubs option will be removed")
end
end
end
@@ -120,7 +120,7 @@ describe "major deprecations" do
gem "rack"
G
- expect(warnings).to have_major_deprecation("gems.rb and gems.locked will be prefered to Gemfile and Gemfile.lock.")
+ expect(warnings).to have_major_deprecation("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
end
context "with flags" do
@@ -154,7 +154,7 @@ describe "major deprecations" do
Bundler.setup
RUBY
- expect(warnings).to have_major_deprecation("gems.rb and gems.locked will be prefered to Gemfile and Gemfile.lock.")
+ expect(warnings).to have_major_deprecation("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
end
end
diff --git a/spec/other/platform_spec.rb b/spec/other/platform_spec.rb
index 7aa0d0f8ea..6adbcef111 100644
--- a/spec/other/platform_spec.rb
+++ b/spec/other/platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle platform" do
+RSpec.describe "bundle platform" do
context "without flags" do
it "returns all the output" do
gemfile <<-G
diff --git a/spec/other/ssl_cert_spec.rb b/spec/other/ssl_cert_spec.rb
index e1a134c919..2de4dfdd0c 100644
--- a/spec/other/ssl_cert_spec.rb
+++ b/spec/other/ssl_cert_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "bundler/ssl_certs/certificate_manager"
-describe "SSL Certificates", :rubygems_master do
+RSpec.describe "SSL Certificates", :rubygems_master do
hosts = %w(
rubygems.org
index.rubygems.org
diff --git a/spec/other/trampoline_spec.rb b/spec/other/trampoline_spec.rb
index 9a8e0a4a5d..39de7048c7 100644
--- a/spec/other/trampoline_spec.rb
+++ b/spec/other/trampoline_spec.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundler version trampolining" do
+RSpec.describe "bundler version trampolining" do
before do
- ENV["BUNDLE_DISABLE_POSTIT"] = nil
- ENV["BUNDLE_ENABLE_TRAMPOLINE"] = "true"
+ ENV["BUNDLE_TRAMPOLINE_DISABLE"] = nil
+ ENV["BUNDLE_TRAMPOLINE_FORCE"] = "true"
FileUtils.rm_rf(system_gem_path)
FileUtils.cp_r(base_system_gems, system_gem_path)
end
@@ -59,8 +59,8 @@ describe "bundler version trampolining" do
end
end
- context "without BUNDLE_ENABLE_TRAMPOLINE" do
- before { ENV["BUNDLE_ENABLE_TRAMPOLINE"] = nil }
+ context "without BUNDLE_TRAMPOLINE_FORCE" do
+ before { ENV["BUNDLE_TRAMPOLINE_FORCE"] = nil }
context "when the version is >= 2" do
let(:version) { "2.7182818285" }
diff --git a/spec/plugins/command_spec.rb b/spec/plugins/command_spec.rb
index 71e87a5b01..6ad782b758 100644
--- a/spec/plugins/command_spec.rb
+++ b/spec/plugins/command_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "command plugins" do
+RSpec.describe "command plugins" do
before do
build_repo2 do
build_plugin "command-mah" do |s|
diff --git a/spec/plugins/hook_spec.rb b/spec/plugins/hook_spec.rb
index bafe688d5e..9850d850ac 100644
--- a/spec/plugins/hook_spec.rb
+++ b/spec/plugins/hook_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "hook plugins" do
+RSpec.describe "hook plugins" do
before do
build_repo2 do
build_plugin "before-install-plugin" do |s|
diff --git a/spec/plugins/install_spec.rb b/spec/plugins/install_spec.rb
index a24f318156..e2d351181c 100644
--- a/spec/plugins/install_spec.rb
+++ b/spec/plugins/install_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundler plugin install" do
+RSpec.describe "bundler plugin install" do
before do
build_repo2 do
build_plugin "foo"
@@ -70,6 +70,17 @@ describe "bundler plugin install" do
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
@@ -80,6 +91,7 @@ describe "bundler plugin install" do
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
diff --git a/spec/plugins/source/example_spec.rb b/spec/plugins/source/example_spec.rb
index 520e1b9db4..2ae34caf73 100644
--- a/spec/plugins/source/example_spec.rb
+++ b/spec/plugins/source/example_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "real source plugins" do
+RSpec.describe "real source plugins" do
context "with a minimal source plugin" do
before do
build_repo2 do
diff --git a/spec/plugins/source_spec.rb b/spec/plugins/source_spec.rb
index 92e948c190..0448bc409a 100644
--- a/spec/plugins/source_spec.rb
+++ b/spec/plugins/source_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundler source plugin" do
+RSpec.describe "bundler source plugin" do
describe "plugins dsl eval for #source with :type option" do
before do
update_repo2 do
diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb
index 5f73228b4a..c2a4ae536b 100644
--- a/spec/quality_spec.rb
+++ b/spec/quality_spec.rb
@@ -6,7 +6,7 @@ if defined?(Encoding) && Encoding.default_external.name != "UTF-8"
Encoding.default_external = Encoding.find("UTF-8")
end
-describe "The library itself" do
+RSpec.describe "The library itself" do
def check_for_spec_defs_with_single_quotes(filename)
failing_lines = []
@@ -191,6 +191,38 @@ describe "The library itself" do
expect(error_messages.compact).to be_well_formed
end
+ it "documents all used settings" do
+ exemptions = %w(
+ gem.coc
+ gem.mit
+ warned_version
+ )
+
+ all_settings = Hash.new {|h, k| h[k] = [] }
+ documented_settings = exemptions
+
+ 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" }
+
+ Dir.chdir(File.expand_path("../../lib", __FILE__)) do
+ key_pattern = /([a-z\._-]+)/i
+ `git ls-files -z`.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 `lib/#{filename}:#{number.succ}`" }
+ end
+ end
+ documented_settings = File.read("../man/bundle-config.ronn").scan(/^\* `#{key_pattern}`/).flatten
+ end
+
+ documented_settings.each {|s| all_settings.delete(s) }
+ exemptions.each {|s| all_settings.delete(s) }
+ 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
+ end
+
it "can still be built" do
Dir.chdir(root) do
begin
diff --git a/spec/realworld/dependency_api_spec.rb b/spec/realworld/dependency_api_spec.rb
index 9823cf8c76..468fa3644c 100644
--- a/spec/realworld/dependency_api_spec.rb
+++ b/spec/realworld/dependency_api_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "gemcutter's dependency API", :realworld => true do
+RSpec.describe "gemcutter's dependency API", :realworld => true do
context "when Gemcutter API takes too long to respond" do
before do
require_rack
@@ -26,6 +26,7 @@ describe "gemcutter's dependency API", :realworld => true do
end
after do
+ Artifice.deactivate
@t.kill
@t.join
end
diff --git a/spec/realworld/edgecases_spec.rb b/spec/realworld/edgecases_spec.rb
index 7a78a114b4..302fd57cf0 100644
--- a/spec/realworld/edgecases_spec.rb
+++ b/spec/realworld/edgecases_spec.rb
@@ -1,7 +1,21 @@
# frozen_string_literal: true
require "spec_helper"
-describe "real world edgecases", :realworld => true, :sometimes => true do
+RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do
+ def rubygems_version(name, requirement)
+ 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], nil)
+ rubygem = index.search(Gem::Dependency.new(name, requirement)).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})"
+ 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
@@ -15,7 +29,7 @@ describe "real world edgecases", :realworld => true, :sometimes => true do
# https://github.com/bundler/bundler/issues/1202
it "bundle cache works with rubygems 1.3.7 and pre gems",
- :ruby => "~> 1.8.7", "https://rubygems.org" => "~> 1.3.7" do
+ :ruby => "~> 1.8.7", :rubygems => "~> 1.3.7" do
install_gemfile <<-G
source "https://rubygems.org"
gem "rack", "1.3.0.beta2"
@@ -32,8 +46,8 @@ describe "real world edgecases", :realworld => true, :sometimes => true do
source "https://rubygems.org"
gem 'i18n', '~> 0.6.0'
- gem 'activesupport', '~> 3.0'
- gem 'activerecord', '~> 3.0'
+ gem 'activesupport', '~> 3.0.5'
+ gem 'activerecord', '~> 3.0.5'
gem 'builder', '~> 2.1.2'
G
bundle :lock
@@ -48,8 +62,8 @@ describe "real world edgecases", :realworld => true, :sometimes => true do
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("rails (3.2.22.4)")
+ bundle! :lock
+ expect(lockfile).to include(rubygems_version("rails", "~> 3.0"))
expect(lockfile).to include("capybara (2.2.1)")
end
@@ -76,8 +90,144 @@ describe "real world edgecases", :realworld => true, :sometimes => true do
gem "builder", "~> 2.1.2"
G
bundle :lock
- expect(lockfile).to include("i18n (0.6.11)")
- expect(lockfile).to include("activesupport (3.0.5)")
+ 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)
+
+ BUNDLED WITH
+ 1.13.1
+ L
+
+ bundle! "lock --update paperclip"
+
+ expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end
# https://github.com/bundler/bundler/issues/1500
diff --git a/spec/realworld/gemfile_source_header_spec.rb b/spec/realworld/gemfile_source_header_spec.rb
index 1c39fe97bb..ba888d43bd 100644
--- a/spec/realworld/gemfile_source_header_spec.rb
+++ b/spec/realworld/gemfile_source_header_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "thread"
-describe "fetching dependencies with a mirrored source", :realworld => true, :rubygems => ">= 2.0" do
+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}" }
@@ -12,6 +12,7 @@ describe "fetching dependencies with a mirrored source", :realworld => true, :ru
end
after do
+ Artifice.deactivate
@t.kill
@t.join
end
diff --git a/spec/realworld/mirror_probe_spec.rb b/spec/realworld/mirror_probe_spec.rb
index bb2be7f232..93dca0c173 100644
--- a/spec/realworld/mirror_probe_spec.rb
+++ b/spec/realworld/mirror_probe_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
require "thread"
-describe "fetching dependencies with a not available mirror", :realworld => true do
+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 }
@@ -15,6 +15,7 @@ describe "fetching dependencies with a not available mirror", :realworld => true
end
after do
+ Artifice.deactivate
@server_thread.kill
@server_thread.join
end
@@ -78,6 +79,22 @@ describe "fetching dependencies with a not available mirror", :realworld => true
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
+
+ expect(out).to eq "Fetching source index from #{mirror}/
+
+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}/"
+ end
end
context "with a global mirror without a fallback timeout" do
diff --git a/spec/realworld/parallel_spec.rb b/spec/realworld/parallel_spec.rb
index 539b8ab287..6950bead19 100644
--- a/spec/realworld/parallel_spec.rb
+++ b/spec/realworld/parallel_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "parallel", :realworld => true, :sometimes => true do
+RSpec.describe "parallel", :realworld => true, :sometimes => true do
it "installs" do
gemfile <<-G
source "https://rubygems.org"
diff --git a/spec/resolver/basic_spec.rb b/spec/resolver/basic_spec.rb
index 3e8883d1d4..69881d1019 100644
--- a/spec/resolver/basic_spec.rb
+++ b/spec/resolver/basic_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Resolving" do
+RSpec.describe "Resolving" do
before :each do
@index = an_awesome_index
end
@@ -214,18 +214,22 @@ describe "Resolving" do
should_conservative_resolve_and_include :patch, [], %w(foo-1.4.4 bar-2.1.1)
end
- it "will not revert to a previous version in strict mode level patch" do
- pending "possible issue with molinillo - needs further research"
- should_conservative_resolve_and_include [:patch, :strict], [], %w(foo-1.4.3 bar-2.1.1)
+ it "cannot revert to a previous version in strict mode level patch" do
+ # the strict option removes the version required to match, so a version conflict results
+ expect do
+ should_conservative_resolve_and_include [:patch, :strict], [], %w(foo-1.4.3 bar-2.1.1)
+ end.to raise_error Bundler::VersionConflict, /#{Regexp.escape("Could not find gem 'bar (~> 2.1.0)'")}/
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 "will not revert to a previous version in strict mode level minor" do
- pending "possible issue with molinillo - needs further research"
- should_conservative_resolve_and_include [:minor, :strict], [], %w(foo-1.4.3 bar-2.1.1)
+ it "cannot revert to a previous version in strict mode level minor" do
+ # the strict option removes the version required to match, so a version conflict results
+ expect do
+ should_conservative_resolve_and_include [:minor, :strict], [], %w(foo-1.4.3 bar-2.1.1)
+ end.to raise_error Bundler::VersionConflict, /#{Regexp.escape("Could not find gem 'bar (~> 2.0.0)'")}/
end
end
end
diff --git a/spec/resolver/platform_spec.rb b/spec/resolver/platform_spec.rb
index fa91eab9c2..90d6f637ce 100644
--- a/spec/resolver/platform_spec.rb
+++ b/spec/resolver/platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Resolving platform craziness" do
+RSpec.describe "Resolving platform craziness" do
describe "with cross-platform gems" do
before :each do
@index = an_awesome_index
diff --git a/spec/runtime/executable_spec.rb b/spec/runtime/executable_spec.rb
index 1ce65bd598..ff27d0b415 100644
--- a/spec/runtime/executable_spec.rb
+++ b/spec/runtime/executable_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Running bin/* commands" do
+RSpec.describe "Running bin/* commands" do
before :each do
gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/runtime/gem_tasks_spec.rb b/spec/runtime/gem_tasks_spec.rb
index c187efd681..422ec45470 100644
--- a/spec/runtime/gem_tasks_spec.rb
+++ b/spec/runtime/gem_tasks_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "require 'bundler/gem_tasks'" do
+RSpec.describe "require 'bundler/gem_tasks'" do
before :each do
bundled_app("foo.gemspec").open("w") do |f|
f.write <<-GEMSPEC
diff --git a/spec/runtime/inline_spec.rb b/spec/runtime/inline_spec.rb
index 3119045be4..022b123dc3 100644
--- a/spec/runtime/inline_spec.rb
+++ b/spec/runtime/inline_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundler/inline#gemfile" do
+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)
@@ -85,12 +85,14 @@ describe "bundler/inline#gemfile" do
script <<-RUBY, :artifice => "endpoint"
gemfile(true) do
- source "https://rubygems.org"
+ 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
@@ -104,12 +106,12 @@ describe "bundler/inline#gemfile" do
end
end
gemfile(true, :ui => MyBundlerUI.new) do
- source "https://rubygems.org"
+ source "https://notaserver.com"
gem "activesupport", :require => true
end
RUBY
- expect(out).to eq("CONFIRMED!")
+ expect(out).to eq("CONFIRMED!\nCONFIRMED!")
expect(exitstatus).to be_zero if exitstatus
end
@@ -173,4 +175,79 @@ describe "bundler/inline#gemfile" do
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
end
diff --git a/spec/runtime/load_spec.rb b/spec/runtime/load_spec.rb
index c010f8f8e9..d0e308ed3e 100644
--- a/spec/runtime/load_spec.rb
+++ b/spec/runtime/load_spec.rb
@@ -1,16 +1,9 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Bundler.load" do
+RSpec.describe "Bundler.load" do
before :each do
system_gems "rack-1.0.0"
- # clear memoized method results
- # TODO: Don't reset internal ivars
- Bundler.instance_eval do
- @load = nil
- @runtime = nil
- @definition = nil
- end
end
describe "with a gemfile" do
diff --git a/spec/runtime/platform_spec.rb b/spec/runtime/platform_spec.rb
index 666864a88c..4df934e71f 100644
--- a/spec/runtime/platform_spec.rb
+++ b/spec/runtime/platform_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Bundler.setup with multi platform stuff" do
+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}"
@@ -88,4 +88,36 @@ describe "Bundler.setup with multi platform stuff" do
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
end
diff --git a/spec/runtime/require_spec.rb b/spec/runtime/require_spec.rb
index ef88f91282..2d8935d2af 100644
--- a/spec/runtime/require_spec.rb
+++ b/spec/runtime/require_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Bundler.require" do
+RSpec.describe "Bundler.require" do
before :each do
build_lib "one", "1.0.0" do |s|
s.write "lib/baz.rb", "puts 'baz'"
@@ -196,7 +196,7 @@ describe "Bundler.require" do
expect(err).to lack_errors
end
- it "does not mangle explictly given requires" do
+ it "does not mangle explicitly given requires" do
gemfile <<-G
path "#{lib_path}"
gem 'jquery-rails', :require => 'jquery-rails'
@@ -362,7 +362,7 @@ describe "Bundler.require" do
end
end
-describe "Bundler.require with platform specific dependencies" do
+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}"
diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb
index eab66e2ee2..be0c7a1593 100644
--- a/spec/runtime/setup_spec.rb
+++ b/spec/runtime/setup_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Bundler.setup" do
+RSpec.describe "Bundler.setup" do
describe "with no arguments" do
it "makes all groups available" do
install_gemfile <<-G
@@ -673,6 +673,34 @@ describe "Bundler.setup" do
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}"
@@ -1080,8 +1108,8 @@ describe "Bundler.setup" do
end
end
- describe "when Psych is not in the Gemfile", :ruby => "~> 2.2" do
- it "does not load Psych" do
+ describe "with gemified standard libraries" do
+ it "does not load Psych", :ruby => "~> 2.2" do
gemfile ""
ruby <<-RUBY
require 'bundler/setup'
@@ -1093,5 +1121,31 @@ describe "Bundler.setup" do
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
+ end
+
+ describe "after setup" do
+ it "allows calling #gem on random objects" 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
end
end
diff --git a/spec/runtime/with_clean_env_spec.rb b/spec/runtime/with_clean_env_spec.rb
index 752754be39..b351e86c8b 100644
--- a/spec/runtime/with_clean_env_spec.rb
+++ b/spec/runtime/with_clean_env_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "Bundler.with_env helpers" do
+RSpec.describe "Bundler.with_env helpers" do
describe "Bundler.original_env" do
before do
gemfile ""
@@ -116,14 +116,14 @@ describe "Bundler.with_env helpers" do
end
end
- describe "Bundler.clean_system" do
+ describe "Bundler.clean_system", :ruby => ">= 1.9" 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" do
+ describe "Bundler.clean_exec", :ruby => ">= 1.9" 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))
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a3251ea640..297d81f531 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -17,6 +17,10 @@ rescue LoadError
abort "Run rake spec:deps to install development dependencies"
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"
# Require the correct version of popen for the current platform
@@ -31,7 +35,7 @@ else
end
Dir["#{File.expand_path("../support", __FILE__)}/*.rb"].each do |file|
- require file unless file =~ %r{fakeweb/.*\.rb}
+ require file unless file.end_with?("hax.rb")
end
$debug = false
@@ -61,6 +65,14 @@ RSpec.configure do |config|
# 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["BUNDLER_SUDO_TESTS"] && Spec::Sudo.present?
config.filter_run :sudo => true
else
@@ -91,6 +103,14 @@ RSpec.configure do |config|
config.before :all do
build_repo1
+ # HACK: necessary until rspec-mocks > 3.5.0 is used
+ # see https://github.com/bundler/bundler/pull/5363#issuecomment-278089256
+ if RUBY_VERSION < "1.9"
+ FileUtils.module_eval do
+ alias_method :mkpath, :mkdir_p
+ module_function :mkpath
+ end
+ end
end
config.before :each do
diff --git a/spec/support/artifice/compact_index.rb b/spec/support/artifice/compact_index.rb
index f3ff2db4fa..9111ed8211 100644
--- a/spec/support/artifice/compact_index.rb
+++ b/spec/support/artifice/compact_index.rb
@@ -67,9 +67,11 @@ class CompactIndexAPI < Endpoint
@gems ||= {}
@gems[gem_repo] ||= begin
specs = Bundler::Deprecate.skip_during do
- Marshal.load(File.open(gem_repo.join("specs.4.8")).read).map do |name, version, platform|
- load_spec(name, version, platform, gem_repo)
- end
+ %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|
@@ -103,8 +105,8 @@ class CompactIndexAPI < Endpoint
file = tmp("versions.list")
file.delete if file.file?
file = CompactIndex::VersionsFile.new(file.to_s)
- file.update_with(gems)
- CompactIndex.versions(file, nil, {})
+ file.create(gems)
+ file.contents
end
end
diff --git a/spec/support/artifice/compact_index_concurrent_download.rb b/spec/support/artifice/compact_index_concurrent_download.rb
index 30a2171a30..b788a852cf 100644
--- a/spec/support/artifice/compact_index_concurrent_download.rb
+++ b/spec/support/artifice/compact_index_concurrent_download.rb
@@ -22,8 +22,8 @@ class CompactIndexConcurrentDownload < CompactIndexAPI
file = tmp("versions.list")
file.delete if file.file?
file = CompactIndex::VersionsFile.new(file.to_s)
- file.update_with(gems)
- CompactIndex.versions(file, nil, {})
+ file.create(gems)
+ file.contents
end
end
end
diff --git a/spec/support/artifice/compact_index_extra_api.rb b/spec/support/artifice/compact_index_extra_api.rb
index 063e5589d4..844a9ca9f2 100644
--- a/spec/support/artifice/compact_index_extra_api.rb
+++ b/spec/support/artifice/compact_index_extra_api.rb
@@ -15,8 +15,8 @@ class CompactIndexExtraApi < CompactIndexAPI
file = tmp("versions.list")
file.delete if file.file?
file = CompactIndex::VersionsFile.new(file.to_s)
- file.update_with(gems(gem_repo4))
- CompactIndex.versions(file, nil, {})
+ file.create(gems(gem_repo4))
+ file.contents
end
end
diff --git a/spec/support/artifice/compact_index_wrong_dependencies.rb b/spec/support/artifice/compact_index_wrong_dependencies.rb
new file mode 100644
index 0000000000..25935f5e5d
--- /dev/null
+++ b/spec/support/artifice/compact_index_wrong_dependencies.rb
@@ -0,0 +1,16 @@
+# 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/support/artifice/endpoint.rb b/spec/support/artifice/endpoint.rb
index 2955889a86..861ac49e93 100644
--- a/spec/support/artifice/endpoint.rb
+++ b/spec/support/artifice/endpoint.rb
@@ -3,12 +3,7 @@ require File.expand_path("../../path.rb", __FILE__)
require File.expand_path("../../../../lib/bundler/deprecate", __FILE__)
include Spec::Path
-# Set up pretend http gem server with FakeWeb
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/artifice*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/rack-*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/rack-*/lib")].last}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/tilt*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/sinatra*/lib")].first}"
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
require "artifice"
require "sinatra/base"
@@ -24,7 +19,11 @@ class Endpoint < Sinatra::Base
require "rubygems"
require "bundler"
Bundler::Deprecate.skip_during do
- Marshal.load(File.open(gem_repo.join("specs.4.8")).read).map do |name, version, platform|
+ 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)
{
diff --git a/spec/support/artifice/endpoint_500.rb b/spec/support/artifice/endpoint_500.rb
index 9bbbd1e2f2..993630b79e 100644
--- a/spec/support/artifice/endpoint_500.rb
+++ b/spec/support/artifice/endpoint_500.rb
@@ -2,11 +2,7 @@
require File.expand_path("../../path.rb", __FILE__)
include Spec::Path
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/artifice*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/rack-*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/rack-*/lib")].last}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/tilt*/lib")].first}"
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/sinatra*/lib")].first}"
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
require "artifice"
require "sinatra/base"
diff --git a/spec/support/artifice/fail.rb b/spec/support/artifice/fail.rb
index e15768a328..1059c6df4e 100644
--- a/spec/support/artifice/fail.rb
+++ b/spec/support/artifice/fail.rb
@@ -1,23 +1,39 @@
# frozen_string_literal: true
-require File.expand_path("../../path.rb", __FILE__)
-
-# Set up pretend http gem server with FakeWeb
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gems.join("gems/artifice*/lib")].first.to_s
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gems.join("gems/rack-*/lib")].first.to_s
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gems.join("gems/rack-*/lib")].last.to_s
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gems.join("gems/tilt*/lib")].first.to_s
-require "artifice"
-
-class Fail
- def call(env)
- raise(exception(env))
+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
- def exception(env)
+ # 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! #{env["PATH_INFO"]}")
+ const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
end
end
-Artifice.activate_with(Fail.new)
+
+# Replace Net::HTTP with our failing subclass
+::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, ::Fail)
+end
diff --git a/spec/support/artifice/windows.rb b/spec/support/artifice/windows.rb
new file mode 100644
index 0000000000..c18ca454ec
--- /dev/null
+++ b/spec/support/artifice/windows.rb
@@ -0,0 +1,48 @@
+# 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/support/builders.rb b/spec/support/builders.rb
index 7436779d15..bda808c0b2 100644
--- a/spec/support/builders.rb
+++ b/spec/support/builders.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require "bundler/shared_helpers"
+require "shellwords"
module Spec
module Builders
@@ -79,8 +80,8 @@ module Spec
end
build_gem "platform_specific" do |s|
- s.platform = Gem::Platform.local
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Gem::Platform.local}'"
+ 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|
@@ -505,6 +506,10 @@ module Spec
@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
@@ -660,14 +665,15 @@ module Spec
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 #{branch}`.empty?
- silently("git branch #{branch}")
+ if `git branch | grep #{escaped_branch}`.empty?
+ silently("git branch #{escaped_branch}")
end
- silently("git checkout #{branch}")
+ silently("git checkout #{escaped_branch}")
elsif tag = options[:tag]
- `git tag #{tag}`
+ `git tag #{Shellwords.shellescape(tag)}`
elsif options[:remote]
silently("git remote add origin file://#{options[:remote]}")
elsif options[:push]
diff --git a/spec/support/fakeweb/rack-1.0.0.marshal b/spec/support/fakeweb/rack-1.0.0.marshal
deleted file mode 100644
index 383ce407f9..0000000000
--- a/spec/support/fakeweb/rack-1.0.0.marshal
+++ /dev/null
@@ -1,2 +0,0 @@
-[{ :dependencies[ [" thin" >= 0["ruby-openid" ~> 2.0.0[" mongrel" >= 0["memcache-client" >= 0[" fcgi" >= 0[" camping" >= 0["test-spec" >= 0: platform" ruby: name" rack: number"
-1.0.0 \ No newline at end of file
diff --git a/spec/support/fakeweb/windows.rb b/spec/support/fakeweb/windows.rb
deleted file mode 100644
index f1f21eed3f..0000000000
--- a/spec/support/fakeweb/windows.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-require File.expand_path("../../path.rb", __FILE__)
-include Spec::Path
-
-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"]
-
-# Set up pretend http gem server with FakeWeb
-$LOAD_PATH.unshift "#{Dir[base_system_gems.join("gems/fakeweb*/lib")].first}"
-require "fakeweb"
-
-FakeWeb.allow_net_connect = false
-
-files.each do |file|
- FakeWeb.register_uri(:get, "http://localgemserver.test/#{file}",
- :body => File.read("#{gem_repo1}/#{file}"))
-end
-FakeWeb.register_uri(:get, "http://localgemserver.test/gems/rcov-1.0-x86-mswin32.gem",
- :status => ["404", "Not Found"])
-
-FakeWeb.register_uri(:get, "http://localgemserver.test/api/v1/dependencies",
- :status => ["404", "Not Found"])
-
-FakeWeb.register_uri(:get, "http://localgemserver.test/versions",
- :status => ["500", "Internal Server Error"])
diff --git a/spec/support/hax.rb b/spec/support/hax.rb
index 2cca22c6ab..663d3527c5 100644
--- a/spec/support/hax.rb
+++ b/spec/support/hax.rb
@@ -1,12 +1,16 @@
# frozen_string_literal: true
require "rubygems"
-class Gem::Platform
- @local = new(ENV["BUNDLER_SPEC_PLATFORM"]) if ENV["BUNDLER_SPEC_PLATFORM"]
+module Gem
+ class Platform
+ @local = new(ENV["BUNDLER_SPEC_PLATFORM"]) if ENV["BUNDLER_SPEC_PLATFORM"]
+ end
+ @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
end
if ENV["BUNDLER_SPEC_VERSION"]
module Bundler
+ remove_const(:VERSION) if const_defined?(:VERSION)
VERSION = ENV["BUNDLER_SPEC_VERSION"].dup
end
end
diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb
index fe79604f30..1b2769ebf8 100644
--- a/spec/support/helpers.rb
+++ b/spec/support/helpers.rb
@@ -2,17 +2,17 @@
module Spec
module Helpers
def reset!
- Dir["#{tmp}/{gems/*,*}"].each do |dir|
- next if %(base remote1 gems rubygems).include?(File.basename(dir))
+ 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}`
+ `sudo rm -rf "#{dir}"`
else
FileUtils.rm_rf(dir)
end
end
- FileUtils.mkdir_p(tmp)
FileUtils.mkdir_p(home)
- ENV["BUNDLE_DISABLE_POSTIT"] = "1"
+ FileUtils.mkdir_p(tmpdir)
+ ENV["BUNDLE_TRAMPOLINE_DISABLE"] = "1"
Bundler.reset!
Bundler.ui = nil
Bundler.ui # force it to initialize
@@ -84,22 +84,33 @@ module Spec
with_sudo = options.delete(:sudo)
sudo = with_sudo == :preserve_env ? "sudo -E" : "sudo" if with_sudo
- options["no-color"] = true unless options.key?("no-color") || cmd.to_s.start_with?("exec", "exe", "ex", "e", "conf")
+ options["no-color"] = true unless options.key?("no-color") || cmd.to_s =~ /\A(e|ex|exe|exec|conf|confi|config)(\s|\z)/
bundle_bin = options.delete("bundle_bin") || File.expand_path("../../../exe/bundle", __FILE__)
+ if system_bundler = options.delete(:system_bundler)
+ bundle_bin = "-S bundle"
+ end
+
requires = options.delete(:requires) || []
- requires << File.expand_path("../fakeweb/" + options.delete(:fakeweb) + ".rb", __FILE__) if options.key?(:fakeweb)
- requires << File.expand_path("../artifice/" + options.delete(:artifice) + ".rb", __FILE__) if options.key?(:artifice)
+ if artifice = options.delete(:artifice) { "fail" unless RSpec.current_example.metadata[:realworld] }
+ requires << File.expand_path("../artifice/#{artifice}.rb", __FILE__)
+ end
requires << "support/hax"
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 = (options.delete(:env) || {}).map {|k, v| "#{k}='#{v}'" }.join(" ")
+ env["PATH"].gsub!("#{Path.root}/exe", "") if env["PATH"] && system_bundler
args = options.map do |k, v|
v == true ? " --#{k}" : " --#{k} #{v}" if v
end.join
- cmd = "#{env} #{sudo} #{Gem.ruby} -I#{lib}:#{spec} #{requires_str} #{bundle_bin} #{cmd}#{args}"
+ 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
@@ -110,19 +121,8 @@ module Spec
end
def bundle_ruby(options = {})
- options["no-color"] = true unless options.key?("no-color")
-
- bundle_bin = File.expand_path("../../../exe/bundle_ruby", __FILE__)
-
- requires = options.delete(:requires) || []
- requires << File.expand_path("../fakeweb/" + options.delete(:fakeweb) + ".rb", __FILE__) if options.key?(:fakeweb)
- requires << File.expand_path("../artifice/" + options.delete(:artifice) + ".rb", __FILE__) if options.key?(:artifice)
- requires_str = requires.map {|r| "-r#{r}" }.join(" ")
-
- env = (options.delete(:env) || {}).map {|k, v| "#{k}='#{v}' " }.join
- cmd = "#{env}#{Gem.ruby} -I#{lib} #{requires_str} #{bundle_bin}"
-
- sys_exec(cmd) {|i, o, thr| yield i, o, thr if block_given? }
+ options["bundle_bin"] = File.expand_path("../../../exe/bundle_ruby", __FILE__)
+ bundle("", options)
end
def ruby(ruby, options = {})
@@ -208,7 +208,11 @@ module Spec
end
def gemfile(*args)
- create_file("Gemfile", *args)
+ if args.empty?
+ File.open("Gemfile", "r", &:read)
+ else
+ create_file("Gemfile", *args)
+ end
end
def lockfile(*args)
@@ -241,12 +245,20 @@ module Spec
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 = "#{gem_repo1}/gems/#{g}.gem"
+ path = if g == :bundler
+ Dir.chdir(root) { gem_command! :build, "#{root}/bundler.gemspec" }
+ bundler_path = root + "bundler-#{Bundler::VERSION}.gem"
+ else
+ "#{gem_repo}/gems/#{g}.gem"
+ end
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
- gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies #{path}"
+ gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies '#{path}'"
+ bundler_path && bundler_path.rmtree
end
end
diff --git a/spec/support/path.rb b/spec/support/path.rb
index cf77adb509..2b929003fb 100644
--- a/spec/support/path.rb
+++ b/spec/support/path.rb
@@ -89,6 +89,10 @@ module Spec
bundled_app ".bundle", "plugin", "gems", *args
end
+ def tmpdir(*args)
+ tmp "tmpdir", *args
+ end
+
extend self
end
end
diff --git a/spec/support/rubygems_ext.rb b/spec/support/rubygems_ext.rb
index 4ddbc3312a..88886c3f1f 100644
--- a/spec/support/rubygems_ext.rb
+++ b/spec/support/rubygems_ext.rb
@@ -9,8 +9,9 @@ module Spec
# rack 2.x requires Ruby version >= 2.2.2.
# artifice doesn't support rack 2.x now.
"rack" => "< 2",
- "fakeweb artifice compact_index" => nil,
- "sinatra" => "1.2.7",
+ "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
@@ -36,19 +37,25 @@ module Spec
FileUtils.rm_rf(Path.base_system_gems)
FileUtils.mkdir_p(Path.base_system_gems)
puts "installing gems for the tests to use..."
- DEPS.sort {|a, _| a[1].nil? ? 1 : -1 }.each {|n, v| install_gem(n, v) }
+ 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_gem(name, version = nil)
- cmd = "gem install #{name} --no-rdoc --no-ri"
- cmd += " --version '#{version}'" if version
- system(cmd) || raise("Installing gem #{name} for the tests to use failed!")
+ def self.install_gems(gems)
+ reqs, no_reqs = gems.partition {|_, req| !req.nil? && !req.split(" ").empty? }
+ reqs = reqs.sort_by {|name, _| name == "rack" ? 0 : 1 } # TODO: remove when we drop ruby 1.8.7 support
+ no_reqs.map!(&:first)
+ reqs.map! {|name, req| "'#{name}:#{req}'" }
+ deps = reqs.concat(no_reqs).join(" ")
+ cmd = "gem install #{deps} --no-rdoc --no-ri --conservative"
+ puts cmd
+ system(cmd) || raise("Installing gems #{deps} for the tests to use failed!")
end
end
end
diff --git a/spec/update/gems/post_install_spec.rb b/spec/update/gems/post_install_spec.rb
new file mode 100644
index 0000000000..5a4fe7f321
--- /dev/null
+++ b/spec/update/gems/post_install_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+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
+ 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
+ end
+
+ it_behaves_like "a post-install message outputter"
+ it_behaves_like "a config observer"
+ end
+end
diff --git a/spec/update/git_spec.rb b/spec/update/git_spec.rb
index b67ddda202..021c8c942b 100644
--- a/spec/update/git_spec.rb
+++ b/spec/update/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "bundle update" do
+RSpec.describe "bundle update" do
describe "git sources" do
it "floats on a branch when :branch is used" do
build_git "foo", "1.0"
diff --git a/spec/update/path_spec.rb b/spec/update/path_spec.rb
index aa220ad60e..5ac4f7b1fe 100644
--- a/spec/update/path_spec.rb
+++ b/spec/update/path_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-describe "path sources" do
+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")
diff --git a/task/release.rake b/task/release.rake
new file mode 100644
index 0000000000..5653969084
--- /dev/null
+++ b/task/release.rake
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+namespace :release do
+ def confirm(prompt = "")
+ loop do
+ print(prompt)
+ print(": ") unless prompt.empty?
+ break if $stdin.gets.strip == "y"
+ end
+ rescue Interrupt
+ abort
+ end
+
+ def gh_api_request(opts)
+ require "net/http"
+ require "json"
+ host = opts.fetch(:host) { "https://api.github.com/" }
+ path = opts.fetch(:path)
+ response = Net::HTTP.get_response(URI.join(host, path))
+
+ links = Hash[*(response["Link"] || "").split(", ").map do |link|
+ href, name = link.match(/<(.*?)>; rel="(\w+)"/).captures
+
+ [name.to_sym, href]
+ end.flatten]
+
+ parsed_response = JSON.parse(response.body)
+
+ if n = links[:next]
+ parsed_response.concat gh_api_request(:host => host, :path => n)
+ end
+
+ parsed_response
+ end
+
+ desc "Make a patch release with the PRs from master in the patch milestone"
+ task :patch, :version do |_t, args|
+ version = args.version
+
+ version ||= begin
+ version = BUNDLER_SPEC.version
+ segments = version.segments
+ if segments.last.is_a?(String)
+ segments << "1"
+ else
+ segments[-1] += 1
+ end
+ segments.join(".")
+ end
+
+ confirm "You are about to release #{version}, currently #{BUNDLER_SPEC.version}"
+
+ milestones = gh_api_request(:path => "repos/bundler/bundler/milestones?state=open")
+ unless patch_milestone = milestones.find {|m| m["title"] == version }
+ abort "failed to find #{version} milestone on GitHub"
+ end
+ prs = gh_api_request(:path => "repos/bundler/bundler/issues?milestone=#{patch_milestone["number"]}&state=all")
+ prs.map! do |pr|
+ abort "#{pr["html_url"]} hasn't been closed yet!" unless pr["state"] == "closed"
+ next unless pr["pull_request"]
+ pr["number"].to_s
+ end.compact
+
+ version_file = "lib/bundler/version.rb"
+ version_contents = File.read(version_file)
+ unless version_contents.sub!(/^(\s*VERSION = )"#{Gem::Version::VERSION_PATTERN}"/, "\\1#{version.to_s.dump}")
+ abort "failed to update #{version_file}, is it in the expected format?"
+ end
+ File.open(version_file, "w") {|f| f.write(version_contents) }
+
+ BUNDLER_SPEC.version = version
+
+ branch = version.split(".", 3)[0, 2].push("stable").join("-")
+ sh("git", "checkout", branch)
+
+ commits = `git log --oneline origin/master --`.split("\n").map {|l| l.split(/\s/, 2) }.reverse
+ commits.select! {|_sha, message| message =~ /(Auto merge of|Merge pull request) ##{Regexp.union(*prs)}/ }
+
+ abort "Could not find commits for all PRs" unless commits.size == prs.size
+
+ unless system("git", "cherry-pick", "-x", "-m", "1", *commits.map(&:first))
+ abort unless system("zsh")
+ end
+
+ prs.each do |pr|
+ system("open", "https://github.com/bundler/bundler/pull/#{pr}")
+ confirm "Add to the changelog"
+ end
+
+ confirm "Update changelog"
+ sh("git", "commit", "-am", "Version #{version} with changelog")
+ sh("rake", "release")
+ sh("git", "checkout", "master")
+ sh("git", "pull")
+ sh("git", "merge", "v#{version}", "--no-edit")
+ sh("git", "push")
+ end
+
+ desc "Open all PRs that have not been included in a stable release"
+ task :open_unreleased_prs do
+ def prs(on = "master")
+ commits = `git log --oneline origin/#{on} --`.split("\n")
+ commits.reverse_each.map {|c| c =~ /(Auto merge of|Merge pull request) #(\d+)/ && $2 }.compact
+ end
+
+ last_stable = `git ls-remote origin`.split("\n").map {|r| r =~ %r{refs/tags/v([\d.]+)$} && $1 }.compact.map {|v| Gem::Version.create(v) }.max
+ last_stable = last_stable.segments[0, 2].<<("stable").join("-")
+
+ in_release = prs("HEAD") - prs(last_stable)
+
+ in_release.each do |pr|
+ system("open", "https://github.com/bundler/bundler/pull/#{pr}")
+ confirm
+ end
+ end
+end