Repository: ruby/rubygems (Ruby)

Issue:
YAML comments not respected when loading gemrc
### Describe the problem as clearly as you can

RubyGems v3.4.10 parses gemrc contents in a way that it strips away the part of the value after `#` (YAML comment syntax). This goes along with my expectations, since I assume that gemrc is YAML format.

**RubyGems v3.5.4 doesn't strip values after `#` and loads them verbatim.** This is what I perceive to be a bug.

Most likely a regression due to https://github.com/rubygems/rubygems/pull/6615 /cc @hsbt 

### Did you try upgrading RubyGems?

Yes, but issue still persists in RubyGems 3.5.4.

### Post steps to reproduce the problem

Notice that `--no-install` is behind YAML comment:
```
$ cat ~/.gemrc
gem: --no-rdoc --no-ri # --user-install
```

### Which command did you run?

`gem install lolcat`

### What were you expecting to happen?

I was expecting that the `lolcat` executable gets placed in EXECUTABLE DIRECTORY as reported by `gem env`.

### What actually happened?

The `lolcat` executable got placed USER INSTALLATION DIRECTORY even though `--user-install` was commented out from gemrc.

### Run `gem env` and paste the output below

```
$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 3.4.10
  - ...
  - GEM CONFIGURATION:
     - ...
     - "gem" => "--no-rdoc --no-ri"
 
$ gem update --system

$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 3.5.4
  - ...
  - GEM CONFIGURATION:
     - ...
     - "gem" => "--no-rdoc --no-ri # --user-install"
```

Please observe the change to GEM CONFIGURATION.


Discussion:
@mislav Thanks for your report. It seems bug of `YAMLSerializer`. I'll look it later.



Patch:
diff --git a/bundler/lib/bundler/yaml_serializer.rb b/bundler/lib/bundler/yaml_serializer.rb
index 37ccc46c2628..42e6aaf89d33 100644
--- a/bundler/lib/bundler/yaml_serializer.rb
+++ b/bundler/lib/bundler/yaml_serializer.rb
@@ -58,6 +58,8 @@ def load(str)
       str.split(/\r?\n/) do |line|
         if match = HASH_REGEX.match(line)
           indent, key, quote, val = match.captures
+          val = strip_comment(val)
+
           convert_to_backward_compatible_key!(key)
           depth = indent.size / 2
           if quote.empty? && val.empty?
@@ -72,6 +74,8 @@ def load(str)
           end
         elsif match = ARRAY_REGEX.match(line)
           _, val = match.captures
+          val = strip_comment(val)
+
           last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
 
           last_hash[last_empty_key].push(val)
@@ -80,6 +84,14 @@ def load(str)
       res
     end
 
+    def strip_comment(val)
+      if val.include?("#") && !val.start_with?("#")
+        val.split("#", 2).first.strip
+      else
+        val
+      end
+    end
+
     # for settings' keys
     def convert_to_backward_compatible_key!(key)
       key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)
diff --git a/bundler/spec/bundler/yaml_serializer_spec.rb b/bundler/spec/bundler/yaml_serializer_spec.rb
index 913b0235b8c3..de437f764ad7 100644
--- a/bundler/spec/bundler/yaml_serializer_spec.rb
+++ b/bundler/spec/bundler/yaml_serializer_spec.rb
@@ -174,6 +174,21 @@
 
       expect(serializer.load(yaml)).to eq(hash)
     end
+
+    it "skip commented out words" do
+      yaml = <<~YAML
+        ---
+        foo: bar
+        buzz: foo # bar
+      YAML
+
+      hash = {
+        "foo" => "bar",
+        "buzz" => "foo",
+      }
+
+      expect(serializer.load(yaml)).to eq(hash)
+    end
   end
 
   describe "against yaml lib" do
diff --git a/lib/rubygems/yaml_serializer.rb b/lib/rubygems/yaml_serializer.rb
index 947fda091146..128becc1ce7f 100644
--- a/lib/rubygems/yaml_serializer.rb
+++ b/lib/rubygems/yaml_serializer.rb
@@ -58,6 +58,8 @@ def load(str)
       str.split(/\r?\n/) do |line|
         if match = HASH_REGEX.match(line)
           indent, key, quote, val = match.captures
+          val = strip_comment(val)
+
           convert_to_backward_compatible_key!(key)
           depth = indent.size / 2
           if quote.empty? && val.empty?
@@ -72,6 +74,8 @@ def load(str)
           end
         elsif match = ARRAY_REGEX.match(line)
           _, val = match.captures
+          val = strip_comment(val)
+
           last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
 
           last_hash[last_empty_key].push(val)
@@ -80,6 +84,14 @@ def load(str)
       res
     end
 
+    def strip_comment(val)
+      if val.include?("#") && !val.start_with?("#")
+        val.split("#", 2).first.strip
+      else
+        val
+      end
+    end
+
     # for settings' keys
     def convert_to_backward_compatible_key!(key)
       key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)


Test patch:
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index ad120d2aaa20..ae6739db9890 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -548,4 +548,14 @@ def test_dump_with_rubygems_yaml
 
     assert_equal("---\n:foo: \"bar\"\n", actual)
   end
+
+  def test_handle_comment
+    yaml = <<~YAML
+      ---
+      :foo: bar # buzz
+    YAML
+
+    actual = Gem::ConfigFile.load_with_rubygems_config_hash(yaml)
+    assert_equal("bar", actual[:foo])
+  end
 end