Skip to content

Commit 0244255

Browse files
hsbtclaude
andcommitted
Align the credential_store false vocabulary with to_bool
`credential_store` was the only Bundler setting that read `off` as disabled, while every other setting goes through `Settings#to_bool`, whose false vocabulary has never had it. The disabled branch now delegates to `#to_bool` so the two cannot drift again. The true list stays because the setting is tri-state and the boolean spellings still have to be told apart from a backend name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 4846a48 commit 0244255

4 files changed

Lines changed: 70 additions & 9 deletions

File tree

lib/bundler/man/bundle-config.1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ A \fBcreated_at\fR timestamp is read as UTC when it carries no time zone offset\
107107
.IP "\(bu" 4
108108
\fBcredential_store\fR (\fBBUNDLE_CREDENTIAL_STORE\fR): Experimental: store and read host credentials (the values otherwise set via \fBbundle config set <host> <user:pass>\fR) in a credential store instead of the plain text config file\. Set it to \fBtrue\fR to use the operating system's native store (macOS Keychain, Linux Secret Service, Windows Credential Manager) when one is available on this platform, or to the name of a backend provided by a third\-party gem, such as \fB1password\fR\. Falls back to the config file when the store is unavailable or fails, warning that the credential was written in plain text\. Defaults to false\. Credentials already written to the config file are not migrated automatically; re\-run \fBbundle config set <host> <user:pass>\fR with the setting enabled to move each one into the store\. Being experimental, the name and behavior of this setting may change in a future release\.
109109
.IP
110+
The value is read in three ways\. An empty value and the false values Bundler takes everywhere else (\fBfalse\fR, \fBf\fR, \fBno\fR, \fBn\fR, \fB0\fR) turn the store off\. \fBtrue\fR, \fBt\fR, \fByes\fR, \fBy\fR, \fBon\fR and \fB1\fR select the native store\. Case does not matter for any of those\. Anything else is a backend name, taken as written, and only lowercase letters, digits, \fB_\fR and \fB\-\fR are recognized in one\. So \fBoff\fR names a backend rather than turning the store off, which \fBfalse\fR does\.
111+
.IP
110112
A credential kept in the store is never printed back\. \fBbundle config get <host>\fR and \fBbundle config list\fR name the key and say that its value lives in the credential store\. With \fB\-\-parseable\fR, such a key is left out entirely, since that output is meant to be read back by \fBbundle config set\fR\. A third\-party backend is not required to enumerate what it holds, so a credential kept in one may not be listed at all\.
111113
.IP
112114
The store belongs to the machine's user, not to a project, so \fB\-\-local\fR and \fB\-\-global\fR make no difference to where a credential is kept\. Setting a host's credential in one project changes it for every project on the machine, and unsetting it there removes it everywhere\. Protecting the store itself is the operating system's job, or that of whichever backend you selected\.

lib/bundler/man/bundle-config.1.ronn

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,14 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
193193
setting enabled to move each one into the store. Being experimental, the
194194
name and behavior of this setting may change in a future release.
195195

196+
The value is read in three ways. An empty value and the false values
197+
Bundler takes everywhere else (`false`, `f`, `no`, `n`, `0`) turn the
198+
store off. `true`, `t`, `yes`, `y`, `on` and `1` select the native store.
199+
Case does not matter for any of those. Anything else is a backend name,
200+
taken as written, and only lowercase letters, digits, `_` and `-` are
201+
recognized in one. So `off` names a backend rather than turning the store
202+
off, which `false` does.
203+
196204
A credential kept in the store is never printed back. `bundle config get
197205
<host>` and `bundle config list` name the key and say that its value lives
198206
in the credential store. With `--parseable`, such a key is left out

lib/bundler/settings.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -465,17 +465,16 @@ def is_userinfo(value)
465465
value.include?(":")
466466
end
467467

468-
##
469-
# The Gem::CredentialStore instance to use, or nil when the
470-
# `credential_store` setting is off. The value is `true`/`"true"` for this
471-
# platform's native backend or a backend name such as `"1password"`.
472-
# Guarded by a cheap lookup so reading and writing settings costs nothing
473-
# extra when the setting is disabled.
474-
475468
# Kept separate from RubyGems so gem signout does not remove Bundler's
476469
# host credentials.
477470
CREDENTIAL_STORE_SERVICE = "bundler"
478471

472+
##
473+
# The Gem::CredentialStore for the spec #credential_store_spec returns,
474+
# or nil when the setting is off or this RubyGems has no credential store.
475+
# Guarded by a cheap lookup so reading and writing settings costs nothing
476+
# extra when the setting is disabled.
477+
479478
def active_credential_store(host = nil)
480479
spec = credential_store_spec(host)
481480
return nil unless spec
@@ -493,8 +492,13 @@ def credential_store_spec(host = nil)
493492
value = self[:credential_store] if value.nil?
494493

495494
# An environment variable can carry bytes String#downcase would reject.
496-
case value.to_s.b.downcase
497-
when "", "false", "0", "no", "off", "f", "n" then nil
495+
normalized = value.to_s.b.downcase
496+
497+
# Tri-state, unlike a BOOL_KEYS setting, so #to_bool is only consulted
498+
# for the false half.
499+
return nil unless to_bool(normalized)
500+
501+
case normalized
498502
when "true", "1", "yes", "on", "t", "y" then true
499503
else value.to_s
500504
end

spec/bundler/settings_spec.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,53 @@
275275
end
276276
end
277277

278+
describe "#credential_store_spec" do
279+
it "is off when the setting is unset" do
280+
expect(settings.send(:credential_store_spec)).to be_nil
281+
end
282+
283+
it "reads the same false spellings as every other Bundler boolean" do
284+
["", "false", "FALSE", "f", "no", "n", "0"].each do |off|
285+
settings.set_local "credential_store", off
286+
287+
expect(settings.send(:credential_store_spec)).to be_nil, "#{off.inspect} should turn the store off"
288+
end
289+
end
290+
291+
it "reads the boolean true spellings as this platform's native store" do
292+
%w[true TRUE t yes y on 1].each do |on|
293+
settings.set_local "credential_store", on
294+
295+
expect(settings.send(:credential_store_spec)).to be(true), "#{on.inspect} should select the native store"
296+
end
297+
end
298+
299+
it "reads anything else as a backend name, `off` included" do
300+
%w[1password off 1Password].each do |name|
301+
settings.set_local "credential_store", name
302+
303+
expect(settings.send(:credential_store_spec)).to eq(name)
304+
end
305+
end
306+
307+
it "lets a host override the global setting" do
308+
settings.set_local "credential_store", "1password"
309+
settings.set_local "credential_store.gemserver.example.org", "false"
310+
311+
expect(settings.send(:credential_store_spec, "gemserver.example.org")).to be_nil
312+
expect(settings.send(:credential_store_spec, "other.example.org")).to eq("1password")
313+
end
314+
315+
it "survives an undecodable environment variable" do
316+
# #to_bool refuses these bytes too, not just String#downcase.
317+
without_env_side_effects do
318+
ENV["BUNDLE_CREDENTIAL_STORE"] = "\xff".dup.force_encoding("UTF-8")
319+
320+
expect(settings.send(:credential_store_spec)).to eq(ENV["BUNDLE_CREDENTIAL_STORE"])
321+
end
322+
end
323+
end
324+
278325
describe "#credentials_for" do
279326
let(:uri) { Gem::URI("https://gemserver.example.org/") }
280327
let(:credentials) { "username:password" }

0 commit comments

Comments
 (0)