diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml
index c51d02a4bdd8955968c8cf0bc5fa6d9e3366aef6..ca7ff4dc8cf954f3cf4e84bdaea1c9a2654c91aa 100644
--- a/Library/.rubocop.yml
+++ b/Library/.rubocop.yml
@@ -29,7 +29,7 @@ Layout/IndentArray:
   EnforcedStyle: special_inside_parentheses
 
 Layout/IndentHeredoc:
-  EnforcedStyle: unindent
+  EnforcedStyle: squiggly
 
 # conflicts with DSL-style path concatenation with `/`
 Layout/SpaceAroundOperators:
@@ -73,6 +73,8 @@ Metrics/CyclomaticComplexity:
 
 Metrics/LineLength:
   Max: 324
+  # ignore manpage comments
+  IgnoredPatterns: ['#: ']
 
 Metrics/MethodLength:
   Max: 222
diff --git a/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb b/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb
index 3f63dae8fed03918bc0a80fe2cf8cef4be2de1c2..7505ac49a96f9ebec8e66dafaa46b385c45aeb27 100644
--- a/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb
+++ b/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb
@@ -253,7 +253,7 @@ module Hbc
       end
 
       def trash_paths(*paths, command: nil, **_)
-        result = command.run!("/usr/bin/osascript", args: ["-e", <<-'EOS'.undent, *paths])
+        result = command.run!("/usr/bin/osascript", args: ["-e", <<~'EOS', *paths])
           on run argv
             repeat with i from 1 to (count argv)
               set item i of argv to (item i of argv as POSIX file)
diff --git a/Library/Homebrew/cask/lib/hbc/artifact/installer.rb b/Library/Homebrew/cask/lib/hbc/artifact/installer.rb
index 588bcabd547041201441385261341a15c3e1a115..5cd388c7fee5d6c55e20997061188ceae8d998f9 100644
--- a/Library/Homebrew/cask/lib/hbc/artifact/installer.rb
+++ b/Library/Homebrew/cask/lib/hbc/artifact/installer.rb
@@ -10,7 +10,7 @@ module Hbc
 
       module ManualInstaller
         def install_phase(**)
-          puts <<-EOS.undent
+          puts <<~EOS
             To complete the installation of Cask #{cask}, you must also
             run the installer at
 
diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb
index bd25477aca3eacb528796d7d554160ccea7824d5..5180c56889c335b6f38c6489c05448c43b7b96f1 100644
--- a/Library/Homebrew/cask/lib/hbc/audit.rb
+++ b/Library/Homebrew/cask/lib/hbc/audit.rb
@@ -174,7 +174,7 @@ module Hbc
         add_warning "error retrieving appcast: #{result[:command_result].stderr}"
       else
         expected = cask.appcast.checkpoint
-        add_warning <<-EOS.undent unless expected == actual_checkpoint
+        add_warning <<~EOS unless expected == actual_checkpoint
           appcast checkpoint mismatch
           Expected: #{expected}
           Actual: #{actual_checkpoint}
diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb
index 08d4576439a1fef7213d914dd3d9dbe80d8edd9c..c32b355847acf41cfcded17bb09ecd2ca80fd433 100644
--- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb
+++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb
@@ -190,7 +190,7 @@ module Hbc
       when 2..Float::INFINITY
         loaders = possible_tap_casks.map(&FromTapPathLoader.method(:new))
 
-        raise CaskError, <<-EOS.undent
+        raise CaskError, <<~EOS
           Cask #{ref} exists in multiple taps:
           #{loaders.map { |loader| "  #{loader.tap}/#{loader.token}" }.join("\n")}
         EOS
diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb
index d260be4e3779fb384a120bc87471d164ba0c5d68..9283802d5e4ecdf1aba3c4f608263a07c42d0b24 100644
--- a/Library/Homebrew/cask/lib/hbc/cli.rb
+++ b/Library/Homebrew/cask/lib/hbc/cli.rb
@@ -235,7 +235,7 @@ module Hbc
       end
 
       def purpose
-        puts <<-EOS.undent
+        puts <<~EOS
           brew-cask provides a friendly homebrew-style CLI workflow for the
           administration of macOS applications distributed as binaries.
 
diff --git a/Library/Homebrew/cask/lib/hbc/cli/create.rb b/Library/Homebrew/cask/lib/hbc/cli/create.rb
index 8de101092b9642cf9cdea153fa41fca1920888cd..e6ca3d7bc9924a6e86f4cc0f8cbfe43a2044efd0 100644
--- a/Library/Homebrew/cask/lib/hbc/cli/create.rb
+++ b/Library/Homebrew/cask/lib/hbc/cli/create.rb
@@ -21,7 +21,7 @@ module Hbc
       end
 
       def self.template(cask_token)
-        <<-EOS.undent
+        <<~EOS
           cask '#{cask_token}' do
             version ''
             sha256 ''
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb
index b83224fb1ebfb6bd44ff9dfed5c80277572cfba8..f06e2acc5835bea98c8f0d7944098398b6298ff5 100644
--- a/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb
@@ -16,7 +16,7 @@ module Hbc
         super
 
         if args.count != 1
-          raise ArgumentError, <<-EOS.undent
+          raise ArgumentError, <<~EOS
             This command requires exactly one argument.
 
             #{self.class.usage}
@@ -31,7 +31,7 @@ module Hbc
       end
 
       def self.usage
-        <<-EOS.undent
+        <<~EOS
           Usage: brew cask _audit_modified_casks [options...] <commit range>
 
           Given a range of Git commits, find any Casks that were modified and run `brew
diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb
index f2059605ca1511be084ab126f96a0f7eae965b08..c962632bca7c8771ca0f612e172b0a7d7d7eb192 100644
--- a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb
+++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb
@@ -25,7 +25,7 @@ module Hbc
 
           single = versions.count == 1
 
-          puts <<-EOS.undent
+          puts <<~EOS
             #{cask} #{versions.join(", ")} #{single ? "is" : "are"} still installed.
             Remove #{single ? "it" : "them all"} with `brew cask uninstall --force #{cask}`.
           EOS
diff --git a/Library/Homebrew/cask/lib/hbc/container/air.rb b/Library/Homebrew/cask/lib/hbc/container/air.rb
index 4b083e538bb44a0341b4680daff71ddf3dac8afa..f2ebb6bcd0eab84fa5ce69bd3d23a2fb86fba039 100644
--- a/Library/Homebrew/cask/lib/hbc/container/air.rb
+++ b/Library/Homebrew/cask/lib/hbc/container/air.rb
@@ -13,7 +13,7 @@ module Hbc
 
       def self.installer_cmd
         return @installer_cmd ||= INSTALLER_PATHNAME if installer_exist?
-        raise CaskError, <<-EOS.undent
+        raise CaskError, <<~EOS
           Adobe AIR runtime not present, try installing it via
 
               brew cask install adobe-air
diff --git a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb
index 7d373b5f33c7f77ea88cbe7b2a627eb33bcc469b..45ee5d1601c8f8dc3735e7024695f83202511f07 100644
--- a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb
+++ b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb
@@ -9,22 +9,22 @@ module Hbc
   class DSL
     class Caveats < Base
       def path_environment_variable(path)
-        puts <<-EOS.undent
-        To use #{@cask}, you may need to add the #{path} directory
-        to your PATH environment variable, eg (for bash shell):
+        puts <<~EOS
+          To use #{@cask}, you may need to add the #{path} directory
+          to your PATH environment variable, eg (for bash shell):
 
-          export PATH=#{path}:"$PATH"
+            export PATH=#{path}:"$PATH"
 
         EOS
       end
 
       def zsh_path_helper(path)
-        puts <<-EOS.undent
-        To use #{@cask}, zsh users may need to add the following line to their
-        ~/.zprofile.  (Among other effects, #{path} will be added to the
-        PATH environment variable):
+        puts <<~EOS
+          To use #{@cask}, zsh users may need to add the following line to their
+          ~/.zprofile.  (Among other effects, #{path} will be added to the
+          PATH environment variable):
 
-          eval `/usr/libexec/path_helper -s`
+            eval `/usr/libexec/path_helper -s`
 
         EOS
       end
@@ -32,7 +32,7 @@ module Hbc
       def files_in_usr_local
         localpath = "/usr/local"
         return unless HOMEBREW_PREFIX.to_s.downcase.start_with?(localpath)
-        puts <<-EOS.undent
+        puts <<~EOS
           Cask #{@cask} installs files under "#{localpath}". The presence of such
           files can cause warnings when running "brew doctor", which is considered
           to be a bug in Homebrew-Cask.
@@ -42,72 +42,72 @@ module Hbc
 
       def depends_on_java(java_version = "any")
         if java_version == "any"
-          puts <<-EOS.undent
-          #{@cask} requires Java. You can install the latest version with
+          puts <<~EOS
+            #{@cask} requires Java. You can install the latest version with
 
-            brew cask install java
+              brew cask install java
 
           EOS
         elsif java_version.include?("9") || java_version.include?("+")
-          puts <<-EOS.undent
-          #{@cask} requires Java #{java_version}. You can install the latest version with
+          puts <<~EOS
+            #{@cask} requires Java #{java_version}. You can install the latest version with
 
-            brew cask install java
+              brew cask install java
 
           EOS
         else
-          puts <<-EOS.undent
-          #{@cask} requires Java #{java_version}. You can install it with
+          puts <<~EOS
+            #{@cask} requires Java #{java_version}. You can install it with
 
-            brew cask install caskroom/versions/java#{java_version}
+              brew cask install caskroom/versions/java#{java_version}
 
           EOS
         end
       end
 
       def logout
-        puts <<-EOS.undent
-        You must log out and log back in for the installation of #{@cask}
-        to take effect.
+        puts <<~EOS
+          You must log out and log back in for the installation of #{@cask}
+          to take effect.
 
         EOS
       end
 
       def reboot
-        puts <<-EOS.undent
-        You must reboot for the installation of #{@cask} to take effect.
+        puts <<~EOS
+          You must reboot for the installation of #{@cask} to take effect.
 
         EOS
       end
 
       def discontinued
-        puts <<-EOS.undent
-        #{@cask} has been officially discontinued upstream.
-        It may stop working correctly (or at all) in recent versions of macOS.
+        puts <<~EOS
+          #{@cask} has been officially discontinued upstream.
+          It may stop working correctly (or at all) in recent versions of macOS.
 
         EOS
       end
 
       def free_license(web_page)
-        puts <<-EOS.undent
-        The vendor offers a free license for #{@cask} at
-          #{web_page}
+        puts <<~EOS
+          The vendor offers a free license for #{@cask} at
+            #{web_page}
 
         EOS
       end
 
       def malware(radar_number)
-        puts <<-EOS.undent
-        #{@cask} has been reported to bundle malware. Like with any app, use at your own risk.
+        puts <<~EOS
+          #{@cask} has been reported to bundle malware. Like with any app, use at your own risk.
 
-        A report has been made to Apple about this app. Their certificate will hopefully be revoked.
-        See the public report at
-          #{Formatter.url("https://openradar.appspot.com/#{radar_number}")}
+          A report has been made to Apple about this app. Their certificate will hopefully be revoked.
+          See the public report at
+            #{Formatter.url("https://openradar.appspot.com/#{radar_number}")}
 
-        If this report is accurate, please duplicate it at
-          #{Formatter.url("https://bugreport.apple.com/")}
-        If this report is a mistake, please let us know by opening an issue at
-          #{Formatter.url("https://github.com/caskroom/homebrew-cask/issues/new")}
+          If this report is accurate, please duplicate it at
+            #{Formatter.url("https://bugreport.apple.com/")}
+          If this report is a mistake, please let us know by opening an issue at
+            #{Formatter.url("https://github.com/caskroom/homebrew-cask/issues/new")}
 
         EOS
       end
diff --git a/Library/Homebrew/cask/lib/hbc/exceptions.rb b/Library/Homebrew/cask/lib/hbc/exceptions.rb
index f7f9e43b66f6f163ba6d07f0303211d705785619..2626a467a259bc7b5f4c336b2764dcf39aa350b6 100644
--- a/Library/Homebrew/cask/lib/hbc/exceptions.rb
+++ b/Library/Homebrew/cask/lib/hbc/exceptions.rb
@@ -44,7 +44,7 @@ module Hbc
 
   class CaskAlreadyInstalledError < AbstractCaskErrorWithToken
     def to_s
-      <<-EOS.undent
+      <<~EOS
         Cask '#{token}' is already installed.
 
         To re-install #{token}, run:
@@ -80,7 +80,7 @@ module Hbc
 
   class CaskX11DependencyError < AbstractCaskErrorWithToken
     def to_s
-      <<-EOS.undent
+      <<~EOS
         Cask '#{token}' requires XQuartz/X11, which can be installed using Homebrew-Cask by running
           #{Formatter.identifier("brew cask install xquartz")}
 
@@ -132,7 +132,7 @@ module Hbc
 
   class CaskSha256MissingError < CaskSha256Error
     def to_s
-      <<-EOS.undent
+      <<~EOS
         Cask '#{token}' requires a checksum:
           #{Formatter.identifier("sha256 '#{actual}'")}
       EOS
@@ -148,7 +148,7 @@ module Hbc
     end
 
     def to_s
-      <<-EOS.undent
+      <<~EOS
         Checksum for Cask '#{token}' does not match.
 
         Expected: #{Formatter.success(expected.to_s)}
@@ -162,7 +162,7 @@ module Hbc
 
   class CaskNoShasumError < CaskSha256Error
     def to_s
-      <<-EOS.undent
+      <<~EOS
         Cask '#{token}' does not have a sha256 checksum defined and was not installed.
         This means you have the #{Formatter.identifier("--require-sha")} option set, perhaps in your HOMEBREW_CASK_OPTS.
       EOS
diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb
index 68b9595e1082a26485e2a9402806cb828e5fb485..1063f488bcf28a9cc2e88165380b72ef8ed91e7f 100644
--- a/Library/Homebrew/cask/lib/hbc/installer.rb
+++ b/Library/Homebrew/cask/lib/hbc/installer.rb
@@ -326,7 +326,7 @@ module Hbc
                       ],
                       sudo: true)
       else
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Accessibility access cannot be enabled automatically on this version of macOS.
           See System Preferences to enable it manually.
         EOS
@@ -347,7 +347,7 @@ module Hbc
                       ],
                       sudo: true)
       else
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Accessibility access cannot be disabled automatically on this version of macOS.
           See System Preferences to disable it manually.
         EOS
diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb
index 9ce3de907b61765071e744bf5b23409801d406b7..3c8311a3b3faf98ca63da89ddce736cac248ff02 100644
--- a/Library/Homebrew/cask/lib/hbc/system_command.rb
+++ b/Library/Homebrew/cask/lib/hbc/system_command.rb
@@ -166,12 +166,12 @@ module Hbc
         _warn_plist_garbage(command, Regexp.last_match[2])
         xml = Plist.parse_xml(output)
         unless xml.respond_to?(:keys) && !xml.keys.empty?
-          raise CaskError, <<-EOS
-    Empty result parsing plist output from command.
-      command was:
-      #{command}
-      output we attempted to parse:
-      #{output}
+          raise CaskError, <<~EOS
+            Empty result parsing plist output from command.
+              command was:
+              #{command}
+              output we attempted to parse:
+              #{output}
           EOS
         end
         xml
diff --git a/Library/Homebrew/cask/lib/hbc/utils.rb b/Library/Homebrew/cask/lib/hbc/utils.rb
index 22f826e748ff7678d11f1f4be1b0302231f7ca1a..0f44c0157fdbdf05536613c9923d24baa4917ded 100644
--- a/Library/Homebrew/cask/lib/hbc/utils.rb
+++ b/Library/Homebrew/cask/lib/hbc/utils.rb
@@ -90,7 +90,7 @@ module Hbc
     end
 
     def self.error_message_with_suggestions
-      <<-EOS.undent
+      <<~EOS
         Follow the instructions here:
           #{Formatter.url(BUG_REPORTS_URL)}
       EOS
diff --git a/Library/Homebrew/cask/lib/hbc/version.rb b/Library/Homebrew/cask/lib/hbc/version.rb
index 2eb942b956fff2b6985646511c0fdac7373df9c0..c7ce8e2d8cc6e2ed1b6cfcc9ae2c96bf4a828b1e 100644
--- a/Library/Homebrew/cask/lib/hbc/version.rb
+++ b/Library/Homebrew/cask/lib/hbc/version.rb
@@ -1,7 +1,7 @@
 module Hbc
   def self.full_version
     @full_version ||= begin
-      <<-EOS.undent
+      <<~EOS
         Homebrew-Cask #{HOMEBREW_VERSION}
         caskroom/homebrew-cask #{Hbc.default_tap.version_string}
       EOS
diff --git a/Library/Homebrew/caveats.rb b/Library/Homebrew/caveats.rb
index 1849ea79b336350b8c27bd20c4499bae1f856ddf..485116cff1bd4c4cd46f467f0d0839c6e1df1248 100644
--- a/Library/Homebrew/caveats.rb
+++ b/Library/Homebrew/caveats.rb
@@ -44,7 +44,7 @@ class Caveats
   def keg_only_text
     return unless f.keg_only?
 
-    s = <<-EOS.undent
+    s = <<~EOS
       This formula is keg-only, which means it was not symlinked into #{HOMEBREW_PREFIX},
       because #{f.keg_only_reason.to_s.chomp}.
     EOS
@@ -86,12 +86,12 @@ class Caveats
 
     case shell
     when :bash
-      <<-EOS.undent
+      <<~EOS
         Bash completion has been installed to:
           #{HOMEBREW_PREFIX}/etc/bash_completion.d
       EOS
     when :zsh
-      <<-EOS.undent
+      <<~EOS
         zsh #{installed.join(" and ")} have been installed to:
           #{HOMEBREW_PREFIX}/share/zsh/site-functions
       EOS
@@ -111,7 +111,7 @@ class Caveats
     homebrew_site_packages = Language::Python.homebrew_site_packages
     user_site_packages = Language::Python.user_site_packages "python"
     pth_file = user_site_packages/"homebrew.pth"
-    instructions = <<-EOS.undent.gsub(/^/, "  ")
+    instructions = <<~EOS.gsub(/^/, "  ")
       mkdir -p #{user_site_packages}
       echo 'import site; site.addsitedir("#{homebrew_site_packages}")' >> #{pth_file}
     EOS
@@ -119,7 +119,7 @@ class Caveats
     if f.keg_only?
       keg_site_packages = f.opt_prefix/"lib/python2.7/site-packages"
       unless Language::Python.in_sys_path?("python", keg_site_packages)
-        s = <<-EOS.undent
+        s = <<~EOS
           If you need Python to find bindings for this keg-only formula, run:
             echo #{keg_site_packages} >> #{homebrew_site_packages/f.name}.pth
         EOS
@@ -131,7 +131,7 @@ class Caveats
     return if Language::Python.reads_brewed_pth_files?("python")
 
     if !Language::Python.in_sys_path?("python", homebrew_site_packages)
-      s = <<-EOS.undent
+      s = <<~EOS
         Python modules have been installed and Homebrew's site-packages is not
         in your Python sys.path, so you will not be able to import the modules
         this formula installed. If you plan to develop with these modules,
@@ -139,7 +139,7 @@ class Caveats
       EOS
       s += instructions
     elsif keg.python_pth_files_installed?
-      s = <<-EOS.undent
+      s = <<~EOS
         This formula installed .pth files to Homebrew's site-packages and your
         Python isn't configured to process them, so you will not be able to
         import the modules this formula installed. If you plan to develop
@@ -155,55 +155,13 @@ class Caveats
     return unless keg
     return unless keg.elisp_installed?
 
-    <<-EOS.undent
+    <<~EOS
       Emacs Lisp files have been installed to:
         #{HOMEBREW_PREFIX}/share/emacs/site-lisp/#{f.name}
     EOS
   end
 
-  def plist_caveats
-    s = []
-    if f.plist || (keg&.plist_installed?)
-      plist_domain = f.plist_path.basename(".plist")
-
-      # we readlink because this path probably doesn't exist since caveats
-      # occurs before the link step of installation
-      # Yosemite security measures mildly tighter rules:
-      # https://github.com/Homebrew/legacy-homebrew/issues/33815
-      if !plist_path.file? || !plist_path.symlink?
-        if f.plist_startup
-          s << "To have launchd start #{f.full_name} now and restart at startup:"
-          s << "  sudo brew services start #{f.full_name}"
-        else
-          s << "To have launchd start #{f.full_name} now and restart at login:"
-          s << "  brew services start #{f.full_name}"
-        end
-      # For startup plists, we cannot tell whether it's running on launchd,
-      # as it requires for `sudo launchctl list` to get real result.
-      elsif f.plist_startup
-        s << "To restart #{f.full_name} after an upgrade:"
-        s << "  sudo brew services restart #{f.full_name}"
-      elsif Kernel.system "/bin/launchctl list #{plist_domain} &>/dev/null"
-        s << "To restart #{f.full_name} after an upgrade:"
-        s << "  brew services restart #{f.full_name}"
-      else
-        s << "To start #{f.full_name}:"
-        s << "  brew services start #{f.full_name}"
-      end
-
-      if f.plist_manual
-        s << "Or, if you don't want/need a background service you can just run:"
-        s << "  #{f.plist_manual}"
-      end
-
-      # pbpaste is the system clipboard tool on macOS and fails with `tmux` by default
-      # check if this is being run under `tmux` to avoid failing
-      if ENV["TMUX"] && !quiet_system("/usr/bin/pbpaste")
-        s << "" << "WARNING: brew services will fail when run under tmux."
-      end
-    end
-    s.join("\n") + "\n" unless s.empty?
-  end
+  def plist_caveats; end
 
   def plist_path
     destination = if f.plist_startup
@@ -222,3 +180,5 @@ class Caveats
     destination_path/plist_filename
   end
 end
+
+require "extend/os/caveats"
diff --git a/Library/Homebrew/cmd/cleanup.rb b/Library/Homebrew/cmd/cleanup.rb
index d8f669e8502018463d0b741e6865f51f8513aa31..290b748de25cfe98b268bbbed13cdac1b78afe84 100644
--- a/Library/Homebrew/cmd/cleanup.rb
+++ b/Library/Homebrew/cmd/cleanup.rb
@@ -38,7 +38,7 @@ module Homebrew
   end
 
   def report_unremovable_kegs
-    ofail <<-EOS.undent
+    ofail <<~EOS
       Could not cleanup old kegs! Fix your permissions on:
         #{Cleanup.unremovable_kegs.join "\n  "}
     EOS
diff --git a/Library/Homebrew/cmd/commands.rb b/Library/Homebrew/cmd/commands.rb
index addccd60978c66b0e069a29ffec66149e1d6619b..0dfc6c4512d6691513b6494b1c5cf1578bf98706 100644
--- a/Library/Homebrew/cmd/commands.rb
+++ b/Library/Homebrew/cmd/commands.rb
@@ -16,12 +16,12 @@ module Homebrew
     else
       # Find commands in Homebrew/cmd
       puts "Built-in commands"
-      puts Formatter.columns(internal_commands)
+      puts Formatter.columns(internal_commands.sort)
 
       # Find commands in Homebrew/dev-cmd
       puts
       puts "Built-in developer commands"
-      puts Formatter.columns(internal_developer_commands)
+      puts Formatter.columns(internal_developer_commands.sort)
 
       # Find commands in the path
       unless (exts = external_commands).empty?
@@ -51,8 +51,8 @@ module Homebrew
   end
 
   def find_internal_commands(directory)
-    directory.children.each_with_object([]) do |f, cmds|
-      cmds << f.basename.to_s.sub(/\.(?:rb|sh)$/, "") if f.file?
-    end
+    Pathname.glob(directory/"*")
+            .select(&:file?)
+            .map { |f| f.basename.to_s.sub(/\.(?:rb|sh)$/, "") }
   end
 end
diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb
index de7aa4a51203d0297d6f057a24a194d68e2f77f3..ae758e143319741cb0a57ef6b6979e567faf8865 100644
--- a/Library/Homebrew/cmd/deps.rb
+++ b/Library/Homebrew/cmd/deps.rb
@@ -68,16 +68,16 @@ module Homebrew
 
     if mode.tree?
       if mode.installed?
-        puts_deps_tree Formula.installed, !ARGV.one?
+        puts_deps_tree Formula.installed.sort, !ARGV.one?
       else
         raise FormulaUnspecifiedError if ARGV.named.empty?
         puts_deps_tree ARGV.formulae, !ARGV.one?
       end
     elsif mode.all?
-      puts_deps Formula
+      puts_deps Formula.sort
     elsif ARGV.named.empty?
       raise FormulaUnspecifiedError unless mode.installed?
-      puts_deps Formula.installed
+      puts_deps Formula.installed.sort
     elsif mode.for_each?
       puts_deps ARGV.formulae
     else
diff --git a/Library/Homebrew/cmd/diy.rb b/Library/Homebrew/cmd/diy.rb
index a8f6440df6da528d470e60a3170142a62dcf96b3..3980b3d9ea5e9ec206ee5d4ef1d59e80275f644e 100644
--- a/Library/Homebrew/cmd/diy.rb
+++ b/Library/Homebrew/cmd/diy.rb
@@ -43,7 +43,7 @@ module Homebrew
     detected_name = basename[/(.*?)-?#{Regexp.escape(version)}/, 1] || basename
     canonical_name = Formulary.canonical_name(detected_name)
 
-    odie <<-EOS.undent if detected_name != canonical_name
+    odie <<~EOS if detected_name != canonical_name
       The detected name #{detected_name.inspect} exists in Homebrew as an alias
       of #{canonical_name.inspect}. Consider using the canonical name instead:
         brew diy --name=#{canonical_name}
diff --git a/Library/Homebrew/cmd/doctor.rb b/Library/Homebrew/cmd/doctor.rb
index cca2dca03208de88a1292d8bc8dac82c29f547db..7b1778e63bee362fe04bd09230f35f13b99df006 100644
--- a/Library/Homebrew/cmd/doctor.rb
+++ b/Library/Homebrew/cmd/doctor.rb
@@ -43,7 +43,7 @@ module Homebrew
       out = checks.send(method)
       next if out.nil? || out.empty?
       if first_warning
-        $stderr.puts <<-EOS.undent
+        $stderr.puts <<~EOS
           #{Tty.bold}Please note that these warnings are just used to help the Homebrew maintainers
           with debugging if you file an issue. If everything you use Homebrew for is
           working fine: please don't worry and just ignore them. Thanks!#{Tty.reset}
diff --git a/Library/Homebrew/cmd/gist-logs.rb b/Library/Homebrew/cmd/gist-logs.rb
index 012121b085d24e124f381f8aed968913a1f6a208..45537602b0f0e4e8e5646373b0373cba48983135 100644
--- a/Library/Homebrew/cmd/gist-logs.rb
+++ b/Library/Homebrew/cmd/gist-logs.rb
@@ -29,7 +29,7 @@ module Homebrew
     files["00.config.out"] = { content: s.string }
     files["00.doctor.out"] = { content: `brew doctor 2>&1` }
     unless f.core_formula?
-      tap = <<-EOS.undent
+      tap = <<~EOS
         Formula: #{f.name}
         Tap: #{f.tap}
         Path: #{f.path}
@@ -47,7 +47,7 @@ module Homebrew
 
     if ARGV.include?("--new-issue") || ARGV.switch?("n")
       if GitHub.api_credentials_type == :none
-        puts <<-EOS.undent
+        puts <<~EOS
           You can create a new personal access token:
            #{GitHub::ALL_SCOPES_URL}
           and then set the new HOMEBREW_GITHUB_API_TOKEN as the authentication method.
@@ -64,7 +64,7 @@ module Homebrew
 
   def brief_build_info(f)
     build_time_str = f.logs.ctime.strftime("%Y-%m-%d %H:%M:%S")
-    s = <<-EOS.undent
+    s = <<~EOS
       Homebrew build logs for #{f.full_name} on #{OS_VERSION}
     EOS
     if ARGV.include?("--with-hostname")
diff --git a/Library/Homebrew/cmd/help.rb b/Library/Homebrew/cmd/help.rb
index a0c44854b688cc979731a97d3c6e6c73ec827e5f..c85916f9b92401163647dbb09acd264d6194cec0 100644
--- a/Library/Homebrew/cmd/help.rb
+++ b/Library/Homebrew/cmd/help.rb
@@ -1,4 +1,4 @@
-HOMEBREW_HELP = <<-EOS.unindent.freeze
+HOMEBREW_HELP = <<~EOS.freeze
   Example usage:
     brew search [TEXT|/REGEX/]
     brew (info|home|options) [FORMULA...]
diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb
index 5eb03370664ef5ab0ca8fd7b7c2aa6bf60090d65..83bb712ab0a0dad7ac9f206a66916e867b77b904 100644
--- a/Library/Homebrew/cmd/info.rb
+++ b/Library/Homebrew/cmd/info.rb
@@ -67,9 +67,9 @@ module Homebrew
 
   def print_json
     ff = if ARGV.include? "--all"
-      Formula
+      Formula.sort
     elsif ARGV.include? "--installed"
-      Formula.installed
+      Formula.installed.sort
     else
       ARGV.formulae
     end
@@ -128,7 +128,7 @@ module Homebrew
       "#{c.name}#{reason}"
     end.sort!
     unless conflicts.empty?
-      puts <<-EOS.undent
+      puts <<~EOS
         Conflicts with:
           #{conflicts.join("\n  ")}
       EOS
diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb
index ca8f29477216ec984e2cd1e79b69a50a404f5a48..575dbc4b3a68e24322a24b96955e4575f66d8f0a 100644
--- a/Library/Homebrew/cmd/install.rb
+++ b/Library/Homebrew/cmd/install.rb
@@ -110,17 +110,17 @@ module Homebrew
       ARGV.formulae.each do |f|
         # head-only without --HEAD is an error
         if !ARGV.build_head? && f.stable.nil? && f.devel.nil?
-          raise <<-EOS.undent
-          #{f.full_name} is a head-only formula
-          Install with `brew install --HEAD #{f.full_name}`
+          raise <<~EOS
+            #{f.full_name} is a head-only formula
+            Install with `brew install --HEAD #{f.full_name}`
           EOS
         end
 
         # devel-only without --devel is an error
         if !ARGV.build_devel? && f.stable.nil? && f.head.nil?
-          raise <<-EOS.undent
-          #{f.full_name} is a devel-only formula
-          Install with `brew install --devel #{f.full_name}`
+          raise <<~EOS
+            #{f.full_name} is a devel-only formula
+            Install with `brew install --devel #{f.full_name}`
           EOS
         end
 
@@ -150,12 +150,12 @@ module Homebrew
           # sure --force flag is passed.
           if f.outdated?
             optlinked_version = Keg.for(f.opt_prefix).version
-            onoe <<-EOS.undent
+            onoe <<~EOS
               #{f.full_name} #{optlinked_version} is already installed
               To upgrade to #{f.version}, run `brew upgrade #{f.name}`
             EOS
           else
-            opoo <<-EOS.undent
+            opoo <<~EOS
               #{f.full_name} #{f.pkg_version} is already installed
             EOS
           end
@@ -173,13 +173,13 @@ module Homebrew
           msg = "#{f.full_name} #{installed_version} is already installed"
           linked_not_equals_installed = f.linked_version != installed_version
           if f.linked? && linked_not_equals_installed
-            msg = <<-EOS.undent
+            msg = <<~EOS
               #{msg}
               The currently linked version is #{f.linked_version}
               You can use `brew switch #{f} #{installed_version}` to link this version.
             EOS
           elsif !f.linked? || f.keg_only?
-            msg = <<-EOS.undent
+            msg = <<~EOS
               #{msg}, it's just not linked.
               You can use `brew link #{f}` to link this version.
             EOS
@@ -188,7 +188,7 @@ module Homebrew
         elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first
           msg = "#{old_formula.full_name} #{old_formula.installed_version} already installed"
           if !old_formula.linked? && !old_formula.keg_only?
-            msg = <<-EOS.undent
+            msg = <<~EOS
               #{msg}, it's just not linked.
               You can use `brew link #{old_formula.full_name}` to link this version.
             EOS
@@ -197,7 +197,7 @@ module Homebrew
         elsif f.migration_needed? && !ARGV.force?
           # Check if the formula we try to install is the same as installed
           # but not migrated one. If --force passed then install anyway.
-          opoo <<-EOS.undent
+          opoo <<~EOS
             #{f.oldname} already installed, it's just not migrated
             You can migrate formula with `brew migrate #{f}`
             Or you can force install it with `brew install #{f} --force`
@@ -283,7 +283,7 @@ module Homebrew
   def check_ppc
     case Hardware::CPU.type
     when :ppc
-      abort <<-EOS.undent
+      abort <<~EOS
         Sorry, Homebrew does not support your computer's CPU architecture.
         For PPC support, see: https://github.com/mistydemeo/tigerbrew
       EOS
@@ -308,7 +308,7 @@ module Homebrew
   def check_cellar
     FileUtils.mkdir_p HOMEBREW_CELLAR unless File.exist? HOMEBREW_CELLAR
   rescue
-    raise <<-EOS.undent
+    raise <<~EOS
       Could not create #{HOMEBREW_CELLAR}
       Check you have permission to write to #{HOMEBREW_CELLAR.parent}
     EOS
diff --git a/Library/Homebrew/cmd/leaves.rb b/Library/Homebrew/cmd/leaves.rb
index 4038aee4c345e5fb0174527dbbb1b46bda82194f..574ceb64ed8f5b01e109358630ac4594cee6e8af 100644
--- a/Library/Homebrew/cmd/leaves.rb
+++ b/Library/Homebrew/cmd/leaves.rb
@@ -9,7 +9,7 @@ module Homebrew
   module_function
 
   def leaves
-    installed = Formula.installed
+    installed = Formula.installed.sort
     deps_of_installed = Set.new
 
     installed.each do |f|
diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb
index 6c4b912e80b07d0a758e02eb6ef7c422da3c7f4c..5afb19ed6164b586dad7c250d0ad3b8532737c62 100644
--- a/Library/Homebrew/cmd/link.rb
+++ b/Library/Homebrew/cmd/link.rb
@@ -29,7 +29,7 @@ module Homebrew
       keg_only = keg_only?(keg.rack)
       if HOMEBREW_PREFIX.to_s == "/usr/local" && keg_only &&
          keg.name.start_with?("openssl", "libressl")
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Refusing to link: #{keg.name}
           Linking keg-only #{keg.name} means you may end up linking against the insecure,
           deprecated system OpenSSL while using the headers from Homebrew's #{keg.name}.
diff --git a/Library/Homebrew/cmd/linkapps.rb b/Library/Homebrew/cmd/linkapps.rb
index 8713b609ef55ba08569d48989be6587f45a68a7f..e8d482529224fd231ddf09f4e1e26f1f975367f8 100644
--- a/Library/Homebrew/cmd/linkapps.rb
+++ b/Library/Homebrew/cmd/linkapps.rb
@@ -19,7 +19,7 @@ module Homebrew
   module_function
 
   def linkapps
-    opoo <<-EOS.undent
+    opoo <<~EOS
       `brew linkapps` has been deprecated and will eventually be removed!
 
       Unfortunately `brew linkapps` cannot behave nicely with e.g. Spotlight using
diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb
index 436fc1f9764e5d6dde29511f25cd90071d04010c..f1766728607e5062066bc9cc9df6455cfad1fc5a 100644
--- a/Library/Homebrew/cmd/list.rb
+++ b/Library/Homebrew/cmd/list.rb
@@ -39,7 +39,7 @@ module Homebrew
       filtered_list
     elsif ARGV.named.empty?
       if ARGV.include? "--full-name"
-        full_names = Formula.installed.map(&:full_name).sort &tap_and_name_comparison
+        full_names = Formula.installed.map(&:full_name).sort(&tap_and_name_comparison)
         return if full_names.empty?
         puts Formatter.columns(full_names)
       else
@@ -87,7 +87,7 @@ module Homebrew
     dirs.delete "etc"
     dirs.delete "var"
 
-    args = dirs + %w[-type f (]
+    args = dirs.sort + %w[-type f (]
     args.concat UNBREWED_EXCLUDE_FILES.flat_map { |f| %W[! -name #{f}] }
     args.concat UNBREWED_EXCLUDE_PATHS.flat_map { |d| %W[! -path #{d}] }
     args.concat %w[)]
diff --git a/Library/Homebrew/cmd/log.rb b/Library/Homebrew/cmd/log.rb
index 9323c762d0aae4a7c7033e8c057b534bcb7f3213..898e921ba7b14bf5be8bd02e06c0fd81d71a5bda 100644
--- a/Library/Homebrew/cmd/log.rb
+++ b/Library/Homebrew/cmd/log.rb
@@ -31,7 +31,7 @@ module Homebrew
     end
 
     if File.exist? "#{repo}/.git/shallow"
-      opoo <<-EOS.undent
+      opoo <<~EOS
         #{name} is a shallow clone so only partial output will be shown.
         To get a full clone run:
           git -C "#{git_cd}" fetch --unshallow
diff --git a/Library/Homebrew/cmd/missing.rb b/Library/Homebrew/cmd/missing.rb
index 8a1dc506df26c078cbb752227f741e878bd42a19..707ad683455814079e55d1a9f2fd96d9bb111056 100644
--- a/Library/Homebrew/cmd/missing.rb
+++ b/Library/Homebrew/cmd/missing.rb
@@ -16,9 +16,9 @@ module Homebrew
     return unless HOMEBREW_CELLAR.exist?
 
     ff = if ARGV.named.empty?
-      Formula.installed
+      Formula.installed.sort
     else
-      ARGV.resolved_formulae
+      ARGV.resolved_formulae.sort
     end
 
     ff.each do |f|
diff --git a/Library/Homebrew/cmd/options.rb b/Library/Homebrew/cmd/options.rb
index 843d3a1ee842fd79c417bfbc1f25086cb4d11183..6bb6afafe9839c7941d91c52bed72b0e09dcd4b0 100644
--- a/Library/Homebrew/cmd/options.rb
+++ b/Library/Homebrew/cmd/options.rb
@@ -16,9 +16,9 @@ module Homebrew
 
   def options
     if ARGV.include? "--all"
-      puts_options Formula.to_a
+      puts_options Formula.to_a.sort
     elsif ARGV.include? "--installed"
-      puts_options Formula.installed
+      puts_options Formula.installed.sort
     else
       raise FormulaUnspecifiedError if ARGV.named.empty?
       puts_options ARGV.formulae
diff --git a/Library/Homebrew/cmd/readall.rb b/Library/Homebrew/cmd/readall.rb
index 3591e0c09aa34e5f7dc23243c77b78c294421a00..9fac52713d25ed1f439eea23b0d03e21b03f44fd 100644
--- a/Library/Homebrew/cmd/readall.rb
+++ b/Library/Homebrew/cmd/readall.rb
@@ -1,6 +1,7 @@
 #: @hide_from_man_page
 #:  * `readall` [tap]:
-#:    Import all formulae in a tap (defaults to core tap).
+#:    Import all formulae from specified taps (defaults to
+#:    all installed taps).
 #:
 #:    This can be useful for debugging issues across all formulae
 #:    when making significant changes to `formula.rb`,
@@ -13,16 +14,8 @@ module Homebrew
 
   def readall
     if ARGV.include?("--syntax")
-      ruby_files = []
-      scan_files = %W[
-        #{HOMEBREW_LIBRARY}/*.rb
-        #{HOMEBREW_LIBRARY}/Homebrew/**/*.rb
-      ]
-      Dir.glob(scan_files).each do |rb|
-        next if rb.include?("/vendor/")
-        next if rb.include?("/cask/")
-        ruby_files << rb
-      end
+      scan_files = "#{HOMEBREW_LIBRARY_PATH}/**/*.rb"
+      ruby_files = Dir.glob(scan_files).reject { |file| file =~ %r{/(vendor|cask)/} }
 
       Homebrew.failed = true unless Readall.valid_ruby_syntax?(ruby_files)
     end
@@ -31,7 +24,7 @@ module Homebrew
     taps = if ARGV.named.empty?
       Tap
     else
-      [Tap.fetch(ARGV.named.first)]
+      ARGV.named.map { |t| Tap.fetch(t) }
     end
     taps.each do |tap|
       Homebrew.failed = true unless Readall.valid_tap?(tap, options)
diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb
index c01a11c104ca6398730e5341c7b4236b0557081f..4ba5247f1241a81dcca181de602f7e7511e23021 100644
--- a/Library/Homebrew/cmd/search.rb
+++ b/Library/Homebrew/cmd/search.rb
@@ -24,7 +24,7 @@ module Homebrew
 
   def search
     if ARGV.empty?
-      puts Formatter.columns(Formula.full_names)
+      puts Formatter.columns(Formula.full_names.sort)
     elsif ARGV.include? "--macports"
       exec_browser "https://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
     elsif ARGV.include? "--fink"
@@ -52,15 +52,15 @@ module Homebrew
         results = search_taps(name)
       end
 
-      puts Formatter.columns(results) unless results.empty?
+      puts Formatter.columns(results.sort) unless results.empty?
     else
       query = ARGV.first
       regex = query_regexp(query)
       local_results = search_formulae(regex)
-      puts Formatter.columns(local_results) unless local_results.empty?
+      puts Formatter.columns(local_results.sort) unless local_results.empty?
 
       tap_results = search_taps(query)
-      puts Formatter.columns(tap_results) unless tap_results.empty?
+      puts Formatter.columns(tap_results.sort) unless tap_results.empty?
 
       if $stdout.tty?
         count = local_results.length + tap_results.length
@@ -87,7 +87,7 @@ module Homebrew
         arg.include?(char) && !arg.start_with?("/")
       end
     end
-    ohai <<-EOS.undent
+    ohai <<~EOS
       Did you mean to perform a regular expression search?
       Surround your query with /slashes/ to search locally by regex.
     EOS
diff --git a/Library/Homebrew/cmd/sh.rb b/Library/Homebrew/cmd/sh.rb
index 69f329cb3a332436624a74a0fd618e4f94d7eef0..3fe5f30055c672bd91790193b8c1a73d53101250 100644
--- a/Library/Homebrew/cmd/sh.rb
+++ b/Library/Homebrew/cmd/sh.rb
@@ -27,14 +27,14 @@ module Homebrew
     end
     ENV["PS1"] = 'brew \[\033[1;32m\]\w\[\033[0m\]$ '
     ENV["VERBOSE"] = "1"
-    puts <<-EOS.undent_________________________________________________________72
-         Your shell has been configured to use Homebrew's build environment;
-         this should help you build stuff. Notably though, the system versions of
-         gem and pip will ignore our configuration and insist on using the
-         environment they were built under (mostly). Sadly, scons will also
-         ignore our configuration.
-         When done, type `exit'.
-         EOS
+    puts <<~EOS
+      Your shell has been configured to use Homebrew's build environment;
+      this should help you build stuff. Notably though, the system versions of
+      gem and pip will ignore our configuration and insist on using the
+      environment they were built under (mostly). Sadly, scons will also
+      ignore our configuration.
+      When done, type `exit'.
+    EOS
     $stdout.flush
     exec ENV["SHELL"]
   end
diff --git a/Library/Homebrew/cmd/tap-info.rb b/Library/Homebrew/cmd/tap-info.rb
index cb0e0b387ec2eccb2c41a3a424019c75270a2aa0..d01ce8a02709e5e39754c13d874d211d517c9f90 100644
--- a/Library/Homebrew/cmd/tap-info.rb
+++ b/Library/Homebrew/cmd/tap-info.rb
@@ -21,10 +21,11 @@ module Homebrew
   module_function
 
   def tap_info
+    # TODO: This still returns a non-alphabetised list on APFS.
     if ARGV.include? "--installed"
       taps = Tap
     else
-      taps = ARGV.named.map do |name|
+      taps = ARGV.named.sort.map do |name|
         Tap.fetch(name)
       end
     end
diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb
index 4839ba1e06ce30534fe28d7c8776954ec6f1b8dc..f95b6c7bb4a91051926837197fde864280043c5e 100644
--- a/Library/Homebrew/cmd/uninstall.rb
+++ b/Library/Homebrew/cmd/uninstall.rb
@@ -131,7 +131,7 @@ module Homebrew
 
   class DeveloperDependentsMessage < DependentsMessage
     def output
-      opoo <<-EOS.undent
+      opoo <<~EOS
         #{list reqs} #{are_required_by_deps}.
         You can silence this warning with:
           #{sample_command}
@@ -141,7 +141,7 @@ module Homebrew
 
   class NondeveloperDependentsMessage < DependentsMessage
     def output
-      ofail <<-EOS.undent
+      ofail <<~EOS
         Refusing to uninstall #{list reqs}
         because #{they reqs} #{are_required_by_deps}.
         You can override this and force removal with:
diff --git a/Library/Homebrew/cmd/unlinkapps.rb b/Library/Homebrew/cmd/unlinkapps.rb
index 56dba3603c3f080be5c8c5dfb72dcc4522783414..7f401aaebbc1d3a10537b7fc88e6f4342449e5af 100644
--- a/Library/Homebrew/cmd/unlinkapps.rb
+++ b/Library/Homebrew/cmd/unlinkapps.rb
@@ -20,7 +20,7 @@ module Homebrew
   module_function
 
   def unlinkapps
-    opoo <<-EOS.undent
+    opoo <<~EOS
       `brew unlinkapps` has been deprecated and will eventually be removed!
 
       Unfortunately `brew linkapps` cannot behave nicely with e.g. Spotlight using either aliases or symlinks and Homebrew formulae do not build "proper" `.app` bundles that can be relocated. Instead, please consider using `brew cask` and migrate formulae using `.app`s to casks.
diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb
index 0974df0b415e7534ca985ef1d98e6433297ede03..98823a1526856f21c6cff2e96efda75d066df317 100644
--- a/Library/Homebrew/cmd/update-report.rb
+++ b/Library/Homebrew/cmd/update-report.rb
@@ -33,7 +33,7 @@ module Homebrew
 
         # Use an extra newline and bold to avoid this being missed.
         ohai "Homebrew has enabled anonymous aggregate user behaviour analytics."
-        puts <<-EOS.undent
+        puts <<~EOS
           #{Tty.bold}Read the analytics documentation (and how to opt-out) here:
             #{Formatter.url("https://docs.brew.sh/Analytics.html")}#{Tty.reset}
 
@@ -167,7 +167,7 @@ module Homebrew
     end
 
     if @migration_failed
-      opoo <<-EOS.undent
+      opoo <<~EOS
         Failed to migrate #{legacy_cache} to
         #{HOMEBREW_CACHE}. Please do so manually.
       EOS
@@ -176,7 +176,7 @@ module Homebrew
       FileUtils.rm_rf legacy_cache
       if legacy_cache.exist?
         FileUtils.touch migration_attempted_file
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Failed to delete #{legacy_cache}.
           Please do so manually.
         EOS
@@ -191,7 +191,7 @@ module Homebrew
     ohai "Migrating HOMEBREW_REPOSITORY (please wait)..."
 
     unless HOMEBREW_PREFIX.writable_real?
-      ofail <<-EOS.undent
+      ofail <<~EOS
         #{HOMEBREW_PREFIX} is not writable.
 
         You should change the ownership and permissions of #{HOMEBREW_PREFIX}
@@ -205,7 +205,7 @@ module Homebrew
     new_homebrew_repository = Pathname.new "/usr/local/Homebrew"
     new_homebrew_repository.rmdir_if_possible
     if new_homebrew_repository.exist?
-      ofail <<-EOS.undent
+      ofail <<~EOS
         #{new_homebrew_repository} already exists.
         Please remove it manually or uninstall and reinstall Homebrew into a new
         location as the migration cannot be done automatically.
@@ -258,7 +258,7 @@ module Homebrew
     end
 
     unless unremovable_paths.empty?
-      ofail <<-EOS.undent
+      ofail <<~EOS
         Could not remove old HOMEBREW_REPOSITORY paths!
         Please do this manually with:
           sudo rm -rf #{unremovable_paths.join " "}
@@ -274,7 +274,7 @@ module Homebrew
     begin
       FileUtils.ln_s(src.relative_path_from(dst.parent), dst)
     rescue Errno::EACCES, Errno::ENOENT
-      ofail <<-EOS.undent
+      ofail <<~EOS
         Could not create symlink at #{dst}!
         Please do this manually with:
           sudo ln -sf #{src} #{dst}
@@ -285,13 +285,13 @@ module Homebrew
     link_completions_manpages_and_docs(new_homebrew_repository)
 
     ohai "Migrated HOMEBREW_REPOSITORY to #{new_homebrew_repository}!"
-    puts <<-EOS.undent
+    puts <<~EOS
       Homebrew no longer needs to have ownership of /usr/local. If you wish you can
       return /usr/local to its default ownership with:
         sudo chown root:wheel #{HOMEBREW_PREFIX}
     EOS
   rescue => e
-    ofail <<-EOS.undent
+    ofail <<~EOS
       #{Tty.bold}Failed to migrate HOMEBREW_REPOSITORY to #{new_homebrew_repository}!#{Tty.reset}
       The error was:
         #{e}
@@ -309,7 +309,7 @@ module Homebrew
     Utils::Link.link_manpages(repository, command)
     Utils::Link.link_docs(repository, command)
   rescue => e
-    ofail <<-EOS.undent
+    ofail <<~EOS
       Failed to link all completions, docs and manpages:
         #{e}
     EOS
@@ -449,7 +449,7 @@ class Reporter
         next unless (HOMEBREW_PREFIX/"Caskroom"/new_name).exist?
         new_tap = Tap.fetch(new_tap_name)
         new_tap.install unless new_tap.installed?
-        ohai "#{name} has been moved to Homebrew.", <<-EOS.undent
+        ohai "#{name} has been moved to Homebrew.", <<~EOS
           To uninstall the cask run:
             brew cask uninstall --force #{name}
         EOS
@@ -480,14 +480,14 @@ class Reporter
           system HOMEBREW_BREW_FILE, "prune"
           ohai "brew cask install #{new_name}"
           system HOMEBREW_BREW_FILE, "cask", "install", new_name
-          ohai <<-EOS.undent
+          ohai <<~EOS
             #{name} has been moved to Homebrew-Cask.
             The existing keg has been unlinked.
             Please uninstall the formula when convenient by running:
               brew uninstall --force #{name}
           EOS
         else
-          ohai "#{name} has been moved to Homebrew-Cask.", <<-EOS.undent
+          ohai "#{name} has been moved to Homebrew-Cask.", <<~EOS
             To uninstall the formula and install the cask run:
               brew uninstall --force #{name}
               brew cask install #{new_name}
@@ -598,7 +598,7 @@ class ReporterHub
     return if formulae.empty?
     # Dump formula list.
     ohai title
-    puts Formatter.columns(formulae)
+    puts Formatter.columns(formulae.sort)
   end
 
   def installed?(formula)
diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb
index f1ce3c7da4ff1f742ed5503c2990f689b60fa1ff..de886ff3dd5296f822927a9ee71294491f6ff622 100644
--- a/Library/Homebrew/cmd/upgrade.rb
+++ b/Library/Homebrew/cmd/upgrade.rb
@@ -26,7 +26,7 @@ module Homebrew
     Homebrew.perform_preinstall_checks
 
     if ARGV.include?("--all")
-      opoo <<-EOS.undent
+      opoo <<~EOS
         We decided to not change the behaviour of `brew upgrade` so
         `brew upgrade --all` is equivalent to `brew upgrade` without any other
         arguments (so the `--all` is a no-op and can be removed).
diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb
index 0b09e1bf19d4436ec80ae84373f4c1b0767928d1..1688899f9d9ddf585806402cd185cf4259e11359 100644
--- a/Library/Homebrew/cmd/uses.rb
+++ b/Library/Homebrew/cmd/uses.rb
@@ -125,7 +125,7 @@ module Homebrew
     end
 
     return if uses.empty?
-    puts Formatter.columns(uses.map(&:full_name))
+    puts Formatter.columns(uses.map(&:full_name).sort)
     odie "Missing formulae should not have dependents!" if used_formulae_missing
   end
 end
diff --git a/Library/Homebrew/cmd/vendor-install.sh b/Library/Homebrew/cmd/vendor-install.sh
index 6d16a297d57604b7c877153d21e8a6df6c74b5fd..15caca8ef31d3c76268ff4f384451c429503e6c1 100644
--- a/Library/Homebrew/cmd/vendor-install.sh
+++ b/Library/Homebrew/cmd/vendor-install.sh
@@ -21,8 +21,8 @@ then
   fi
 elif [[ -n "$HOMEBREW_LINUX" ]]
 then
-  ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.x86_64_linux.bottle.tar.gz"
-  ruby_SHA="543c18bd33a300e6c16671437b1e0f17b03bb64e6a485fc15ff7de1eb1a0bc2a"
+  ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.x86_64_linux.bottle.1.tar.gz"
+  ruby_SHA="33643b1ca6f860d6df01686636326785763e5e81cf0cef37d8a7ab96a6ca1fa1"
 fi
 
 fetch() {
@@ -81,9 +81,9 @@ fetch() {
     trap - SIGINT
   fi
 
-  if [[ -x "$(which shasum)" ]]
+  if [[ -x "/usr/bin/shasum" ]]
   then
-    sha="$(shasum -a 256 "$CACHED_LOCATION" | cut -d' ' -f1)"
+    sha="$(/usr/bin/shasum -a 256 "$CACHED_LOCATION" | cut -d' ' -f1)"
   elif [[ -x "$(which sha256sum)" ]]
   then
     sha="$(sha256sum "$CACHED_LOCATION" | cut -d' ' -f1)"
diff --git a/Library/Homebrew/compat.rb b/Library/Homebrew/compat.rb
index 3c080f61688795bbdef8eb83afb82d41a6353439..8b3d72ec7b2942e52eb8195f8b4bbadb0d6826a4 100644
--- a/Library/Homebrew/compat.rb
+++ b/Library/Homebrew/compat.rb
@@ -26,3 +26,4 @@ require "compat/ENV/shared"
 require "compat/ENV/std"
 require "compat/ENV/super"
 require "compat/utils/shell"
+require "compat/extend/string"
diff --git a/Library/Homebrew/compat/extend/string.rb b/Library/Homebrew/compat/extend/string.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6069a6bec4a8a3b988988ac23486cbdd90e22a71
--- /dev/null
+++ b/Library/Homebrew/compat/extend/string.rb
@@ -0,0 +1,18 @@
+class String
+  def undent
+    gsub(/^[ \t]{#{(slice(/^[ \t]+/) || '').length}}/, "")
+  end
+  alias unindent undent
+
+  # eg:
+  #   if foo then <<-EOS.undent_________________________________________________________72
+  #               Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
+  #               eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+  #               minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+  #               ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
+  #               voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+  #               sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+  #               mollit anim id est laborum.
+  #               EOS
+  alias undent_________________________________________________________72 undent
+end
diff --git a/Library/Homebrew/compat/formula_specialties.rb b/Library/Homebrew/compat/formula_specialties.rb
index 78966625eef5bd62975b3cd8311d1efe6440446a..3f8102575df276960be0c06a04c31b7bcd169aed 100644
--- a/Library/Homebrew/compat/formula_specialties.rb
+++ b/Library/Homebrew/compat/formula_specialties.rb
@@ -31,7 +31,7 @@ class AmazonWebServicesFormula < Formula
   def standard_instructions(home_name, home_value = libexec)
     odeprecated "AmazonWebServicesFormula#standard_instructions", "Formula#caveats"
 
-    <<-EOS.undent
+    <<~EOS
       Before you can use these tools you must export some variables to your $SHELL.
 
       To export the needed variables, add them to your dotfiles.
diff --git a/Library/Homebrew/compat/hbc/cli.rb b/Library/Homebrew/compat/hbc/cli.rb
index 0173bce9c4618b49706a1f8d0f4c7e0670628c1a..60d298ed63af2a0cc381ae845de22104bab0a46e 100644
--- a/Library/Homebrew/compat/hbc/cli.rb
+++ b/Library/Homebrew/compat/hbc/cli.rb
@@ -5,7 +5,7 @@ module Hbc
     include Options
 
     option "--binarydir=PATH", (lambda do |*|
-      opoo <<-EOS.undent
+      opoo <<~EOS
         Option --binarydir is obsolete!
         Homebrew-Cask now uses the same location as your Homebrew installation for executable links.
       EOS
diff --git a/Library/Homebrew/compat/requirements.rb b/Library/Homebrew/compat/requirements.rb
index 77760d1dc2d44968595b855e9fe3a394355ab149..3886cd7c7ec23339f502d30da0cac62e7089c603 100644
--- a/Library/Homebrew/compat/requirements.rb
+++ b/Library/Homebrew/compat/requirements.rb
@@ -17,3 +17,4 @@ PythonDependency           = PythonRequirement
 TuntapDependency           = TuntapRequirement
 X11Dependency              = X11Requirement
 ConflictsWithBinaryOsxfuse = NonBinaryOsxfuseRequirement
+MinimumMacOSRequirement    = MacOSRequirement
diff --git a/Library/Homebrew/constants.rb b/Library/Homebrew/constants.rb
index b122946c8fbb767a11cac1e47e771550156ebf52..956548640ed7b65c18cf33a18e2cf84b773c87a3 100644
--- a/Library/Homebrew/constants.rb
+++ b/Library/Homebrew/constants.rb
@@ -2,4 +2,4 @@
 
 # RuboCop version used for `brew style` and `brew cask style`
 HOMEBREW_RUBOCOP_VERSION = "0.50.0"
-HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.14.2" # has to be updated when RuboCop version changes
+HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.14.4" # has to be updated when RuboCop version changes
diff --git a/Library/Homebrew/cxxstdlib.rb b/Library/Homebrew/cxxstdlib.rb
index 8a67a9c53bf1c8ff6a53843973f1ae95e584157e..7a833c92ce4be8ccbb5203c085918839e8aa6328 100644
--- a/Library/Homebrew/cxxstdlib.rb
+++ b/Library/Homebrew/cxxstdlib.rb
@@ -5,7 +5,7 @@ class CxxStdlib
 
   class CompatibilityError < StandardError
     def initialize(formula, dep, stdlib)
-      super <<-EOS.undent
+      super <<~EOS
         #{formula.full_name} dependency #{dep.name} was built with a different C++ standard
         library (#{stdlib.type_string} from #{stdlib.compiler}). This may cause problems at runtime.
         EOS
diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb
index f855669e0a375d4f09d04597c69e820dcd21c44c..bc0246dd253775e7bbb4aa2a4b7f9541c2e45bba 100644
--- a/Library/Homebrew/dependency_collector.rb
+++ b/Library/Homebrew/dependency_collector.rb
@@ -105,7 +105,8 @@ class DependencyCollector
     case spec
     when :x11        then X11Requirement.new(spec.to_s, tags)
     when :xcode      then XcodeRequirement.new(tags)
-    when :macos      then MinimumMacOSRequirement.new(tags)
+    when :linux      then LinuxRequirement.new(tags)
+    when :macos      then MacOSRequirement.new(tags)
     when :mysql      then MysqlRequirement.new(tags)
     when :postgresql then PostgresqlRequirement.new(tags)
     when :gpg        then GPG2Requirement.new(tags)
diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb
index b58672043a4fdf7b42f86784e30b665682185c93..bfe4dbc00c25746f2eeb5d43ed20b260a7b8084a 100644
--- a/Library/Homebrew/dev-cmd/audit.rb
+++ b/Library/Homebrew/dev-cmd/audit.rb
@@ -368,7 +368,7 @@ class FormulaAuditor
 
       if valid_versioned_aliases.empty?
         if formula.tap
-          problem <<-EOS.undent
+          problem <<~EOS
             Formula has other versions so create a versioned alias:
               cd #{formula.tap.alias_dir}
               ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name}
@@ -379,7 +379,7 @@ class FormulaAuditor
       end
 
       unless invalid_versioned_aliases.empty?
-        problem <<-EOS.undent
+        problem <<~EOS
           Formula has invalid versioned aliases:
             #{invalid_versioned_aliases.join("\n  ")}
         EOS
@@ -476,7 +476,7 @@ class FormulaAuditor
         when "gfortran"
           problem "Use `depends_on :fortran` instead of `depends_on 'gfortran'`"
         when "ruby"
-          problem <<-EOS.undent
+          problem <<~EOS
             Don't use "ruby" as a dependency. If this formula requires a
             minimum Ruby version not provided by the system you should
             use the RubyRequirement:
@@ -484,7 +484,7 @@ class FormulaAuditor
             where "1.8" is the minimum version of Ruby required.
           EOS
         when "open-mpi", "mpich"
-          problem <<-EOS.undent
+          problem <<~EOS
             There are multiple conflicting ways to install MPI. Use an MPIRequirement:
               depends_on :mpi => [<lang list>]
             Where <lang list> is a comma delimited list that can include:
@@ -492,7 +492,7 @@ class FormulaAuditor
             EOS
         when *BUILD_TIME_DEPS
           next if dep.build? || dep.run?
-          problem <<-EOS.undent
+          problem <<~EOS
             #{dep} dependency should be
               depends_on "#{dep}" => :build
             Or if it is indeed a runtime dependency
@@ -541,7 +541,7 @@ class FormulaAuditor
     first_word = reason.split[0]
 
     if reason =~ /\A[A-Z]/ && !reason.start_with?(*whitelist)
-      problem <<-EOS.undent
+      problem <<~EOS
         '#{first_word}' from the keg_only reason should be '#{first_word.downcase}'.
       EOS
     end
@@ -854,7 +854,7 @@ class FormulaAuditor
     return unless formula.tap&.official?
     return unless formula.tap.tap_migrations.key?(formula.name)
 
-    problem <<-EOS.undent
+    problem <<~EOS
       #{formula.name} seems to be listed in tap_migrations.json!
       Please remove #{formula.name} from present tap & tap_migrations.json
       before submitting it to Homebrew/homebrew-#{formula.tap.repo}.
@@ -865,7 +865,7 @@ class FormulaAuditor
     return unless formula.prefix.directory?
     return unless Keg.new(formula.prefix).empty_installation?
 
-    problem <<-EOS.undent
+    problem <<~EOS
       The installation seems to be empty. Please ensure the prefix
       is set correctly and expected files are installed.
       The prefix configure/make argument may be case-sensitive.
diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb
index fb862c77339db484e94173103bdeb5dc670d97f5..a54211f5c13fc4de66aa6a7f22f34f156c9cf2c8 100644
--- a/Library/Homebrew/dev-cmd/bottle.rb
+++ b/Library/Homebrew/dev-cmd/bottle.rb
@@ -324,7 +324,7 @@ module Homebrew
           "#{key}: old: #{old_value}, new: #{value}"
         end
 
-        odie <<-EOS.undent
+        odie <<~EOS
           --keep-old was passed but there are changes in:
           #{mismatches.join("\n")}
         EOS
@@ -428,7 +428,7 @@ module Homebrew
               end
 
               unless mismatches.empty?
-                odie <<-EOS.undent
+                odie <<~EOS
                   --keep-old was passed but there are changes in:
                   #{mismatches.join("\n")}
                 EOS
diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
index 87d8274ccbb8a0048158c9682cb04df77748680b..fe7e6be3dabaad387dafcaf50c45eea55c8b0650 100644
--- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb
+++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
@@ -91,7 +91,7 @@ module Homebrew
     pull_requests = fetch_pull_requests(formula)
     return unless pull_requests
     return if pull_requests.empty?
-    duplicates_message = <<-EOS.undent
+    duplicates_message = <<~EOS
       These open pull requests may be duplicates:
       #{pull_requests.map { |pr| "#{pr["title"]} #{pr["html_url"]}" }.join("\n")}
     EOS
@@ -101,7 +101,7 @@ module Homebrew
     elsif !ARGV.force? && ARGV.flag?("--quiet")
       odie error_message
     elsif !ARGV.force?
-      odie <<-EOS.undent
+      odie <<~EOS
         #{duplicates_message.chomp}
         #{error_message}
       EOS
@@ -247,13 +247,13 @@ module Homebrew
 
     if new_formula_version < old_formula_version
       formula.path.atomic_write(backup_file) unless ARGV.dry_run?
-      odie <<-EOS.undent
+      odie <<~EOS
         You probably need to bump this formula manually since changing the
         version from #{old_formula_version} to #{new_formula_version} would be a downgrade.
       EOS
     elsif new_formula_version == old_formula_version
       formula.path.atomic_write(backup_file) unless ARGV.dry_run?
-      odie <<-EOS.undent
+      odie <<~EOS
         You probably need to bump this formula manually since the new version
         and old version are both #{new_formula_version}.
       EOS
@@ -312,17 +312,15 @@ module Homebrew
         remote = Utils.popen_read("hub fork 2>&1")[/remote:? (\S+)/, 1] if remote.to_s.empty?
         odie "cannot get remote from 'hub'!" if remote.to_s.empty?
         safe_system "git", "push", "--set-upstream", remote, "#{branch}:#{branch}"
-        pr_message = <<-EOS.undent
+        pr_message = <<~EOS
           #{formula.name} #{new_formula_version}#{devel_message}
 
           Created with `brew bump-formula-pr`.
         EOS
         user_message = ARGV.value("message")
         if user_message
-          pr_message += <<-EOS.undent
-
+          pr_message += <<~EOS
             ---
-
             #{user_message}
           EOS
         end
diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb
index e423842bac9da9bd6aa33f24da0241a01eb0030c..e5481b532cf479bb229fac8306e87bbf45a7c151 100644
--- a/Library/Homebrew/dev-cmd/create.rb
+++ b/Library/Homebrew/dev-cmd/create.rb
@@ -71,7 +71,7 @@ module Homebrew
 
       if Formula.aliases.include? fc.name
         realname = Formulary.canonical_name(fc.name)
-        raise <<-EOS.undent
+        raise <<~EOS
           The formula #{realname} is already aliased to #{fc.name}
           Please check that you are not creating a duplicate.
           To force creation use --force.
@@ -165,7 +165,7 @@ class FormulaCreator
     path.write ERB.new(template, nil, ">").result(binding)
   end
 
-  def template; <<-EOS.undent
+  def template; <<~EOS
     # Documentation: https://docs.brew.sh/Formula-Cookbook.html
     #                http://www.rubydoc.info/github/Homebrew/brew/master/Formula
     # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
diff --git a/Library/Homebrew/dev-cmd/edit.rb b/Library/Homebrew/dev-cmd/edit.rb
index b1e485fe248fb786e36f7e24179945e10bfbf849..0039b4cce6d7ad8aab44b3ee75cbd60cad5cbfc2 100644
--- a/Library/Homebrew/dev-cmd/edit.rb
+++ b/Library/Homebrew/dev-cmd/edit.rb
@@ -11,7 +11,7 @@ module Homebrew
 
   def edit
     unless (HOMEBREW_REPOSITORY/".git").directory?
-      raise <<-EOS.undent
+      raise <<~EOS
         Changes will be lost!
         The first time you `brew update', all local changes will be lost, you should
         thus `brew update' before you `brew edit'!
diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb
index 6445bc34cf4fd921da769ce5090ce77dc96d406e..bf19ee3c577c3dcfed1b5269ebdd4b870514d968 100644
--- a/Library/Homebrew/dev-cmd/mirror.rb
+++ b/Library/Homebrew/dev-cmd/mirror.rb
@@ -20,7 +20,7 @@ module Homebrew
       package_url = "#{bintray_repo_url}/#{bintray_package}"
 
       unless system "curl", "--silent", "--fail", "--output", "/dev/null", package_url
-        package_blob = <<-EOS.undent
+        package_blob = <<~EOS
           {"name": "#{bintray_package}",
            "public_download_numbers": true,
            "public_stats": true}
diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb
index aa3c9a9d7100c98866de216c5b907d82882669ca..79b9da9ebb3f7061b28faa1af4d61d62b944691c 100644
--- a/Library/Homebrew/dev-cmd/pull.rb
+++ b/Library/Homebrew/dev-cmd/pull.rb
@@ -1,4 +1,4 @@
-#:  * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] [`--warn-on-publish-failure`] <patch-source> [<patch-source>]:
+#:  * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] [`--warn-on-publish-failure`] [`--bintray-org=`<bintray-org>] [`--test-bot-user=`<test-bot-user>] <patch-source> [<patch-source>]:
 #:
 #:    Gets a patch from a GitHub commit or pull request and applies it to Homebrew.
 #:    Optionally, installs the formulae changed by the patch.
@@ -41,6 +41,12 @@
 #:
 #:    If `--warn-on-publish-failure` was passed, do not exit if there's a
 #:    failure publishing bottles on Bintray.
+#:
+#:    If `--bintray-org=`<bintray-org> is passed, publish at the given Bintray
+#:    organisation.
+#:
+#:    If `--test-bot-user=`<test-bot-user> is passed, pull the bottle block
+#:    commit from the specified user on GitHub.
 
 require "net/http"
 require "net/https"
@@ -52,6 +58,18 @@ require "tap"
 require "version"
 require "pkg_version"
 
+module GitHub
+  module_function
+
+  # Return the corresponding test-bot user name for the given GitHub organization.
+  def test_bot_user(user)
+    test_bot = ARGV.value "test-bot-user"
+    return test_bot if test_bot
+    return "BrewTestBot" if user.casecmp("homebrew").zero?
+    "#{user.capitalize}TestBot"
+  end
+end
+
 module Homebrew
   module_function
 
@@ -225,7 +243,7 @@ module Homebrew
           url
         else
           bottle_branch = "pull-bottle-#{issue}"
-          "https://github.com/BrewTestBot/homebrew-#{tap.repo}/compare/homebrew:master...pr-#{issue}"
+          "https://github.com/#{GitHub.test_bot_user user}/homebrew-#{tap.repo}/compare/#{user}:master...pr-#{issue}"
         end
 
         curl "--silent", "--fail", "--output", "/dev/null", "--head", bottle_commit_url
@@ -257,7 +275,7 @@ module Homebrew
     str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
   end
 
-  def publish_changed_formula_bottles(_tap, changed_formulae_names)
+  def publish_changed_formula_bottles(tap, changed_formulae_names)
     if ENV["HOMEBREW_DISABLE_LOAD_FORMULA"]
       raise "Need to load formulae to publish them!"
     end
@@ -268,7 +286,8 @@ module Homebrew
       changed_formulae_names.each do |name|
         f = Formula[name]
         next if f.bottle_unneeded? || f.bottle_disabled?
-        next unless publish_bottle_file_on_bintray(f, bintray_creds)
+        bintray_org = ARGV.value("bintray-org") || tap.user.downcase
+        next unless publish_bottle_file_on_bintray(f, bintray_org, bintray_creds)
         published << f.full_name
       end
     else
@@ -420,7 +439,7 @@ module Homebrew
   end
 
   # Publishes the current bottle files for a given formula to Bintray
-  def publish_bottle_file_on_bintray(f, creds)
+  def publish_bottle_file_on_bintray(f, bintray_org, creds)
     repo = Utils::Bottles::Bintray.repository(f.tap)
     package = Utils::Bottles::Bintray.package(f.name)
     info = FormulaInfoFromJson.lookup(f.name)
@@ -437,7 +456,7 @@ module Homebrew
          "--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST",
          "--header", "Content-Type: application/json",
          "--data", '{"publish_wait_for_secs": 0}',
-         "https://api.bintray.com/content/homebrew/#{repo}/#{package}/#{version}/publish"
+         "https://api.bintray.com/content/#{bintray_org}/#{repo}/#{package}/#{version}/publish"
     true
   rescue => e
     raise unless ARGV.include?("--warn-on-publish-failure")
diff --git a/Library/Homebrew/dev-cmd/tap-new.rb b/Library/Homebrew/dev-cmd/tap-new.rb
index 964ba2f5d429889c6c0cabadfa65e0bfb3e7c280..1c3bf20ebc990120de89923630cb9f9c37ae4e98 100644
--- a/Library/Homebrew/dev-cmd/tap-new.rb
+++ b/Library/Homebrew/dev-cmd/tap-new.rb
@@ -24,7 +24,7 @@ module Homebrew
 
     (tap.path/"Formula").mkpath
 
-    readme = <<-EOS.undent
+    readme = <<~EOS
       # #{titleized_user} #{titleized_repo}
 
       ## How do I install these formulae?
@@ -43,7 +43,7 @@ module Homebrew
     EOS
     write_path(tap, "README.md", readme)
 
-    travis = <<-EOS.undent
+    travis = <<~EOS
       language: ruby
       os: osx
       env: OSX=10.12
diff --git a/Library/Homebrew/dev-cmd/update-test.rb b/Library/Homebrew/dev-cmd/update-test.rb
index 1f1cdbeed160a0b14614224fe833cfbe6e633cec..ee189ad5ef9aa6cdd8bc753909b654da00f7e56d 100644
--- a/Library/Homebrew/dev-cmd/update-test.rb
+++ b/Library/Homebrew/dev-cmd/update-test.rb
@@ -88,7 +88,7 @@ module Homebrew
       safe_system "brew", "update", "--verbose"
       actual_end_commit = Utils.popen_read("git", "rev-parse", branch).chomp
       if start_commit != end_commit && start_commit == actual_end_commit
-        raise <<-EOS.undent
+        raise <<~EOS
           brew update didn't update #{branch}!
           Start commit:        #{start_commit}
           Expected end commit: #{end_commit}
diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb
index 3edf31012f7170a287018be03b86c10333eb27f7..88d461e0aa0838400d24ad16fb4493121b7a8e1a 100644
--- a/Library/Homebrew/diagnostic.rb
+++ b/Library/Homebrew/diagnostic.rb
@@ -92,7 +92,7 @@ module Homebrew
       def check_for_installed_developer_tools
         return if DevelopmentTools.installed?
 
-        <<-EOS.undent
+        <<~EOS
           No developer tools installed.
           #{DevelopmentTools.installation_instructions}
         EOS
@@ -101,7 +101,7 @@ module Homebrew
       def check_build_from_source
         return unless ENV["HOMEBREW_BUILD_FROM_SOURCE"]
 
-        <<-EOS.undent
+        <<~EOS
           You have HOMEBREW_BUILD_FROM_SOURCE set. This environment variable is
           intended for use by Homebrew developers. If you are encountering errors,
           please try unsetting this. Please do not file issues if you encounter
@@ -114,7 +114,7 @@ module Homebrew
         bad_paths = PATH.new(ENV["HOMEBREW_PATH"]).select { |p| p.end_with?("/") }
         return if bad_paths.empty?
 
-        inject_file_list bad_paths, <<-EOS.undent
+        inject_file_list bad_paths, <<~EOS
           Some directories in your path end in a slash.
           Directories in your path should not end in a slash. This can break other
           doctor checks. The following directories should be edited:
@@ -136,7 +136,7 @@ module Homebrew
         # Only warn if Python lives with Anaconda, since is most problematic case.
         return unless python_directory == anaconda_directory
 
-        <<-EOS.undent
+        <<~EOS
           Anaconda is known to frequently break Homebrew builds, including Vim and
           MacVim, due to bundling many duplicates of system and Homebrew-available
           tools.
@@ -182,7 +182,7 @@ module Homebrew
           "sentinel.dylib", # SentinelOne
         ]
 
-        __check_stray_files "/usr/local/lib", "*.dylib", white_list, <<-EOS.undent
+        __check_stray_files "/usr/local/lib", "*.dylib", white_list, <<~EOS
           Unbrewed dylibs were found in /usr/local/lib.
           If you didn't put them there on purpose they could cause problems when
           building Homebrew formulae, and may need to be deleted.
@@ -209,7 +209,7 @@ module Homebrew
           "libtrustedcomponents.a", # Symantec Endpoint Protection
         ]
 
-        __check_stray_files "/usr/local/lib", "*.a", white_list, <<-EOS.undent
+        __check_stray_files "/usr/local/lib", "*.a", white_list, <<~EOS
           Unbrewed static libraries were found in /usr/local/lib.
           If you didn't put them there on purpose they could cause problems when
           building Homebrew formulae, and may need to be deleted.
@@ -229,7 +229,7 @@ module Homebrew
           "libublio.pc", # NTFS-3G
         ]
 
-        __check_stray_files "/usr/local/lib/pkgconfig", "*.pc", white_list, <<-EOS.undent
+        __check_stray_files "/usr/local/lib/pkgconfig", "*.pc", white_list, <<~EOS
           Unbrewed .pc files were found in /usr/local/lib/pkgconfig.
           If you didn't put them there on purpose they could cause problems when
           building Homebrew formulae, and may need to be deleted.
@@ -250,7 +250,7 @@ module Homebrew
           "libublio.la", # NTFS-3G
         ]
 
-        __check_stray_files "/usr/local/lib", "*.la", white_list, <<-EOS.undent
+        __check_stray_files "/usr/local/lib", "*.la", white_list, <<~EOS
           Unbrewed .la files were found in /usr/local/lib.
           If you didn't put them there on purpose they could cause problems when
           building Homebrew formulae, and may need to be deleted.
@@ -269,7 +269,7 @@ module Homebrew
           "ntfs-3g/**/*.h", # NTFS-3G
         ]
 
-        __check_stray_files "/usr/local/include", "**/*.h", white_list, <<-EOS.undent
+        __check_stray_files "/usr/local/include", "**/*.h", white_list, <<~EOS
           Unbrewed header files were found in /usr/local/include.
           If you didn't put them there on purpose they could cause problems when
           building Homebrew formulae, and may need to be deleted.
@@ -291,7 +291,7 @@ module Homebrew
         end
         return if broken_symlinks.empty?
 
-        inject_file_list broken_symlinks, <<-EOS.undent
+        inject_file_list broken_symlinks, <<~EOS
           Broken symlinks were found. Remove them with `brew prune`:
         EOS
       end
@@ -300,7 +300,7 @@ module Homebrew
         world_writable = HOMEBREW_TEMP.stat.mode & 0777 == 0777
         return if !world_writable || HOMEBREW_TEMP.sticky?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_TEMP} is world-writable but does not have the sticky bit set.
           Please execute `sudo chmod +t #{HOMEBREW_TEMP}` in your Terminal.
         EOS
@@ -309,7 +309,7 @@ module Homebrew
       def check_access_homebrew_repository
         return if HOMEBREW_REPOSITORY.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_REPOSITORY} is not writable.
 
           You should change the ownership and permissions of #{HOMEBREW_REPOSITORY}
@@ -330,7 +330,7 @@ module Homebrew
 
         return if not_writable_dirs.empty?
 
-        <<-EOS.undent
+        <<~EOS
           The following directories are not writable:
           #{not_writable_dirs.join("\n")}
 
@@ -348,7 +348,7 @@ module Homebrew
         return unless Language::Python.homebrew_site_packages.exist?
         return if Language::Python.homebrew_site_packages.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{Language::Python.homebrew_site_packages} isn't writable.
           This can happen if you "sudo pip install" software that isn't managed
           by Homebrew. If you install a formula with Python modules, the install
@@ -364,7 +364,7 @@ module Homebrew
         return unless HOMEBREW_LOCK_DIR.exist?
         return if HOMEBREW_LOCK_DIR.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_LOCK_DIR} isn't writable.
           Homebrew writes lock files to this location.
 
@@ -378,7 +378,7 @@ module Homebrew
         return unless HOMEBREW_LOGS.exist?
         return if HOMEBREW_LOGS.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_LOGS} isn't writable.
           Homebrew writes debugging logs to this location.
 
@@ -392,7 +392,7 @@ module Homebrew
         return unless HOMEBREW_CACHE.exist?
         return if HOMEBREW_CACHE.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_CACHE} isn't writable.
           This can happen if you run `brew install` or `brew fetch` as another user.
           Homebrew caches downloaded files to this location.
@@ -407,7 +407,7 @@ module Homebrew
         return unless HOMEBREW_CELLAR.exist?
         return if HOMEBREW_CELLAR.writable_real?
 
-        <<-EOS.undent
+        <<~EOS
           #{HOMEBREW_CELLAR} isn't writable.
 
           You should change the ownership and permissions of #{HOMEBREW_CELLAR}
@@ -421,7 +421,7 @@ module Homebrew
         return unless (HOMEBREW_REPOSITORY/"Cellar").exist?
         return unless (HOMEBREW_PREFIX/"Cellar").exist?
 
-        <<-EOS.undent
+        <<~EOS
           You have multiple Cellars.
           You should delete #{HOMEBREW_REPOSITORY}/Cellar:
             rm -rf #{HOMEBREW_REPOSITORY}/Cellar
@@ -445,13 +445,13 @@ module Homebrew
                           .select { |bn| File.exist? "/usr/bin/#{bn}" }
 
               unless conflicts.empty?
-                message = inject_file_list conflicts, <<-EOS.undent
+                message = inject_file_list conflicts, <<~EOS
                   /usr/bin occurs before #{HOMEBREW_PREFIX}/bin
                   This means that system-provided programs will be used instead of those
                   provided by Homebrew. The following tools exist at both paths:
                 EOS
 
-                message += <<-EOS.undent
+                message += <<~EOS
 
                   Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin
                   occurs before /usr/bin. Here is a one-liner:
@@ -472,7 +472,7 @@ module Homebrew
       def check_user_path_2
         return if @seen_prefix_bin
 
-        <<-EOS.undent
+        <<~EOS
           Homebrew's bin was not found in your PATH.
           Consider setting the PATH for example like so
             #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/bin")}
@@ -486,7 +486,7 @@ module Homebrew
         sbin = HOMEBREW_PREFIX/"sbin"
         return unless sbin.directory? && !sbin.children.empty?
 
-        <<-EOS.undent
+        <<~EOS
           Homebrew's sbin was not found in your PATH but you have installed
           formulae that put executables in #{HOMEBREW_PREFIX}/sbin.
           Consider setting the PATH for example like so
@@ -500,7 +500,7 @@ module Homebrew
         end
         return unless curlrc_found
 
-        <<-EOS.undent
+        <<~EOS
           You have a curlrc file
           If you have trouble downloading packages with Homebrew, then maybe this
           is the problem? If the following command doesn't work, then try removing
@@ -526,7 +526,7 @@ module Homebrew
         end
         return if gettext&.linked_keg&.directory? && homebrew_owned
 
-        inject_file_list @found, <<-EOS.undent
+        inject_file_list @found, <<~EOS
           gettext files detected at a system prefix.
           These files can cause compilation and link failures, especially if they
           are compiled with improper architectures. Consider removing these files:
@@ -544,13 +544,13 @@ module Homebrew
         end
         if libiconv&.linked_keg&.directory?
           unless libiconv.keg_only?
-            <<-EOS.undent
+            <<~EOS
               A libiconv formula is installed and linked.
               This will break stuff. For serious. Unlink it.
             EOS
           end
         else
-          inject_file_list @found, <<-EOS.undent
+          inject_file_list @found, <<~EOS
             libiconv files detected at a system prefix other than /usr.
             Homebrew doesn't provide a libiconv formula, and expects to link against
             the system version in /usr. libiconv in other prefixes can cause
@@ -588,7 +588,7 @@ module Homebrew
 
         return if scripts.empty?
 
-        inject_file_list scripts, <<-EOS.undent
+        inject_file_list scripts, <<~EOS
           "config" scripts exist outside your system or Homebrew directories.
           `./configure` scripts often look for *-config scripts to determine if
           software packages are installed, and what additional flags to use when
@@ -605,13 +605,13 @@ module Homebrew
         return if dyld_vars.empty?
 
         values = dyld_vars.map { |var| "#{var}: #{ENV.fetch(var)}" }
-        message = inject_file_list values, <<-EOS.undent
+        message = inject_file_list values, <<~EOS
           Setting DYLD_* vars can break dynamic linking.
           Set variables:
         EOS
 
         if dyld_vars.include? "DYLD_INSERT_LIBRARIES"
-          message += <<-EOS.undent
+          message += <<~EOS
 
             Setting DYLD_INSERT_LIBRARIES can cause Go builds to fail.
             Having this set is common if you use this software:
@@ -624,7 +624,7 @@ module Homebrew
 
       def check_ssl_cert_file
         return unless ENV.key?("SSL_CERT_FILE")
-        <<-EOS.undent
+        <<~EOS
           Setting SSL_CERT_FILE can break downloading files; if that happens
           you should unset it before running Homebrew.
 
@@ -638,7 +638,7 @@ module Homebrew
         return unless HOMEBREW_CELLAR.exist?
         return unless HOMEBREW_CELLAR.symlink?
 
-        <<-EOS.undent
+        <<~EOS
           Symlinked Cellars can cause problems.
           Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR}
                           which resolves to: #{HOMEBREW_CELLAR.realpath}
@@ -675,7 +675,7 @@ module Homebrew
 
         return if where_cellar == where_tmp
 
-        <<-EOS.undent
+        <<~EOS
           Your Cellar and TEMP directories are on different volumes.
           macOS won't move relative symlinks across volumes unless the target file already
           exists. Brews known to be affected by this are Git and Narwhal.
@@ -692,7 +692,7 @@ module Homebrew
 
         git = Formula["git"]
         git_upgrade_cmd = git.any_version_installed? ? "upgrade" : "install"
-        <<-EOS.undent
+        <<~EOS
           An outdated version (#{Utils.git_version}) of Git was detected in your PATH.
           Git 1.8.5 or newer is required to perform checkouts over HTTPS from GitHub and
           to support the 'git -C <path>' option.
@@ -704,7 +704,7 @@ module Homebrew
       def check_for_git
         return if Utils.git_available?
 
-        <<-EOS.undent
+        <<~EOS
           Git could not be found in your PATH.
           Homebrew uses Git for several internal functions, and some formulae use Git
           checkouts instead of stable tarballs. You may want to install Git:
@@ -718,7 +718,7 @@ module Homebrew
         autocrlf = HOMEBREW_REPOSITORY.cd { `git config --get core.autocrlf`.chomp }
         return unless autocrlf == "true"
 
-        <<-EOS.undent
+        <<~EOS
           Suspicious Git newline settings found.
 
           The detected Git newline settings will cause checkout problems:
@@ -736,7 +736,7 @@ module Homebrew
         origin = HOMEBREW_REPOSITORY.git_origin
 
         if origin.nil?
-          <<-EOS.undent
+          <<~EOS
             Missing Homebrew/brew git origin remote.
 
             Without a correctly configured origin, Homebrew won't update
@@ -744,7 +744,7 @@ module Homebrew
               git -C "#{HOMEBREW_REPOSITORY}" remote add origin #{Formatter.url("https://github.com/Homebrew/brew.git")}
           EOS
         elsif origin !~ %r{Homebrew/brew(\.git|/)?$}
-          <<-EOS.undent
+          <<~EOS
             Suspicious Homebrew/brew git origin remote found.
 
             With a non-standard origin, Homebrew won't pull updates from
@@ -765,7 +765,7 @@ module Homebrew
         origin = coretap_path.git_origin
 
         if origin.nil?
-          <<-EOS.undent
+          <<~EOS
             Missing #{CoreTap.instance} git origin remote.
 
             Without a correctly configured origin, Homebrew won't update
@@ -775,7 +775,7 @@ module Homebrew
         elsif origin !~ %r{Homebrew/homebrew-core(\.git|/)?$}
           return if ENV["CI"] && origin.include?("Homebrew/homebrew-test-bot")
 
-          <<-EOS.undent
+          <<~EOS
             Suspicious #{CoreTap.instance} git origin remote found.
 
             With a non-standard origin, Homebrew won't pull updates from
@@ -787,6 +787,18 @@ module Homebrew
               git -C "#{coretap_path}" remote set-url origin #{Formatter.url("https://github.com/Homebrew/homebrew-core.git")}
           EOS
         end
+
+        return if ENV["CI"] || ENV["JENKINS_HOME"]
+
+        branch = coretap_path.git_branch
+        return if branch.nil? || branch =~ /master/
+
+        <<~EOS
+          Homebrew/homebrew-core is not on the master branch
+
+          Check out the master branch by running:
+            git -C "$(brew --repo homebrew/core)" checkout master
+        EOS
       end
 
       def __check_linked_brew(f)
@@ -804,12 +816,12 @@ module Homebrew
       def check_for_linked_keg_only_brews
         return unless HOMEBREW_CELLAR.exist?
 
-        linked = Formula.installed.select do |f|
+        linked = Formula.installed.sort.select do |f|
           f.keg_only? && __check_linked_brew(f)
         end
         return if linked.empty?
 
-        inject_file_list linked.map(&:full_name), <<-EOS.undent
+        inject_file_list linked.map(&:full_name), <<~EOS
           Some keg-only formula are linked into the Cellar.
           Linking a keg-only formula, such as gettext, into the cellar with
           `brew link <formula>` will cause other formulae to detect them during
@@ -835,7 +847,7 @@ module Homebrew
                            .select { |framework| File.exist? framework }
         return if frameworks_found.empty?
 
-        inject_file_list frameworks_found, <<-EOS.undent
+        inject_file_list frameworks_found, <<~EOS
           Some frameworks can be picked up by CMake's build system and likely
           cause the build to fail. To compile CMake, you may wish to move these
           out of the way:
@@ -846,7 +858,7 @@ module Homebrew
         tmpdir = ENV["TMPDIR"]
         return if tmpdir.nil? || File.directory?(tmpdir)
 
-        <<-EOS.undent
+        <<~EOS
           TMPDIR #{tmpdir.inspect} doesn't exist.
         EOS
       end
@@ -859,7 +871,7 @@ module Homebrew
         end
         return if missing.empty?
 
-        <<-EOS.undent
+        <<~EOS
           Some installed formula are missing dependencies.
           You should `brew install` the missing dependencies:
             brew install #{missing.sort_by(&:full_name) * " "}
@@ -874,7 +886,7 @@ module Homebrew
           return if `git status --untracked-files=all --porcelain -- Library/Homebrew/ 2>/dev/null`.chomp.empty?
         end
 
-        <<-EOS.undent
+        <<~EOS
           You have uncommitted modifications to Homebrew
           If this is a surprise to you, then you should stash these modifications.
           Stashing returns Homebrew to a pristine state but can be undone
@@ -886,7 +898,7 @@ module Homebrew
       def check_for_enthought_python
         return unless which "enpkg"
 
-        <<-EOS.undent
+        <<~EOS
           Enthought Python was found in your PATH.
           This can cause build problems, as this software installs its own
           copies of iconv and libxml2 into directories that are picked up by
@@ -897,7 +909,7 @@ module Homebrew
       def check_for_library_python
         return unless File.exist?("/Library/Frameworks/Python.framework")
 
-        <<-EOS.undent
+        <<~EOS
           Python is installed at /Library/Frameworks/Python.framework
 
           Homebrew only supports building against the System-provided Python or a
@@ -910,12 +922,12 @@ module Homebrew
         message = ""
         ["", "3"].map do |suffix|
           next unless paths.include?((HOMEBREW_PREFIX/"share/python#{suffix}").to_s)
-          message += <<-EOS.undent
-              #{HOMEBREW_PREFIX}/share/python#{suffix} is not needed in PATH.
+          message += <<~EOS
+            #{HOMEBREW_PREFIX}/share/python#{suffix} is not needed in PATH.
           EOS
         end
         unless message.empty?
-          message += <<-EOS.undent
+          message += <<~EOS
 
             Formerly homebrew put Python scripts you installed via `pip` or `pip3`
             (or `easy_install`) into that directory above but now it can be removed
@@ -937,7 +949,7 @@ module Homebrew
         return if Regexp.last_match(1).nil?
         return if Regexp.last_match(1) == "2"
 
-        <<-EOS.undent
+        <<~EOS
           python is symlinked to python#{Regexp.last_match(1)}
           This will confuse build scripts and in general lead to subtle breakage.
         EOS
@@ -950,7 +962,7 @@ module Homebrew
         gnubin = %W[#{coreutils.opt_libexec}/gnubin #{coreutils.libexec}/gnubin]
         return if (paths & gnubin).empty?
 
-        <<-EOS.undent
+        <<~EOS
           Putting non-prefixed coreutils in your path can cause gmp builds to fail.
         EOS
       rescue FormulaUnavailableError
@@ -965,7 +977,7 @@ module Homebrew
         default_names = Tab.for_name("findutils").with? "default-names"
         return if !default_names && (paths & gnubin).empty?
 
-        <<-EOS.undent
+        <<~EOS
           Putting non-prefixed findutils in your path can cause python builds to fail.
         EOS
       rescue FormulaUnavailableError
@@ -975,7 +987,7 @@ module Homebrew
       def check_for_pydistutils_cfg_in_home
         return unless File.exist? "#{ENV["HOME"]}/.pydistutils.cfg"
 
-        <<-EOS.undent
+        <<~EOS
           A .pydistutils.cfg file was found in $HOME, which may cause Python
           builds to fail. See:
             #{Formatter.url("https://bugs.python.org/issue6138")}
@@ -997,7 +1009,7 @@ module Homebrew
         end.map(&:basename)
         return if unlinked.empty?
 
-        inject_file_list unlinked, <<-EOS.undent
+        inject_file_list unlinked, <<~EOS
           You have unlinked kegs in your Cellar
           Leaving kegs unlinked can lead to build-trouble and cause brews that depend on
           those kegs to fail to run properly once built. Run `brew link` on these:
@@ -1007,7 +1019,7 @@ module Homebrew
       def check_for_old_env_vars
         return unless ENV["HOMEBREW_KEEP_INFO"]
 
-        <<-EOS.undent
+        <<~EOS
           `HOMEBREW_KEEP_INFO` is no longer used
           info files are no longer deleted by default; you may
           remove this environment variable.
@@ -1021,7 +1033,7 @@ module Homebrew
         return unless Language::Python.in_sys_path?("python", homebrew_site_packages)
 
         user_site_packages = Language::Python.user_site_packages "python"
-        <<-EOS.undent
+        <<~EOS
           Your default Python does not recognize the Homebrew site-packages
           directory as a special site-packages directory, which means that .pth
           files will not be followed. This means you will not be able to import
@@ -1051,8 +1063,7 @@ module Homebrew
 
         message = "You have external commands with conflicting names.\n"
         cmd_map.each do |cmd_name, cmd_paths|
-          message += inject_file_list cmd_paths, <<-EOS.undent
-
+          message += inject_file_list cmd_paths, <<~EOS
             Found command `#{cmd_name}` in following places:
           EOS
         end
@@ -1075,7 +1086,7 @@ module Homebrew
         end
         return if bad_tap_files.empty?
         bad_tap_files.keys.map do |tap|
-          <<-EOS.undent
+          <<~EOS
             Found Ruby file outside #{tap} tap formula directory
             (#{tap.formula_dir}):
               #{bad_tap_files[tap].join("\n  ")}
diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb
index e69a56ddfaa352cd9283a0e65244b6ac6b1176db..f9a359450cfd4a962340295004345850883f115f 100644
--- a/Library/Homebrew/download_strategy.rb
+++ b/Library/Homebrew/download_strategy.rb
@@ -159,7 +159,7 @@ class VCSDownloadStrategy < AbstractDownloadStrategy
     return unless @ref_type == :tag
     return unless @revision && current_revision
     return if current_revision == @revision
-    raise <<-EOS.undent
+    raise <<~EOS
       #{@ref} tag should be #{@revision}
       but is actually #{current_revision}
     EOS
@@ -553,9 +553,9 @@ class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy
   rescue GitHub::HTTPNotFoundError
     # We only handle HTTPNotFoundError here,
     # becase AuthenticationFailedError is handled within util/github.
-    message = <<-EOS.undent
-        HOMEBREW_GITHUB_API_TOKEN can not access the repository: #{@owner}/#{@repo}
-        This token may not have permission to access the repository or the url of formula may be incorrect.
+    message = <<~EOS
+      HOMEBREW_GITHUB_API_TOKEN can not access the repository: #{@owner}/#{@repo}
+      This token may not have permission to access the repository or the url of formula may be incorrect.
     EOS
     raise CurlDownloadStrategyError, message
   end
diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb
index 22a7fe0232936d4e7913163cf2cff0bb7525da19..7705f9d49b42f9562e0c41127285c115f1e7f70a 100644
--- a/Library/Homebrew/exceptions.rb
+++ b/Library/Homebrew/exceptions.rb
@@ -185,7 +185,7 @@ class TapFormulaAmbiguityError < RuntimeError
       "#{Tap.fetch(match[:user], match[:repo])}/#{path.basename(".rb")}"
     end
 
-    super <<-EOS.undent
+    super <<~EOS
       Formulae found in multiple taps: #{formulae.map { |f| "\n       * #{f}" }.join}
 
       Please use the fully-qualified name e.g. #{formulae.first} to refer the formula.
@@ -205,7 +205,7 @@ class TapFormulaWithOldnameAmbiguityError < RuntimeError
       "#{Regexp.last_match(1)}/#{Regexp.last_match(2)}"
     end
 
-    super <<-EOS.undent
+    super <<~EOS
       Formulae with '#{name}' old name found in multiple taps: #{taps.map { |t| "\n       * #{t}" }.join}
 
       Please use the fully-qualified name e.g. #{taps.first}/#{name} to refer the formula or use its new name.
@@ -219,7 +219,7 @@ class TapUnavailableError < RuntimeError
   def initialize(name)
     @name = name
 
-    super <<-EOS.undent
+    super <<~EOS
       No available tap #{name}.
     EOS
   end
@@ -235,7 +235,7 @@ class TapRemoteMismatchError < RuntimeError
     @expected_remote = expected_remote
     @actual_remote = actual_remote
 
-    super <<-EOS.undent
+    super <<~EOS
       Tap #{name} remote mismatch.
       #{expected_remote} != #{actual_remote}
     EOS
@@ -248,7 +248,7 @@ class TapAlreadyTappedError < RuntimeError
   def initialize(name)
     @name = name
 
-    super <<-EOS.undent
+    super <<~EOS
       Tap #{name} already tapped.
     EOS
   end
@@ -260,7 +260,7 @@ class TapAlreadyUnshallowError < RuntimeError
   def initialize(name)
     @name = name
 
-    super <<-EOS.undent
+    super <<~EOS
       Tap #{name} already a full clone.
     EOS
   end
@@ -279,7 +279,7 @@ end
 
 class OperationInProgressError < RuntimeError
   def initialize(name)
-    message = <<-EOS.undent
+    message = <<~EOS
       Operation already in progress for #{name}
       Another active Homebrew process is already using #{name}.
       Please wait for it to finish or terminate it to continue.
@@ -327,7 +327,7 @@ class FormulaConflictError < RuntimeError
     message = []
     message << "Cannot install #{formula.full_name} because conflicting formulae are installed."
     message.concat conflicts.map { |c| conflict_message(c) } << ""
-    message << <<-EOS.undent
+    message << <<~EOS
       Please `brew unlink #{conflicts.map(&:name) * " "}` before continuing.
 
       Unlinking removes a formula's symlinks from #{HOMEBREW_PREFIX}. You can
@@ -341,7 +341,7 @@ end
 
 class FormulaAmbiguousPythonError < RuntimeError
   def initialize(formula)
-    super <<-EOS.undent
+    super <<~EOS
       The version of python to use with the virtualenv in the `#{formula.full_name}` formula
       cannot be guessed automatically. If the simultaneous use of python and python3
       is intentional, please add `:using => "python"` or `:using => "python3"` to
@@ -398,18 +398,18 @@ class BuildError < RuntimeError
       if formula.tap.official?
         puts Formatter.error(Formatter.url(OS::ISSUES_URL), label: "READ THIS")
       elsif issues_url = formula.tap.issues_url
-        puts <<-EOS.undent
+        puts <<~EOS
           If reporting this issue please do so at (not Homebrew/brew or Homebrew/core):
           #{Formatter.url(issues_url)}
         EOS
       else
-        puts <<-EOS.undent
+        puts <<~EOS
           If reporting this issue please do so to (not Homebrew/brew or Homebrew/core):
           #{formula.tap}
         EOS
       end
     else
-      puts <<-EOS.undent
+      puts <<~EOS
         Do not report this issue to Homebrew/brew or Homebrew/core!
       EOS
     end
@@ -445,7 +445,7 @@ class BuildToolsError < RuntimeError
       package_text = "a binary package"
     end
 
-    super <<-EOS.undent
+    super <<~EOS
       The following #{formula_text}:
         #{formulae.join(", ")}
       cannot be installed as #{package_text} and must be built from source.
@@ -467,7 +467,7 @@ class BuildFlagsError < RuntimeError
       require_text = "requires"
     end
 
-    super <<-EOS.undent
+    super <<~EOS
       The following #{flag_text}:
         #{flags.join(", ")}
       #{require_text} building tools, but none are installed.
@@ -481,7 +481,7 @@ end
 # the compilers available on the user's system
 class CompilerSelectionError < RuntimeError
   def initialize(formula)
-    super <<-EOS.undent
+    super <<~EOS
       #{formula.full_name} cannot be built with any available compilers.
       #{DevelopmentTools.custom_installation_instructions}
     EOS
@@ -491,7 +491,7 @@ end
 # Raised in Resource.fetch
 class DownloadError < RuntimeError
   def initialize(resource, cause)
-    super <<-EOS.undent
+    super <<~EOS
       Failed to download resource #{resource.download_name.inspect}
       #{cause.message}
       EOS
@@ -530,7 +530,7 @@ class ChecksumMismatchError < RuntimeError
     @expected = expected
     @hash_type = expected.hash_type.to_s.upcase
 
-    super <<-EOS.undent
+    super <<~EOS
       #{@hash_type} mismatch
       Expected: #{expected}
       Actual: #{actual}
@@ -557,7 +557,7 @@ class MissingApplyError < RuntimeError; end
 
 class BottleFormulaUnavailableError < RuntimeError
   def initialize(bottle_path, formula_path)
-    super <<-EOS.undent
+    super <<~EOS
       This bottle does not contain the formula file:
         #{bottle_path}
         #{formula_path}
diff --git a/Library/Homebrew/extend/ARGV.rb b/Library/Homebrew/extend/ARGV.rb
index 63a0f3e40b07f7f732e2ef2d08aeac70dcb6de35..d9da014f05d2e20011f12e3675fe580eb2234a29 100644
--- a/Library/Homebrew/extend/ARGV.rb
+++ b/Library/Homebrew/extend/ARGV.rb
@@ -124,7 +124,7 @@ module HomebrewArgvExtension
           Keg.new(prefix)
         end
       rescue FormulaUnavailableError
-        raise <<-EOS.undent
+        raise <<~EOS
           Multiple kegs installed to #{rack}
           However we don't know which one you refer to.
           Please delete (with rm -rf!) all but one and then try again.
diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb
index 15488ee191313f5407d5bb9b8166114857c7a55d..5a6b779d0e1e761a29af1de538816b0a14a4bb19 100644
--- a/Library/Homebrew/extend/ENV/shared.rb
+++ b/Library/Homebrew/extend/ENV/shared.rb
@@ -233,7 +233,7 @@ module SharedEnvExtension
       if ARGV.include? "--default-fortran-flags"
         flags = FC_FLAG_VARS.reject { |key| self[key] }
       elsif values_at(*FC_FLAG_VARS).compact.empty?
-        opoo <<-EOS.undent
+        opoo <<~EOS
           No Fortran optimization information was provided.  You may want to consider
           setting FCFLAGS and FFLAGS or pass the `--default-fortran-flags` option to
           `brew install` if your compiler is compatible with GCC.
@@ -286,15 +286,15 @@ module SharedEnvExtension
     begin
       gcc_formula = gcc_version_formula(name)
     rescue FormulaUnavailableError => e
-      raise <<-EOS.undent
-      Homebrew GCC requested, but formula #{e.name} not found!
+      raise <<~EOS
+        Homebrew GCC requested, but formula #{e.name} not found!
       EOS
     end
 
     return if gcc_formula.opt_prefix.exist?
-    raise <<-EOS.undent
-    The requested Homebrew GCC was not installed. You must:
-      brew install #{gcc_formula.full_name}
+    raise <<~EOS
+      The requested Homebrew GCC was not installed. You must:
+        brew install #{gcc_formula.full_name}
     EOS
   end
 
diff --git a/Library/Homebrew/extend/git_repository.rb b/Library/Homebrew/extend/git_repository.rb
index c15988550fa6143e5e06619fe32ecf2b01930abd..6b89d175cadff067d83d888b8dbe04bc99af5aea 100644
--- a/Library/Homebrew/extend/git_repository.rb
+++ b/Library/Homebrew/extend/git_repository.rb
@@ -36,6 +36,15 @@ module GitRepositoryExtension
     end
   end
 
+  def git_branch
+    return unless git? && Utils.git_available?
+    cd do
+      Utils.popen_read(
+        "git", "rev-parse", "--abbrev-ref", "HEAD"
+      ).chuzzle
+    end
+  end
+
   def git_last_commit_date
     return unless git? && Utils.git_available?
     cd do
diff --git a/Library/Homebrew/extend/os/caveats.rb b/Library/Homebrew/extend/os/caveats.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e67138087b4cb662ed24c45f43e48105a7b47719
--- /dev/null
+++ b/Library/Homebrew/extend/os/caveats.rb
@@ -0,0 +1 @@
+require "extend/os/mac/caveats" if OS.mac?
diff --git a/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb
index 3fd847bc4b6a854afade62dc60414e03c84b60d3..3553ada7426d7fcdd98f3bb4febb733f32a726a1 100644
--- a/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb
+++ b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb
@@ -18,7 +18,7 @@ class OsxfuseRequirement < Requirement
   def message
     msg = "libfuse is required to install this formula.\n"
     if libfuse_formula_exists?
-      msg + <<-EOS.undent
+      msg + <<~EOS
         Run "brew install libfuse" to install it.
       EOS
     else
diff --git a/Library/Homebrew/extend/os/mac/caveats.rb b/Library/Homebrew/extend/os/mac/caveats.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d912a83075a77cbf8f4ebe6d86d8c9be7afea25b
--- /dev/null
+++ b/Library/Homebrew/extend/os/mac/caveats.rb
@@ -0,0 +1,45 @@
+class Caveats
+  def plist_caveats
+    s = []
+    if f.plist || (keg&.plist_installed?)
+      plist_domain = f.plist_path.basename(".plist")
+
+      # we readlink because this path probably doesn't exist since caveats
+      # occurs before the link step of installation
+      # Yosemite security measures mildly tighter rules:
+      # https://github.com/Homebrew/legacy-homebrew/issues/33815
+      if !plist_path.file? || !plist_path.symlink?
+        if f.plist_startup
+          s << "To have launchd start #{f.full_name} now and restart at startup:"
+          s << "  sudo brew services start #{f.full_name}"
+        else
+          s << "To have launchd start #{f.full_name} now and restart at login:"
+          s << "  brew services start #{f.full_name}"
+        end
+      # For startup plists, we cannot tell whether it's running on launchd,
+      # as it requires for `sudo launchctl list` to get real result.
+      elsif f.plist_startup
+        s << "To restart #{f.full_name} after an upgrade:"
+        s << "  sudo brew services restart #{f.full_name}"
+      elsif Kernel.system "/bin/launchctl list #{plist_domain} &>/dev/null"
+        s << "To restart #{f.full_name} after an upgrade:"
+        s << "  brew services restart #{f.full_name}"
+      else
+        s << "To start #{f.full_name}:"
+        s << "  brew services start #{f.full_name}"
+      end
+
+      if f.plist_manual
+        s << "Or, if you don't want/need a background service you can just run:"
+        s << "  #{f.plist_manual}"
+      end
+
+      # pbpaste is the system clipboard tool on macOS and fails with `tmux` by default
+      # check if this is being run under `tmux` to avoid failing
+      if ENV["TMUX"] && !quiet_system("/usr/bin/pbpaste")
+        s << "" << "WARNING: brew services will fail when run under tmux."
+      end
+    end
+    s.join("\n") + "\n" unless s.empty?
+  end
+end
diff --git a/Library/Homebrew/extend/os/mac/development_tools.rb b/Library/Homebrew/extend/os/mac/development_tools.rb
index 1931b398d92c036cbdcbd47d235232c96c43ac8e..d37e778ca53f3e2cd07b1b3a1947b6ba3658bf8c 100644
--- a/Library/Homebrew/extend/os/mac/development_tools.rb
+++ b/Library/Homebrew/extend/os/mac/development_tools.rb
@@ -24,18 +24,18 @@ class DevelopmentTools
 
     def installation_instructions
       if MacOS.version >= "10.9"
-        <<-EOS.undent
+        <<~EOS
           Install the Command Line Tools:
             xcode-select --install
         EOS
       elsif MacOS.version == "10.8" || MacOS.version == "10.7"
-        <<-EOS.undent
+        <<~EOS
           Install the Command Line Tools from
             https://developer.apple.com/download/more/
           or via Xcode's preferences.
         EOS
       else
-        <<-EOS.undent
+        <<~EOS
           Install Xcode from
             https://developer.apple.com/download/more/
         EOS
@@ -44,19 +44,19 @@ class DevelopmentTools
 
     def custom_installation_instructions
       if MacOS.version > :leopard
-        <<-EOS.undent
+        <<~EOS
           Install GNU's GCC
             brew install gcc
         EOS
       elsif MacOS.version > :tiger
-        <<-EOS.undent
+        <<~EOS
           Install GNU's GCC
             brew install gcc@4.6
         EOS
       else
         # Tiger doesn't ship with apple-gcc42, and this is required to build
         # some software that doesn't build properly with FSF GCC.
-        <<-EOS.undent
+        <<~EOS
           Install Apple's GCC
             brew install apple-gcc42
           or GNU's GCC
diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb
index 9f7b18b492ae74cea41a6b1580da385cc285c03a..be0ce93510c4521148b4e7922afd4c628c93b51b 100644
--- a/Library/Homebrew/extend/os/mac/diagnostic.rb
+++ b/Library/Homebrew/extend/os/mac/diagnostic.rb
@@ -41,7 +41,7 @@ module Homebrew
           return
         end
 
-        <<-EOS.undent
+        <<~EOS
           You are using macOS #{MacOS.version}.
           #{who} do not provide support for this #{what}.
           You may encounter build failures or other breakages.
@@ -59,7 +59,7 @@ module Homebrew
         # Homebrew/brew is currently using.
         return if ENV["TRAVIS"]
 
-        message = <<-EOS.undent
+        message = <<~EOS
           Your Xcode (#{MacOS::Xcode.version}) is outdated.
           Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
           #{MacOS::Xcode.update_instructions}
@@ -67,7 +67,7 @@ module Homebrew
 
         if OS::Mac.prerelease?
           current_path = Utils.popen_read("/usr/bin/xcode-select", "-p")
-          message += <<-EOS.undent
+          message += <<~EOS
             If #{MacOS::Xcode.latest_version} is installed, you may need to:
               sudo xcode-select --switch /Applications/Xcode.app
             Current developer directory is:
@@ -87,7 +87,7 @@ module Homebrew
         # Homebrew/brew is currently using.
         return if ENV["TRAVIS"]
 
-        <<-EOS.undent
+        <<~EOS
           A newer Command Line Tools release is available.
           #{MacOS::CLT.update_instructions}
         EOS
@@ -99,7 +99,7 @@ module Homebrew
         return unless MacOS.version == :el_capitan
         return unless MacOS::Xcode.version >= "8"
 
-        <<-EOS.undent
+        <<~EOS
           You have Xcode 8 installed without the CLT;
           this causes certain builds to fail on OS X El Capitan (10.11).
           Please install the CLT via:
@@ -111,7 +111,7 @@ module Homebrew
         return unless MacOS::Xcode.installed?
         return unless MacOS::Xcode.below_minimum_version?
 
-        <<-EOS.undent
+        <<~EOS
           Your Xcode (#{MacOS::Xcode.version}) is too outdated.
           Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
           #{MacOS::Xcode.update_instructions}
@@ -122,7 +122,7 @@ module Homebrew
         return unless MacOS::CLT.installed?
         return unless MacOS::CLT.below_minimum_version?
 
-        <<-EOS.undent
+        <<~EOS
           Your Command Line Tools are too outdated.
           #{MacOS::CLT.update_instructions}
         EOS
@@ -140,7 +140,7 @@ module Homebrew
           "Please install Xcode #{MacOS::Xcode.latest_version}."
         end
 
-        <<-EOS.undent
+        <<~EOS
           You seem to have osx-gcc-installer installed.
           Homebrew doesn't support osx-gcc-installer. It causes many builds to fail and
           is an unlicensed distribution of really old Xcode files.
@@ -154,7 +154,7 @@ module Homebrew
         uninstaller = Pathname.new("/Developer/Library/uninstall-developer-folder")
         return unless ((MacOS::Xcode.version || "0") >= "4.3") && uninstaller.exist?
 
-        <<-EOS.undent
+        <<~EOS
           You have leftover files from an older version of Xcode.
           You should delete them using:
             #{uninstaller}
@@ -170,7 +170,7 @@ module Homebrew
         return if libs.empty?
         return if libs.include? "/usr/lib/libxcselect.dylib"
 
-        <<-EOS.undent
+        <<~EOS
           You have an outdated version of /usr/bin/install_name_tool installed.
           This will cause binary package installations to fail.
           This can happen if you install osx-gcc-installer or RailsInstaller.
@@ -183,7 +183,7 @@ module Homebrew
         ponk = MacOS.macports_or_fink
         return if ponk.empty?
 
-        <<-EOS.undent
+        <<~EOS
           You have MacPorts or Fink installed:
             #{ponk.join(", ")}
 
@@ -199,7 +199,7 @@ module Homebrew
         return if RUBY_VERSION == ruby_version
         return if ARGV.homebrew_developer? && OS::Mac.prerelease?
 
-        <<-EOS.undent
+        <<~EOS
           Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew
           is developed and tested on Ruby #{ruby_version}, and may not work correctly
           on other Rubies. Patches are accepted as long as they don't cause breakage
@@ -212,7 +212,7 @@ module Homebrew
         return if prefix.nil?
         return unless prefix.to_s.include?(" ")
 
-        <<-EOS.undent
+        <<~EOS
           Xcode is installed to a directory with a space in the name.
           This will cause some formulae to fail to build.
         EOS
@@ -222,7 +222,7 @@ module Homebrew
         prefix = MacOS::Xcode.prefix
         return if prefix.nil? || prefix.exist?
 
-        <<-EOS.undent
+        <<~EOS
           The directory Xcode is reportedly installed to doesn't exist:
             #{prefix}
           You may need to `xcode-select` the proper path if you have moved Xcode.
@@ -236,7 +236,7 @@ module Homebrew
 
         path = MacOS::Xcode.bundle_path
         path = "/Developer" if path.nil? || !path.directory?
-        <<-EOS.undent
+        <<~EOS
           Your Xcode is configured with an invalid path.
           You should change it to the correct path:
             sudo xcode-select -switch #{path}
@@ -247,7 +247,7 @@ module Homebrew
         return unless MacOS.version <= "10.8"
         return if Formula["curl"].installed?
 
-        <<-EOS.undent
+        <<~EOS
           The system curl on 10.8 and below is often incapable of supporting
           modern secure connections & will fail on fetching formulae.
 
@@ -261,7 +261,7 @@ module Homebrew
         return unless MacOS.version >= :yosemite
         return if ENV["SSL_CERT_DIR"].nil?
 
-        <<-EOS.undent
+        <<~EOS
           SSL_CERT_DIR support was removed from Apple's curl.
           If fetching formulae fails you should:
             unset SSL_CERT_DIR
@@ -274,7 +274,7 @@ module Homebrew
         # license or no "xc*" tool will work.
         return unless `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$CHILD_STATUS.success?
 
-        <<-EOS.undent
+        <<~EOS
           You have not agreed to the Xcode license.
           Builds will fail! Agree to the license by opening Xcode.app or running:
             sudo xcodebuild -license
@@ -289,7 +289,7 @@ module Homebrew
         latest_version = Version.create(MacOS::XQuartz.latest_version)
         return if installed_version >= latest_version
 
-        <<-EOS.undent
+        <<~EOS
           Your XQuartz (#{installed_version}) is outdated.
           Please install XQuartz #{latest_version} (or delete the current version).
           XQuartz can be updated using Homebrew-Cask by running
@@ -301,7 +301,7 @@ module Homebrew
         return unless MacOS::XQuartz.version
         return unless MacOS::XQuartz.version.include? "beta"
 
-        <<-EOS.undent
+        <<~EOS
           The following beta release of XQuartz is installed: #{MacOS::XQuartz.version}
 
           XQuartz beta releases include address sanitization, and do not work with
@@ -337,7 +337,7 @@ module Homebrew
         end
         case_sensitive_vols.uniq!
 
-        <<-EOS.undent
+        <<~EOS
           The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive.
           The default macOS filesystem is case-insensitive. Please report any apparent problems.
         EOS
@@ -346,7 +346,7 @@ module Homebrew
       def check_homebrew_prefix
         return if HOMEBREW_PREFIX.to_s == "/usr/local"
 
-        <<-EOS.undent
+        <<~EOS
           Your Homebrew's prefix is not /usr/local.
           You can install Homebrew anywhere you want but some bottles (binary packages)
           can only be used with a /usr/local prefix and some formulae (packages)
@@ -360,7 +360,7 @@ module Homebrew
 
         mono_config = Pathname.new("/usr/bin/pkg-config")
         if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework")
-          <<-EOS.undent
+          <<~EOS
             You have a non-Homebrew 'pkg-config' in your PATH:
               /usr/bin/pkg-config => #{mono_config.realpath}
 
@@ -371,7 +371,7 @@ module Homebrew
             `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono.
           EOS
         elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config"
-          <<-EOS.undent
+          <<~EOS
             You have a non-Homebrew 'pkg-config' in your PATH:
               #{binary}
 
diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb
index 32e5774f6932f26e674ab051bc563a4ee187dd51..901d8945fe3a3178716cb9c78474533a43f8aba9 100644
--- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb
+++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb
@@ -19,7 +19,7 @@ module FormulaCellarChecks
 
     return if files.empty?
 
-    <<-EOS.undent
+    <<~EOS
       Header files that shadow system header files were installed to "#{formula.include}"
       The offending files are:
         #{files * "\n        "}
@@ -35,7 +35,7 @@ module FormulaCellarChecks
     end
     return if system_openssl.empty?
 
-    <<-EOS.undent
+    <<~EOS
       object files were linked against system openssl
       These object files were linked against the deprecated system OpenSSL or
       the system's private LibreSSL.
@@ -52,7 +52,7 @@ module FormulaCellarChecks
     end
     return if framework_links.empty?
 
-    <<-EOS.undent
+    <<~EOS
       python modules have explicit framework links
       These python extension modules were linked directly to a Python
       framework binary. They should be linked with -undefined dynamic_lookup
@@ -67,13 +67,13 @@ module FormulaCellarChecks
     checker = LinkageChecker.new(keg, formula)
 
     return unless checker.broken_dylibs?
-    output = <<-EOS.undent
+    output = <<~EOS
       #{formula} has broken dynamic library links:
         #{checker.broken_dylibs.to_a * "\n  "}
     EOS
     tab = Tab.for_keg(keg)
     if tab.poured_from_bottle
-      output += <<-EOS.undent
+      output += <<~EOS
         Rebuild this from source with:
           brew reinstall --build-from-source #{formula}
         If that's successful, file an issue#{formula.tap ? " here:\n  #{formula.tap.issues_url}" : "."}
diff --git a/Library/Homebrew/extend/os/mac/missing_formula.rb b/Library/Homebrew/extend/os/mac/missing_formula.rb
index bcf0eb25d3f9d8f5790fdd577888acf91c5b43ff..ea6d50112a72f418e3b7f8cd8096f9184b6c269d 100644
--- a/Library/Homebrew/extend/os/mac/missing_formula.rb
+++ b/Library/Homebrew/extend/os/mac/missing_formula.rb
@@ -5,12 +5,12 @@ module Homebrew
         case name.downcase
         when "xcode"
           if MacOS.version >= :lion
-            <<-EOS.undent
-            Xcode can be installed from the App Store.
+            <<~EOS
+              Xcode can be installed from the App Store.
             EOS
           else
-            <<-EOS.undent
-            Xcode can be installed from #{Formatter.url("https://developer.apple.com/download/more/")}.
+            <<~EOS
+              Xcode can be installed from #{Formatter.url("https://developer.apple.com/download/more/")}.
             EOS
           end
         else
diff --git a/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb
index 8c898a2726bc068a48c641d6147234088c06b2d5..3f10a951acb3c2cd95835c9d4d5d011806385d70 100644
--- a/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb
+++ b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb
@@ -26,7 +26,7 @@ class NonBinaryOsxfuseRequirement < Requirement
   satisfy(build_env: false) { HOMEBREW_PREFIX.to_s != "/usr/local" || !OsxfuseRequirement.binary_osxfuse_installed? }
 
   def message
-    <<-EOS.undent
+    <<~EOS
       osxfuse is already installed from the binary distribution and
       conflicts with this formula.
     EOS
diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb
index e3d6880ba6c38b8c556d3c426487dada222aaadb..b4c7ca95950545355c777d6f02587499a24548bb 100644
--- a/Library/Homebrew/extend/pathname.rb
+++ b/Library/Homebrew/extend/pathname.rb
@@ -403,7 +403,7 @@ class Pathname
     mkpath
     targets.each do |target|
       target = Pathname.new(target) # allow pathnames or strings
-      join(target.basename).write <<-EOS.undent
+      join(target.basename).write <<~EOS
         #!/bin/bash
         exec "#{target}" "$@"
       EOS
@@ -415,9 +415,9 @@ class Pathname
     env_export = ""
     env.each { |key, value| env_export += "#{key}=\"#{value}\" " }
     dirname.mkpath
-    write <<-EOS.undent
-    #!/bin/bash
-    #{env_export}exec "#{target}" "$@"
+    write <<~EOS
+      #!/bin/bash
+      #{env_export}exec "#{target}" "$@"
     EOS
   end
 
@@ -435,7 +435,7 @@ class Pathname
   # Writes an exec script that invokes a java jar
   def write_jar_script(target_jar, script_name, java_opts = "")
     mkpath
-    join(script_name).write <<-EOS.undent
+    join(script_name).write <<~EOS
       #!/bin/bash
       exec java #{java_opts} -jar #{target_jar} "$@"
     EOS
diff --git a/Library/Homebrew/extend/string.rb b/Library/Homebrew/extend/string.rb
index b96f12994995d5843416599ff2370b8868fd1e11..c94bfc83459be89589e9bfde60d09fdef60a968d 100644
--- a/Library/Homebrew/extend/string.rb
+++ b/Library/Homebrew/extend/string.rb
@@ -2,23 +2,6 @@
 require_relative "../vendor/backports/string"
 
 class String
-  def undent
-    gsub(/^[ \t]{#{(slice(/^[ \t]+/) || '').length}}/, "")
-  end
-  alias unindent undent
-
-  # eg:
-  #   if foo then <<-EOS.undent_________________________________________________________72
-  #               Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
-  #               eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
-  #               minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
-  #               ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
-  #               voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
-  #               sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
-  #               mollit anim id est laborum.
-  #               EOS
-  alias undent_________________________________________________________72 undent
-
   # String.chomp, but if result is empty: returns nil instead.
   # Allows `chuzzle || foo` short-circuits.
   def chuzzle
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
index 61042aae76389087042e88b56a4426dbb540f3ae..1765f20c1404dc371a4217cb24b0676d275c684a 100644
--- a/Library/Homebrew/formula.rb
+++ b/Library/Homebrew/formula.rb
@@ -839,7 +839,7 @@ class Formula
   # This method can be overridden to provide a plist.
   # For more examples read Apple's handy manpage:
   # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man5/plist.5.html
-  # <pre>def plist; <<-EOS.undent
+  # <pre>def plist; <<~EOS
   #  <?xml version="1.0" encoding="UTF-8"?>
   #  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   #  <plist version="1.0">
@@ -986,13 +986,13 @@ class Formula
   # to installation through a different package manager on a different OS.
   # @return [String]
   # <pre>def caveats
-  #   <<-EOS.undent
+  #   <<~EOS
   #     Are optional. Something the user should know?
   #   EOS
   # end</pre>
   #
   # <pre>def caveats
-  #   s = <<-EOS.undent
+  #   s = <<~EOS
   #     Print some important notice to the user when `brew info <formula>` is
   #     called or when brewing a formula.
   #     This is optional. You can use all the vars like #{version} here.
@@ -1691,7 +1691,7 @@ class Formula
     # keep Homebrew's site-packages in sys.path when using system Python
     user_site_packages = home/"Library/Python/2.7/lib/python/site-packages"
     user_site_packages.mkpath
-    (user_site_packages/"homebrew.pth").write <<-EOS.undent
+    (user_site_packages/"homebrew.pth").write <<~EOS
       import site; site.addsitedir("#{HOMEBREW_PREFIX}/lib/python2.7/site-packages")
       import sys, os; sys.path = (os.environ["PYTHONPATH"].split(os.pathsep) if "PYTHONPATH" in os.environ else []) + ["#{HOMEBREW_PREFIX}/lib/python2.7/site-packages"] + sys.path
     EOS
@@ -2394,7 +2394,7 @@ class Formula
     # and building the software was ok.
     # <pre>system bin/"foobar", "--version"</pre>
     #
-    # <pre>(testpath/"test.file").write <<-EOS.undent
+    # <pre>(testpath/"test.file").write <<~EOS
     #   writing some test file, if you need to
     # EOS
     # assert_equal "OK", shell_output("test_command test.file").strip</pre>
diff --git a/Library/Homebrew/formula_cellar_checks.rb b/Library/Homebrew/formula_cellar_checks.rb
index 6c433e420d27aa0ae436089372a8c84b97f809df..471a5ea2fe9749fd95da2084aba6210e68e14531 100644
--- a/Library/Homebrew/formula_cellar_checks.rb
+++ b/Library/Homebrew/formula_cellar_checks.rb
@@ -12,7 +12,7 @@ module FormulaCellarChecks
     prefix_bin = prefix_bin.realpath
     return if ORIGINAL_PATHS.include? prefix_bin
 
-    <<-EOS.undent
+    <<~EOS
       #{prefix_bin} is not in your PATH
       You can amend this by altering your #{Utils::Shell.profile} file
     EOS
@@ -22,7 +22,7 @@ module FormulaCellarChecks
     # Check for man pages that aren't in share/man
     return unless (formula.prefix/"man").directory?
 
-    <<-EOS.undent
+    <<~EOS
       A top-level "man" directory was found
       Homebrew requires that man pages live under share.
       This can often be fixed by passing "--mandir=\#{man}" to configure.
@@ -33,7 +33,7 @@ module FormulaCellarChecks
     # Check for info pages that aren't in share/info
     return unless (formula.prefix/"info").directory?
 
-    <<-EOS.undent
+    <<~EOS
       A top-level "info" directory was found
       Homebrew suggests that info pages live under share.
       This can often be fixed by passing "--infodir=\#{info}" to configure.
@@ -45,7 +45,7 @@ module FormulaCellarChecks
     jars = formula.lib.children.select { |g| g.extname == ".jar" }
     return if jars.empty?
 
-    <<-EOS.undent
+    <<~EOS
       JARs were installed to "#{formula.lib}"
       Installing JARs to "lib" can cause conflicts between packages.
       For Java software, it is typically better for the formula to
@@ -67,7 +67,7 @@ module FormulaCellarChecks
     end
     return if non_libraries.empty?
 
-    <<-EOS.undent
+    <<~EOS
       Non-libraries were installed to "#{formula.lib}"
       Installing non-libraries to "lib" is discouraged.
       The offending files are:
@@ -81,7 +81,7 @@ module FormulaCellarChecks
     non_exes = bin.children.select { |g| g.directory? || !g.executable? }
     return if non_exes.empty?
 
-    <<-EOS.undent
+    <<~EOS
       Non-executables were installed to "#{bin}"
       The offending files are:
         #{non_exes * "\n        "}
@@ -94,7 +94,7 @@ module FormulaCellarChecks
     generics = bin.children.select { |g| generic_names.include? g.basename.to_s }
     return if generics.empty?
 
-    <<-EOS.undent
+    <<~EOS
       Generic binaries were installed to "#{bin}"
       Binaries with generic names are likely to conflict with other software,
       and suggest that this software should be installed to "libexec" and then
@@ -109,7 +109,7 @@ module FormulaCellarChecks
     pth_found = Dir["#{lib}/python{2.7,3}*/site-packages/easy-install.pth"].map { |f| File.dirname(f) }
     return if pth_found.empty?
 
-    <<-EOS.undent
+    <<~EOS
       easy-install.pth files were found
       These .pth files are likely to cause link conflicts. Please invoke
       setup.py using Language::Python.setup_install_args.
@@ -128,7 +128,7 @@ module FormulaCellarChecks
     end
 
     return unless bad_dir_name
-    <<-EOS
+    <<~EOS
       Emacs Lisp files were installed into the wrong site-lisp subdirectory.
       They should be installed into:
       #{share}/emacs/site-lisp/#{name}
@@ -142,7 +142,7 @@ module FormulaCellarChecks
 
     elisps = (share/"emacs/site-lisp").children.select { |file| %w[.el .elc].include? file.extname }
     return if elisps.empty?
-    <<-EOS.undent
+    <<~EOS
       Emacs Lisp files were linked directly to #{HOMEBREW_PREFIX}/share/emacs/site-lisp
       This may cause conflicts with other packages.
       They should instead be installed into:
diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb
index 7cd87e7513bcd0dbbc697c53bb52461a8f8772a3..42abe51718751a938d0bb7ac2b2ac24b4760477d 100644
--- a/Library/Homebrew/formula_installer.rb
+++ b/Library/Homebrew/formula_installer.rb
@@ -93,7 +93,7 @@ class FormulaInstaller
     return false if formula.bottle_disabled?
     unless formula.pour_bottle?
       if install_bottle_options[:warn] && formula.pour_bottle_check_unsatisfied_reason
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Building #{formula.full_name} from source:
             #{formula.pour_bottle_check_unsatisfied_reason}
         EOS
@@ -104,7 +104,7 @@ class FormulaInstaller
     bottle = formula.bottle_specification
     unless bottle.compatible_cellar?
       if install_bottle_options[:warn]
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Building #{formula.full_name} from source:
             The bottle needs a #{bottle.cellar} Cellar (yours is #{HOMEBREW_CELLAR}).
         EOS
@@ -165,14 +165,14 @@ class FormulaInstaller
     end
 
     unless recursive_dependencies.empty?
-      raise CannotInstallFormulaError, <<-EOS.undent
+      raise CannotInstallFormulaError, <<~EOS
         #{formula.full_name} contains a recursive dependency on itself:
           #{recursive_dependencies.join("\n  ")}
       EOS
     end
 
     if recursive_formulae.flat_map(&:recursive_dependencies).map(&:to_s).include?(formula.name)
-      raise CannotInstallFormulaError, <<-EOS.undent
+      raise CannotInstallFormulaError, <<~EOS
         #{formula.full_name} contains a recursive dependency on itself!
       EOS
     end
@@ -188,7 +188,7 @@ class FormulaInstaller
       version_conflicts += version_hash[unversioned_name]
     end
     unless version_conflicts.empty?
-      raise CannotInstallFormulaError, <<-EOS.undent
+      raise CannotInstallFormulaError, <<~EOS
         #{formula.full_name} contains conflicting version recursive dependencies:
           #{version_conflicts.to_a.join ", "}
         View these with `brew deps --tree #{formula.full_name}`.
@@ -221,16 +221,16 @@ class FormulaInstaller
     # function but after instantiating this class so that it can avoid having to
     # relink the active keg if possible (because it is slow).
     if formula.linked_keg.directory?
-      message = <<-EOS.undent
+      message = <<~EOS
         #{formula.name} #{formula.linked_version} is already installed
       EOS
       message += if formula.outdated? && !formula.head?
-        <<-EOS.undent
+        <<~EOS
           To upgrade to #{formula.pkg_version}, run `brew upgrade #{formula.name}`
         EOS
       else
         # some other version is already installed *and* linked
-        <<-EOS.undent
+        <<~EOS
           To install #{formula.pkg_version}, first run `brew unlink #{formula.name}`
         EOS
       end
@@ -355,7 +355,7 @@ class FormulaInstaller
       rescue FormulaUnavailableError => e
         # If the formula name doesn't exist any more then complain but don't
         # stop installation from continuing.
-        opoo <<-EOS.undent
+        opoo <<~EOS
           #{formula}: #{e.message}
           'conflicts_with \"#{c.name}\"' should be removed from #{formula.path.basename}.
         EOS
diff --git a/Library/Homebrew/formula_support.rb b/Library/Homebrew/formula_support.rb
index 2c9c19187a8760db2de96e89139d8ab8b978478a..53fd61db8e75738bf92bd89c112ade2541eb4ac7 100644
--- a/Library/Homebrew/formula_support.rb
+++ b/Library/Homebrew/formula_support.rb
@@ -33,33 +33,33 @@ class KegOnlyReason
   def to_s
     return @explanation unless @explanation.empty?
     case @reason
-    when :versioned_formula then <<-EOS.undent
+    when :versioned_formula then <<~EOS
       this is an alternate version of another formula
     EOS
-    when :provided_by_macos, :provided_by_osx then <<-EOS.undent
+    when :provided_by_macos, :provided_by_osx then <<~EOS
       macOS already provides this software and installing another version in
       parallel can cause all kinds of trouble
     EOS
-    when :shadowed_by_macos, :shadowed_by_osx then <<-EOS.undent
+    when :shadowed_by_macos, :shadowed_by_osx then <<~EOS
       macOS provides similar software and installing this software in
       parallel can cause all kinds of trouble
     EOS
-    when :provided_pre_mountain_lion then <<-EOS.undent
+    when :provided_pre_mountain_lion then <<~EOS
       macOS already provides this software in versions before Mountain Lion
     EOS
-    when :provided_pre_mavericks then <<-EOS.undent
+    when :provided_pre_mavericks then <<~EOS
       macOS already provides this software in versions before Mavericks
     EOS
-    when :provided_pre_el_capitan then <<-EOS.undent
+    when :provided_pre_el_capitan then <<~EOS
       macOS already provides this software in versions before El Capitan
     EOS
-    when :provided_pre_high_sierra then <<-EOS.undent
+    when :provided_pre_high_sierra then <<~EOS
       macOS already provides this software in versions before High Sierra
     EOS
-    when :provided_until_xcode43 then <<-EOS.undent
+    when :provided_until_xcode43 then <<~EOS
       Xcode provides this software prior to version 4.3
     EOS
-    when :provided_until_xcode5 then <<-EOS.undent
+    when :provided_until_xcode5 then <<~EOS
       Xcode provides this software prior to version 5
     EOS
     else
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb
index fddeac6312f9114012dcb714c18cea33aa2f4a78..7eb85ed97d05d6180e91a56d196c447b5834883d 100644
--- a/Library/Homebrew/formulary.rb
+++ b/Library/Homebrew/formulary.rb
@@ -128,7 +128,7 @@ module Formulary
       formula = begin
         Formulary.from_contents name, @bottle_filename, contents, spec
       rescue FormulaUnreadableError => e
-        opoo <<-EOS.undent
+        opoo <<~EOS
           Unreadable formula in #{@bottle_filename}:
           #{e}
         EOS
@@ -443,7 +443,7 @@ module Formulary
     if possible_pinned_tap_formulae.size == 1
       selected_formula = factory(possible_pinned_tap_formulae.first, spec)
       if core_path(ref).file?
-        opoo <<-EOS.undent
+        opoo <<~EOS
           #{ref} is provided by core, but is now shadowed by #{selected_formula.full_name}.
           To refer to the core formula, use Homebrew/core/#{ref} instead.
         EOS
diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb
index de2089ddada17c1579c8f17da3c1bf0f558352b6..ec4e62450801867ce93a3c17d22912eaf2ab4610 100644
--- a/Library/Homebrew/gpg.rb
+++ b/Library/Homebrew/gpg.rb
@@ -32,7 +32,7 @@ class Gpg
   def self.create_test_key(path)
     odie "No GPG present to test against!" unless available?
 
-    (path/"batch.gpg").write <<-EOS.undent
+    (path/"batch.gpg").write <<~EOS
       Key-Type: RSA
       Key-Length: 2048
       Subkey-Type: RSA
diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb
index 677a97c85b795ec704e5f011e2e46a325b13cffa..bb9778c8122540cfc79196bb9d5665c9dfcf32ce 100644
--- a/Library/Homebrew/keg.rb
+++ b/Library/Homebrew/keg.rb
@@ -6,7 +6,7 @@ require "ostruct"
 class Keg
   class AlreadyLinkedError < RuntimeError
     def initialize(keg)
-      super <<-EOS.undent
+      super <<~EOS
         Cannot link #{keg.name}
         Another version is already linked: #{keg.linked_keg_record.resolved_path}
         EOS
@@ -32,9 +32,9 @@ class Keg
     rescue NotAKegError, Errno::ENOENT
       "already exists. You may want to remove it:\n  rm '#{dst}'\n"
     else
-      <<-EOS.undent
-      is a symlink belonging to #{conflict.name}. You can unlink it:
-        brew unlink #{conflict.name}
+      <<~EOS
+        is a symlink belonging to #{conflict.name}. You can unlink it:
+          brew unlink #{conflict.name}
       EOS
     end
 
@@ -42,7 +42,7 @@ class Keg
       s = []
       s << "Could not symlink #{src}"
       s << "Target #{dst}" << suggestion
-      s << <<-EOS.undent
+      s << <<~EOS
         To force the link and overwrite all conflicting files:
           brew link --overwrite #{keg.name}
 
@@ -54,7 +54,7 @@ class Keg
   end
 
   class DirectoryNotWritableError < LinkError
-    def to_s; <<-EOS.undent
+    def to_s; <<~EOS
       Could not symlink #{src}
       #{dst.dirname} is not writable.
       EOS
diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb
index bfec556d03a4a1b7752cdc9da9a6c881076eadfe..931cc59fca55abd4e5b52975876180eaa37c8a4d 100644
--- a/Library/Homebrew/language/python.rb
+++ b/Library/Homebrew/language/python.rb
@@ -46,7 +46,7 @@ module Language
     end
 
     def self.in_sys_path?(python, path)
-      script = <<-EOS.undent
+      script = <<~EOS
         import os, sys
         [os.path.realpath(p) for p in sys.path].index(os.path.realpath("#{path}"))
       EOS
@@ -54,7 +54,7 @@ module Language
     end
 
     def self.setup_install_args(prefix)
-      shim = <<-EOS.undent
+      shim = <<~EOS
         import setuptools, tokenize
         __file__ = 'setup.py'
         exec(compile(getattr(tokenize, 'open', open)(__file__).read()
diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb
index 0eb7492dfd581b745c60e75d7bc9b39e0a866e9e..8664d474bee21129f9fcfb898bb9be6570cbbaa4 100644
--- a/Library/Homebrew/migrator.rb
+++ b/Library/Homebrew/migrator.rb
@@ -6,7 +6,7 @@ require "tab"
 class Migrator
   class MigrationNeededError < RuntimeError
     def initialize(formula)
-      super <<-EOS.undent
+      super <<~EOS
         #{formula.oldname} was renamed to #{formula.name} and needs to be migrated.
         Please run `brew migrate #{formula.oldname}`
       EOS
@@ -33,10 +33,9 @@ class Migrator
         "Please try to use fully-qualified #{tap}/#{formula.oldname} to refer the formula.\n"
       end
 
-      super <<-EOS.undent
-      #{formula.name} from #{formula.tap} is given, but old name #{formula.oldname} was installed from #{tap ? tap : "path or url"}.
-
-      #{msg}To force migrate use `brew migrate --force #{formula.oldname}`.
+      super <<~EOS
+        #{formula.name} from #{formula.tap} is given, but old name #{formula.oldname} was installed from #{tap ? tap : "path or url"}.
+         #{msg}To force migrate use `brew migrate --force #{formula.oldname}`.
       EOS
     end
   end
diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb
index a3d182a2bfb2cc386e42cfd5c5b80ed578c17317..97ed5749ac2a0d25fa7dbd7dec1ad8f9f8473949 100644
--- a/Library/Homebrew/missing_formula.rb
+++ b/Library/Homebrew/missing_formula.rb
@@ -12,10 +12,10 @@ module Homebrew
 
       def blacklisted_reason(name)
         case name.downcase
-        when "gem", /^rubygems?$/ then <<-EOS.undent
+        when "gem", /^rubygems?$/ then <<~EOS
           Homebrew provides gem via: `brew install ruby`.
           EOS
-        when "tex", "tex-live", "texlive", "latex" then <<-EOS.undent
+        when "tex", "tex-live", "texlive", "latex" then <<~EOS
           Installing TeX from source is weird and gross, requires a lot of patches,
           and only builds 32-bit (and thus can't use Homebrew dependencies)
 
@@ -24,42 +24,42 @@ module Homebrew
           You can install it with Homebrew-Cask:
             brew cask install mactex
           EOS
-        when "pip" then <<-EOS.undent
+        when "pip" then <<~EOS
           Homebrew provides pip via: `brew install python`. However you will then
           have two Pythons installed on your Mac, so alternatively you can install
           pip via the instructions at:
             #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")}
           EOS
-        when "pil" then <<-EOS.undent
+        when "pil" then <<~EOS
           Instead of PIL, consider `pip2 install pillow`.
           EOS
-        when "macruby" then <<-EOS.undent
+        when "macruby" then <<~EOS
           MacRuby is not packaged and is on an indefinite development hiatus.
           You can read more about it at:
             #{Formatter.url("https://github.com/MacRuby/MacRuby")}
           EOS
         when /(lib)?lzma/
           "lzma is now part of the xz formula."
-        when "gtest", "googletest", "google-test" then <<-EOS.undent
+        when "gtest", "googletest", "google-test" then <<~EOS
           Installing gtest system-wide is not recommended; it should be vendored
           in your projects that use it.
           EOS
-        when "gmock", "googlemock", "google-mock" then <<-EOS.undent
+        when "gmock", "googlemock", "google-mock" then <<~EOS
           Installing gmock system-wide is not recommended; it should be vendored
           in your projects that use it.
           EOS
-        when "sshpass" then <<-EOS.undent
+        when "sshpass" then <<~EOS
           We won't add sshpass because it makes it too easy for novice SSH users to
           ruin SSH's security.
           EOS
-        when "gsutil" then <<-EOS.undent
+        when "gsutil" then <<~EOS
           Install gsutil with `pip2 install gsutil`
           EOS
-        when "gfortran" then <<-EOS.undent
+        when "gfortran" then <<~EOS
           GNU Fortran is now provided as part of GCC, and can be installed with:
             brew install gcc
           EOS
-        when "play" then <<-EOS.undent
+        when "play" then <<~EOS
           Play 2.3 replaces the play command with activator:
             brew install typesafe-activator
 
@@ -67,7 +67,7 @@ module Homebrew
             #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")}
             #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")}
           EOS
-        when "haskell-platform" then <<-EOS.undent
+        when "haskell-platform" then <<~EOS
           We no longer package haskell-platform. Consider installing ghc
           and cabal-install instead:
             brew install ghc cabal-install
@@ -75,10 +75,10 @@ module Homebrew
           You can install with Homebrew-Cask:
             brew cask install haskell-platform
           EOS
-        when "mysqldump-secure" then <<-EOS.undent
+        when "mysqldump-secure" then <<~EOS
           The creator of mysqldump-secure tried to game our popularity metrics.
           EOS
-        when "ngrok" then <<-EOS.undent
+        when "ngrok" then <<~EOS
           Upstream sunsetted 1.x in March 2016 and 2.x is not open-source.
 
           If you wish to use the 2.x release you can install with Homebrew-Cask:
@@ -98,12 +98,12 @@ module Homebrew
           new_tap_user, new_tap_repo, = new_tap.split("/")
           new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
 
-          message = <<-EOS.undent
+          message = <<~EOS
             It was migrated from #{old_tap} to #{new_tap}.
           EOS
           break if new_tap_name == CoreTap.instance.name
 
-          message += <<-EOS.undent
+          message += <<~EOS
             You can access it again by running:
               brew tap #{new_tap_name}
           EOS
@@ -124,7 +124,7 @@ module Homebrew
           unless silent
             ohai "Searching for a previously deleted formula..."
             if (tap.path/".git/shallow").exist?
-              opoo <<-EOS.undent
+              opoo <<~EOS
                 #{tap} is shallow clone. To get complete history run:
                   git -C "$(brew --repo #{tap})" fetch --unshallow
 
@@ -147,7 +147,7 @@ module Homebrew
           commit_message.sub!(/ \(#(\d+)\)$/, " (#{tap.issues_url}/\\1)")
           commit_message.gsub!(/(Closes|Fixes) #(\d+)/, "\\1 #{tap.issues_url}/\\2")
 
-          <<-EOS.undent
+          <<~EOS
             #{name} was deleted from #{tap.name} in commit #{short_hash}:
               #{commit_message}
 
diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb
index 9dbb252e4c248bd47398520ecc78e691d14160c6..cb4141f489811407827fb57309321832b40cde59 100644
--- a/Library/Homebrew/os/mac.rb
+++ b/Library/Homebrew/os/mac.rb
@@ -201,6 +201,7 @@ module OS
       "8.3.2" => { clang: "8.1", clang_build: 802 },
       "8.3.3" => { clang: "8.1", clang_build: 802 },
       "9.0"   => { clang: "9.0", clang_build: 900 },
+      "9.0.1" => { clang: "9.0", clang_build: 900 },
     }.freeze
 
     def compilers_standard?
@@ -208,7 +209,7 @@ module OS
         send(:"#{method}_version") == build
       end
     rescue IndexError
-      onoe <<-EOS.undent
+      onoe <<~EOS
         Homebrew doesn't know what compiler versions ship with your version
         of Xcode (#{Xcode.version}). Please `brew update` and if that doesn't
         help, file an issue with the output of `brew --config`:
diff --git a/Library/Homebrew/os/mac/keg.rb b/Library/Homebrew/os/mac/keg.rb
index 6caadb1d7ad56a408129740af7f69114e5576a84..6f2d65fb423921acc980b896364803bf801445f3 100644
--- a/Library/Homebrew/os/mac/keg.rb
+++ b/Library/Homebrew/os/mac/keg.rb
@@ -5,7 +5,7 @@ class Keg
     puts "Changing dylib ID of #{file}\n  from #{file.dylib_id}\n    to #{id}" if ARGV.debug?
     MachO::Tools.change_dylib_id(file, id, strict: false)
   rescue MachO::MachOError
-    onoe <<-EOS.undent
+    onoe <<~EOS
       Failed changing dylib ID of #{file}
         from #{file.dylib_id}
           to #{id}
@@ -19,7 +19,7 @@ class Keg
     puts "Changing install name in #{file}\n  from #{old}\n    to #{new}" if ARGV.debug?
     MachO::Tools.change_install_name(file, old, new, strict: false)
   rescue MachO::MachOError
-    onoe <<-EOS.undent
+    onoe <<~EOS
       Failed changing install name in #{file}
         from #{old}
           to #{new}
diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb
index 5071aafcf0ede9abfd739c5d87ed00761e41cf7f..81149fb4c31fadc5e1644c30b460e9db675f7048 100644
--- a/Library/Homebrew/os/mac/xcode.rb
+++ b/Library/Homebrew/os/mac/xcode.rb
@@ -17,13 +17,13 @@ module OS
         when "10.9"  then "6.2"
         when "10.10" then "7.2.1"
         when "10.11" then "8.2.1"
-        when "10.12" then "8.3.3"
-        when "10.13" then "9.0"
+        when "10.12" then "9.0.1"
+        when "10.13" then "9.0.1"
         else
           raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease?
 
           # Default to newest known version of Xcode for unreleased macOS versions.
-          "9.0"
+          "9.0.1"
         end
       end
 
@@ -86,11 +86,11 @@ module OS
 
       def update_instructions
         if MacOS.version >= "10.9" && !OS::Mac.prerelease?
-          <<-EOS.undent
+          <<~EOS
             Xcode can be updated from the App Store.
           EOS
         else
-          <<-EOS.undent
+          <<~EOS
             Xcode can be updated from
               https://developer.apple.com/download/more/
           EOS
@@ -199,11 +199,11 @@ module OS
 
       def update_instructions
         if MacOS.version >= "10.9"
-          <<-EOS.undent
+          <<~EOS
             Update them from Software Update in the App Store.
           EOS
         elsif MacOS.version == "10.8" || MacOS.version == "10.7"
-          <<-EOS.undent
+          <<~EOS
             The standalone package can be obtained from
               https://developer.apple.com/download/more/
             or it can be installed via Xcode's preferences.
@@ -216,8 +216,8 @@ module OS
         # on the older supported platform for that Xcode release, i.e there's no
         # CLT package for 10.11 that contains the Clang version from Xcode 8.
         case MacOS.version
-        when "10.13" then "900.0.37"
-        when "10.12" then "802.0.42"
+        when "10.13" then "900.0.38"
+        when "10.12" then "900.0.38"
         when "10.11" then "800.0.42.1"
         when "10.10" then "700.1.81"
         when "10.9"  then "600.0.57"
diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb
index 8a1b00930d3371022c8e15d5c73763a0ffe0f743..a44add10e5cd4230bfba9a27e7bab2bf504aac16 100644
--- a/Library/Homebrew/patch.rb
+++ b/Library/Homebrew/patch.rb
@@ -139,7 +139,7 @@ class ExternalPatch
       if patch_files.empty?
         children = patch_dir.children
         if children.length != 1 || !children.first.file?
-          raise MissingApplyError, <<-EOS.undent
+          raise MissingApplyError, <<~EOS
             There should be exactly one patch file in the staging directory unless
             the "apply" method was used one or more times in the patch-do block.
           EOS
diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb
index 1ec8580c448802747e290770671563fc6a4dbcb1..a69c68466b53d63a42a96a358f009c1b9f8f451b 100644
--- a/Library/Homebrew/requirement.rb
+++ b/Library/Homebrew/requirement.rb
@@ -35,18 +35,16 @@ class Requirement
     _, _, class_name = self.class.to_s.rpartition "::"
     s = "#{class_name} unsatisfied!\n"
     if cask
-      s += <<-EOS.undent
-
+      s += <<~EOS
         You can install with Homebrew-Cask:
-          brew cask install #{cask}
+         brew cask install #{cask}
       EOS
     end
 
     if download
-      s += <<-EOS.undent
-
+      s += <<~EOS
         You can download from:
-          #{download}
+         #{download}
       EOS
     end
     s
diff --git a/Library/Homebrew/requirements.rb b/Library/Homebrew/requirements.rb
index 553beb2a222294fdde778d6a7d024bf19e2a3860..35a0c242ccc805fea99946862d04309421d9efe9 100644
--- a/Library/Homebrew/requirements.rb
+++ b/Library/Homebrew/requirements.rb
@@ -2,7 +2,8 @@ require "requirement"
 require "requirements/fortran_requirement"
 require "requirements/gpg2_requirement"
 require "requirements/language_module_requirement"
-require "requirements/minimum_macos_requirement"
+require "requirements/linux_requirement"
+require "requirements/macos_requirement"
 require "requirements/maximum_macos_requirement"
 require "requirements/mpi_requirement"
 require "requirements/osxfuse_requirement"
@@ -33,16 +34,16 @@ class XcodeRequirement < Requirement
 
   def message
     version = " #{@version}" if @version
-    message = <<-EOS.undent
+    message = <<~EOS
       A full installation of Xcode.app#{version} is required to compile this software.
       Installing just the Command Line Tools is not sufficient.
     EOS
     if MacOS.version >= :lion
-      message + <<-EOS.undent
+      message + <<~EOS
         Xcode can be installed from the App Store.
       EOS
     else
-      message + <<-EOS.undent
+      message + <<~EOS
         Xcode can be installed from #{Formatter.url("https://developer.apple.com/download/more/")}.
       EOS
     end
@@ -82,7 +83,7 @@ class TeXRequirement < Requirement
   satisfy { which("tex") || which("latex") }
 
   def message
-    s = <<-EOS.undent
+    s = <<~EOS
       A LaTeX distribution is required for Homebrew to install this formula.
 
       Make sure that "/usr/texbin", or the location you installed it to, is in
diff --git a/Library/Homebrew/requirements/java_requirement.rb b/Library/Homebrew/requirements/java_requirement.rb
index de3a33eb45ad94c999760ca394406f591b88cab9..949978dbd605059ed24ad2b0f83c5a154201f515 100644
--- a/Library/Homebrew/requirements/java_requirement.rb
+++ b/Library/Homebrew/requirements/java_requirement.rb
@@ -11,7 +11,7 @@ class JavaRequirement < Requirement
   end
 
   def initialize(tags)
-    @version = tags.shift if /(\d\.)+\d/ =~ tags.first
+    @version = tags.shift if /(\d+\.)+\d/ =~ tags.first
     super
   end
 
@@ -103,7 +103,7 @@ class JavaRequirement < Requirement
   end
 
   def satisfies_version(java)
-    java_version_s = Utils.popen_read("#{java} -version 2>&1")[/1.\d/]
+    java_version_s = Utils.popen_read("#{java} -version 2>&1")[/\d+.\d/]
     return false unless java_version_s
     java_version = Version.create(java_version_s)
     needed_version = Version.create(version_without_plus)
diff --git a/Library/Homebrew/requirements/language_module_requirement.rb b/Library/Homebrew/requirements/language_module_requirement.rb
index 392bc9b7ad2ee010311b5494558c1b5ece1b2f24..5ddce7a66b98abb36cfbc54c1b30a466a73db1ff 100644
--- a/Library/Homebrew/requirements/language_module_requirement.rb
+++ b/Library/Homebrew/requirements/language_module_requirement.rb
@@ -13,16 +13,15 @@ class LanguageModuleRequirement < Requirement
   satisfy(build_env: false) { quiet_system(*the_test) }
 
   def message
-    s = <<-EOS.undent
+    s = <<~EOS
       Unsatisfied dependency: #{@module_name}
       Homebrew does not provide special #{@language.to_s.capitalize} dependencies; install with:
         `#{command_line} #{@module_name}`
     EOS
 
     unless [:python, :perl, :ruby].include? @language
-      s += <<-EOS.undent
-
-      You may need to: `brew install #{@language}`
+      s += <<~EOS
+        You may need to: `brew install #{@language}`
 
       EOS
     end
diff --git a/Library/Homebrew/requirements/linux_requirement.rb b/Library/Homebrew/requirements/linux_requirement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cb4666e56298a7b30121492032351514bff529f4
--- /dev/null
+++ b/Library/Homebrew/requirements/linux_requirement.rb
@@ -0,0 +1,9 @@
+class LinuxRequirement < Requirement
+  fatal true
+
+  satisfy(build_env: false) { OS.linux? }
+
+  def message
+    "Linux is required."
+  end
+end
diff --git a/Library/Homebrew/requirements/macos_requirement.rb b/Library/Homebrew/requirements/macos_requirement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c89144d2c215618634407ab275f51f6819dd12f6
--- /dev/null
+++ b/Library/Homebrew/requirements/macos_requirement.rb
@@ -0,0 +1,31 @@
+require "requirement"
+
+class MacOSRequirement < Requirement
+  fatal true
+
+  def initialize(tags = [])
+    @version = MacOS::Version.from_symbol(tags.first) unless tags.empty?
+    super
+  end
+
+  def minimum_version_specified?
+    OS.mac? && @version
+  end
+
+  satisfy(build_env: false) do
+    next MacOS.version >= @version if minimum_version_specified?
+    next true if OS.mac?
+    next true if @version
+    false
+  end
+
+  def message
+    return "macOS is required." unless minimum_version_specified?
+    "macOS #{@version.pretty_name} or newer is required."
+  end
+
+  def display_s
+    return "macOS is required" unless minimum_version_specified?
+    "macOS >= #{@version}"
+  end
+end
diff --git a/Library/Homebrew/requirements/maximum_macos_requirement.rb b/Library/Homebrew/requirements/maximum_macos_requirement.rb
index 6e798b478e650cb87523828420696adf6f87cad2..9a8851390f7ced9de5994469316fe2f60dd3162d 100644
--- a/Library/Homebrew/requirements/maximum_macos_requirement.rb
+++ b/Library/Homebrew/requirements/maximum_macos_requirement.rb
@@ -11,7 +11,7 @@ class MaximumMacOSRequirement < Requirement
   satisfy(build_env: false) { MacOS.version <= @version }
 
   def message
-    <<-EOS.undent
+    <<~EOS
       This formula either does not compile or function as expected on macOS
       versions newer than #{@version.pretty_name} due to an upstream incompatibility.
     EOS
diff --git a/Library/Homebrew/requirements/minimum_macos_requirement.rb b/Library/Homebrew/requirements/minimum_macos_requirement.rb
deleted file mode 100644
index 2cb63f740a8f2aa19e516704e3628a626b5aa3a4..0000000000000000000000000000000000000000
--- a/Library/Homebrew/requirements/minimum_macos_requirement.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require "requirement"
-
-class MinimumMacOSRequirement < Requirement
-  fatal true
-
-  def initialize(tags)
-    @version = MacOS::Version.from_symbol(tags.first)
-    super
-  end
-
-  satisfy(build_env: false) { MacOS.version >= @version }
-
-  def message
-    "macOS #{@version.pretty_name} or newer is required."
-  end
-
-  def display_s
-    "macOS >= #{@version}"
-  end
-end
diff --git a/Library/Homebrew/requirements/unsigned_kext_requirement.rb b/Library/Homebrew/requirements/unsigned_kext_requirement.rb
index 2ffc8fda35178872054b945a21192c09403c412d..b58a20ea6cdff99951643a9d3f69cbb5e4bc9faf 100644
--- a/Library/Homebrew/requirements/unsigned_kext_requirement.rb
+++ b/Library/Homebrew/requirements/unsigned_kext_requirement.rb
@@ -6,7 +6,7 @@ class UnsignedKextRequirement < Requirement
   satisfy(build_env: false) { MacOS.version < :yosemite }
 
   def message
-    s = <<-EOS.undent
+    s = <<~EOS
       Building this formula from source isn't possible due to OS X
       Yosemite (10.10) and above's strict unsigned kext ban.
     EOS
diff --git a/Library/Homebrew/rubocops/conflicts_cop.rb b/Library/Homebrew/rubocops/conflicts_cop.rb
index 6f05d05673b38824f25dd9f70666423f16ed5afa..1cca3f8ae8f13f186cbbc5f4f033c8986204b23c 100644
--- a/Library/Homebrew/rubocops/conflicts_cop.rb
+++ b/Library/Homebrew/rubocops/conflicts_cop.rb
@@ -6,7 +6,7 @@ module RuboCop
     module FormulaAudit
       # This cop audits versioned Formulae for `conflicts_with`
       class Conflicts < FormulaCop
-        MSG = <<-EOS.undent
+        MSG = <<~EOS.freeze
           Versioned formulae should not use `conflicts_with`.
           Use `keg_only :versioned_formula` instead.
         EOS
diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb
index 63c75194cfb6158cffc8a6b288f6fb7cb658e6a6..7da4d0f107e6764033d36327c9417fe50b3959ed 100644
--- a/Library/Homebrew/rubocops/extend/formula_cop.rb
+++ b/Library/Homebrew/rubocops/extend/formula_cop.rb
@@ -185,19 +185,19 @@ module RuboCop
         nil
       end
 
-      def_node_search :required_dependency?, <<-EOS.undent
+      def_node_search :required_dependency?, <<~EOS
         (send nil :depends_on ({str sym} _))
       EOS
 
-      def_node_search :required_dependency_name?, <<-EOS.undent
+      def_node_search :required_dependency_name?, <<~EOS
         (send nil :depends_on ({str sym} %1))
       EOS
 
-      def_node_search :dependency_type_hash_match?, <<-EOS.undent
+      def_node_search :dependency_type_hash_match?, <<~EOS
         (hash (pair ({str sym} _) ({str sym} %1)))
       EOS
 
-      def_node_search :dependency_name_hash_match?, <<-EOS.undent
+      def_node_search :dependency_name_hash_match?, <<~EOS
         (hash (pair ({str sym} %1) ({str sym} _)))
       EOS
 
diff --git a/Library/Homebrew/rubocops/formula_desc_cop.rb b/Library/Homebrew/rubocops/formula_desc_cop.rb
index 2ef60303dc3c271f0aa0c91215b9cea2e65c67a8..05f60c9d59faa24e55616e75be0218efb02d8c8b 100644
--- a/Library/Homebrew/rubocops/formula_desc_cop.rb
+++ b/Library/Homebrew/rubocops/formula_desc_cop.rb
@@ -29,7 +29,7 @@ module RuboCop
           desc_length = "#{@formula_name}: #{string_content(desc)}".length
           max_desc_length = 80
           return if desc_length <= max_desc_length
-          problem <<-EOS.undent
+          problem <<~EOS
             Description is too long. "name: desc" should be less than #{max_desc_length} characters.
             Length is calculated as #{@formula_name} + desc. (currently #{desc_length})
           EOS
diff --git a/Library/Homebrew/rubocops/lines_cop.rb b/Library/Homebrew/rubocops/lines_cop.rb
index 7e434d7550a1d639e31a8043f00ec22b015f241c..9354f41f612b090584c93c49c4d270e63f774496 100644
--- a/Library/Homebrew/rubocops/lines_cop.rb
+++ b/Library/Homebrew/rubocops/lines_cop.rb
@@ -1,4 +1,3 @@
-require "FileUtils"
 require_relative "./extend/formula_cop"
 
 module RuboCop
@@ -297,7 +296,7 @@ module RuboCop
           end
 
           find_method_with_args(body_node, :skip_clean, :all) do
-            problem <<-EOS.undent.chomp
+            problem <<~EOS.chomp
               `skip_clean :all` is deprecated; brew no longer strips symbols
                       Pass explicit paths to prevent Homebrew from removing empty folders.
             EOS
@@ -313,6 +312,7 @@ module RuboCop
           end
 
           find_instance_method_call(body_node, "ENV", :universal_binary) do
+            next if @formula_name == "wine"
             problem "macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated."
           end
 
@@ -352,7 +352,7 @@ module RuboCop
           node.modifier_form?
         end
 
-        def_node_search :conditional_dependencies, <<-EOS.undent
+        def_node_search :conditional_dependencies, <<~EOS
           {$(if (send (send nil :build) ${:include? :with? :without?} $(str _))
               (send nil :depends_on $({str sym} _)) nil)
 
@@ -361,22 +361,22 @@ module RuboCop
         EOS
 
         # Match depends_on with hash as argument
-        def_node_matcher :hash_dep, <<-EOS.undent
+        def_node_matcher :hash_dep, <<~EOS
           {(hash (pair $(str _) $(str _)))
            (hash (pair $(str _) (array $(str _) ...)))}
         EOS
 
-        def_node_matcher :destructure_hash, <<-EOS.undent
+        def_node_matcher :destructure_hash, <<~EOS
           (hash (pair $(str _) $(sym _)))
         EOS
 
-        def_node_search :formula_path_strings, <<-EOS.undent
+        def_node_search :formula_path_strings, <<~EOS
           {(dstr (begin (send nil %1)) $(str _ ))
            (dstr _ (begin (send nil %1)) $(str _ ))}
         EOS
 
         # Node Pattern search for Language::Node
-        def_node_search :languageNodeModule?, <<-EOS.undent
+        def_node_search :languageNodeModule?, <<~EOS
           (const (const nil :Language) :Node)
         EOS
       end
diff --git a/Library/Homebrew/rubocops/patches_cop.rb b/Library/Homebrew/rubocops/patches_cop.rb
index 7ee1a127a3adabc04407630e1059f3ce91502090..a752f1019af6f516d15de5091da657b056b044a5 100644
--- a/Library/Homebrew/rubocops/patches_cop.rb
+++ b/Library/Homebrew/rubocops/patches_cop.rb
@@ -28,7 +28,7 @@ module RuboCop
           gh_patch_param_pattern = %r{https?://github\.com/.+/.+/(?:commit|pull)/[a-fA-F0-9]*.(?:patch|diff)}
           if regex_match_group(patch, gh_patch_param_pattern)
             if patch_url !~ /\?full_index=\w+$/
-              problem <<-EOS.undent
+              problem <<~EOS
                 GitHub patches should use the full_index parameter:
                   #{patch_url}?full_index=1
               EOS
@@ -41,7 +41,7 @@ module RuboCop
                                             %r{gist\.githubusercontent\.com/.+/raw}])
           if regex_match_group(patch, gh_patch_patterns)
             if patch_url !~ /[a-fA-F0-9]{40}/
-              problem <<-EOS.undent.chomp
+              problem <<~EOS.chomp
                 GitHub/Gist patches should specify a revision:
                 #{patch_url}
               EOS
@@ -50,7 +50,7 @@ module RuboCop
 
           gh_patch_diff_pattern = %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)}
           if match_obj = regex_match_group(patch, gh_patch_diff_pattern)
-            problem <<-EOS.undent
+            problem <<~EOS
               use GitHub pull request URLs:
                 https://github.com/#{match_obj[1]}/#{match_obj[2]}/pull/#{match_obj[3]}.patch
               Rather than patch-diff:
@@ -59,21 +59,21 @@ module RuboCop
           end
 
           if regex_match_group(patch, %r{macports/trunk})
-            problem <<-EOS.undent.chomp
+            problem <<~EOS.chomp
               MacPorts patches should specify a revision instead of trunk:
               #{patch_url}
             EOS
           end
 
           if regex_match_group(patch, %r{^http://trac\.macports\.org})
-            problem <<-EOS.undent.chomp
+            problem <<~EOS.chomp
               Patches from MacPorts Trac should be https://, not http:
               #{patch_url}
             EOS
           end
 
           return unless regex_match_group(patch, %r{^http://bugs\.debian\.org})
-          problem <<-EOS.undent.chomp
+          problem <<~EOS.chomp
             Patches from Debian should be https://, not http:
             #{patch_url}
           EOS
diff --git a/Library/Homebrew/rubocops/urls_cop.rb b/Library/Homebrew/rubocops/urls_cop.rb
index 071a4c42d6e36253b45080f94ed3849131016da2..414f633c97ee32cbbf794295917a88360f4d2615 100644
--- a/Library/Homebrew/rubocops/urls_cop.rb
+++ b/Library/Homebrew/rubocops/urls_cop.rb
@@ -104,7 +104,7 @@ module RuboCop
             end
 
             if url =~ %r{^https?://prdownloads\.}
-              problem <<-EOS.undent.chomp
+              problem <<~EOS.chomp
                 Don't use prdownloads in SourceForge urls (url is #{url}).
                         See: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/
               EOS
@@ -121,7 +121,7 @@ module RuboCop
           # one out of the grab bag.
           unsecure_deb_pattern = %r{^http://http\.debian\.net/debian/(.*)}i
           audit_urls(urls, unsecure_deb_pattern) do |match, _|
-            problem <<-EOS.undent
+            problem <<~EOS
               Please use a secure mirror for Debian URLs.
               We recommend:
                 https://mirrors.ocf.berkeley.edu/debian/#{match[1]}
@@ -176,7 +176,7 @@ module RuboCop
           # Don't use GitHub codeload URLs
           codeload_gh_pattern = %r{https?://codeload\.github\.com/(.+)/(.+)/(?:tar\.gz|zip)/(.+)}
           audit_urls(urls, codeload_gh_pattern) do |match, url|
-            problem <<-EOS.undent
+            problem <<~EOS
               Use GitHub archive URLs:
                 https://github.com/#{match[1]}/#{match[2]}/archive/#{match[3]}.tar.gz
               Rather than codeload:
diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb
index 7d23e59664ae3f41a1ecaba323e64b4e227d3056..ea74fae098179425f7ffb2cb05ef21b2dd98f70e 100644
--- a/Library/Homebrew/sandbox.rb
+++ b/Library/Homebrew/sandbox.rb
@@ -138,7 +138,7 @@ class Sandbox
   end
 
   class SandboxProfile
-    SEATBELT_ERB = <<-EOS.undent
+    SEATBELT_ERB = <<~EOS.freeze
       (version 1)
       (debug deny) ; log all denied operations to /var/log/system.log
       <%= rules.join("\n") %>
diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb
index 78dc4cf4e72135582860ad9a764ebc2e2092a3db..ebfc4cd80425fba5fb52e23c6ba1dc606dec2d27 100644
--- a/Library/Homebrew/tap.rb
+++ b/Library/Homebrew/tap.rb
@@ -115,6 +115,12 @@ class Tap
     path.git?
   end
 
+  # git branch for this {Tap}.
+  def git_branch
+    raise TapUnavailableError, name unless installed?
+    path.git_branch
+  end
+
   # git HEAD for this {Tap}.
   def git_head
     raise TapUnavailableError, name unless installed?
@@ -261,7 +267,7 @@ class Tap
     return if options[:clone_target]
     return unless private?
     return if quiet
-    puts <<-EOS.undent
+    puts <<~EOS
       It looks like you tapped a private repository. To avoid entering your
       credentials each time you update, you can use git HTTP credential
       caching or issue the following command:
@@ -494,7 +500,7 @@ class Tap
 
   # an array of all installed {Tap} names.
   def self.names
-    map(&:name)
+    map(&:name).sort
   end
 
   # @private
diff --git a/Library/Homebrew/test/cask/artifact/app_spec.rb b/Library/Homebrew/test/cask/artifact/app_spec.rb
index 4ead8b7f94c24aa209b60a43ce4d360c2d7994ab..285cc4f31f2f34e00a096cd1df2a2635d35c8745 100644
--- a/Library/Homebrew/test/cask/artifact/app_spec.rb
+++ b/Library/Homebrew/test/cask/artifact/app_spec.rb
@@ -82,12 +82,12 @@ describe Hbc::Artifact::App, :cask do
 
         describe "target is both writable and user-owned" do
           it "overwrites the existing app" do
-            stdout = <<-EOS.undent
+            stdout = <<~EOS
               ==> Removing App '#{target_path}'.
               ==> Moving App 'Caffeine.app' to '#{target_path}'.
             EOS
 
-            stderr = <<-EOS.undent
+            stderr = <<~EOS
               Warning: It seems there is already an App at '#{target_path}'; overwriting.
             EOS
 
@@ -117,12 +117,12 @@ describe Hbc::Artifact::App, :cask do
             expect(command).to receive(:run).with("/usr/bin/chflags", args: ["-R", "--", "000", target_path], must_succeed: false)
               .and_call_original
 
-            stdout = <<-EOS.undent
+            stdout = <<~EOS
               ==> Removing App '#{target_path}'.
               ==> Moving App 'Caffeine.app' to '#{target_path}'.
             EOS
 
-            stderr = <<-EOS.undent
+            stderr = <<~EOS
               Warning: It seems there is already an App at '#{target_path}'; overwriting.
             EOS
 
@@ -162,12 +162,12 @@ describe Hbc::Artifact::App, :cask do
         let(:force) { true }
 
         it "overwrites the existing app" do
-          stdout = <<-EOS.undent
+          stdout = <<~EOS
             ==> Removing App '#{target_path}'.
             ==> Moving App 'Caffeine.app' to '#{target_path}'.
           EOS
 
-          stderr = <<-EOS.undent
+          stderr = <<~EOS
             Warning: It seems there is already an App at '#{target_path}'; overwriting.
           EOS
 
diff --git a/Library/Homebrew/test/cask/artifact/pkg_spec.rb b/Library/Homebrew/test/cask/artifact/pkg_spec.rb
index 7f1b64d1a0d9acc99ea7201e8a083da78566ac69..89916d283638c0766008d013863aecb629c0a4c4 100644
--- a/Library/Homebrew/test/cask/artifact/pkg_spec.rb
+++ b/Library/Homebrew/test/cask/artifact/pkg_spec.rb
@@ -29,7 +29,7 @@ describe Hbc::Artifact::Pkg, :cask do
 
       file = double(path: Pathname.new("/tmp/choices.xml"))
 
-      expect(file).to receive(:write).with(<<-EOS.undent)
+      expect(file).to receive(:write).with(<<~EOS)
         <?xml version="1.0" encoding="UTF-8"?>
         <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
         <plist version="1.0">
diff --git a/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb
index 7f2ef75b3407a6f9e089faffeb162d3d664c4829..8367dc27de406a568481ba5a47f23831a6cf4d2c 100644
--- a/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb
+++ b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb
@@ -61,7 +61,7 @@ describe Hbc::Artifact::App, :cask do
         target_path_mini.mkpath
 
         expect {
-          expect(install_phase).to output(<<-EOS.undent).to_stdout
+          expect(install_phase).to output(<<~EOS).to_stdout
             ==> Moving App 'Caffeine Pro.app' to '#{target_path_pro}'
           EOS
         }.to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path_mini}'.")
@@ -75,7 +75,7 @@ describe Hbc::Artifact::App, :cask do
         target_path_pro.mkpath
 
         expect {
-          expect(install_phase).to output(<<-EOS.undent).to_stdout
+          expect(install_phase).to output(<<~EOS).to_stdout
             ==> Moving App 'Caffeine Mini.app' to '#{target_path_mini}'
           EOS
         }.to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path_pro}'.")
diff --git a/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb b/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb
index b9872ab9e5152d9762fd0969e14fc796e70ce840..7edfb9e1cea7c3a20d48dbd7283323d4917849c6 100644
--- a/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb
+++ b/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb
@@ -11,7 +11,7 @@ shared_examples "#uninstall_phase or #zap_phase" do
     let(:launchctl_remove_cmd) { %w[/bin/launchctl remove my.fancy.package.service] }
     let(:unknown_response) { "launchctl list returned unknown response\n" }
     let(:service_info) do
-      <<-EOS.undent
+      <<~EOS
         {
                 "LimitLoadToSessionType" = "Aqua";
                 "Label" = "my.fancy.package.service";
diff --git a/Library/Homebrew/test/cask/cli/cat_spec.rb b/Library/Homebrew/test/cask/cli/cat_spec.rb
index 6b54a2e4b005296fcbaef6e9acb577aa4ff4ede3..e1db1b17d84c2f4b8c4bd9919ef66cc4a03a0576 100644
--- a/Library/Homebrew/test/cask/cli/cat_spec.rb
+++ b/Library/Homebrew/test/cask/cli/cat_spec.rb
@@ -7,7 +7,7 @@ describe Hbc::CLI::Cat, :cask do
 
   describe "given a basic Cask" do
     let(:basic_cask_content) {
-      <<-EOS.undent
+      <<~EOS
         cask 'basic-cask' do
           version '1.2.3'
           sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
diff --git a/Library/Homebrew/test/cask/cli/cleanup_spec.rb b/Library/Homebrew/test/cask/cli/cleanup_spec.rb
index 7cf00352d3395fa364a67ae9e78f6a9f112b093a..cbef71be8c209cdad7c4a3be3b00825eda942db0 100644
--- a/Library/Homebrew/test/cask/cli/cleanup_spec.rb
+++ b/Library/Homebrew/test/cask/cli/cleanup_spec.rb
@@ -32,7 +32,7 @@ describe Hbc::CLI::Cleanup, :cask do
 
       expect {
         subject.run
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         ==> Removing cached downloads for #{cask_token}
         #{cached_downloads[0]}
         ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space.
@@ -52,7 +52,7 @@ describe Hbc::CLI::Cleanup, :cask do
 
         expect {
           subject.run
-        }.to output(<<-EOS.undent).to_stdout
+        }.to output(<<~EOS).to_stdout
           ==> Removing cached downloads
           #{cached_download}
           ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space.
@@ -70,7 +70,7 @@ describe Hbc::CLI::Cleanup, :cask do
 
           expect {
             subject.run
-          }.to output(<<-EOS.undent).to_stdout
+          }.to output(<<~EOS).to_stdout
             ==> Removing cached downloads older than 10 days old
             Nothing to do
           EOS
diff --git a/Library/Homebrew/test/cask/cli/create_spec.rb b/Library/Homebrew/test/cask/cli/create_spec.rb
index 60c03db752802ad3590b4b681f2d54dec1a5308e..1b15ecd40979c8974fc10a1e677ad75f2d01995f 100644
--- a/Library/Homebrew/test/cask/cli/create_spec.rb
+++ b/Library/Homebrew/test/cask/cli/create_spec.rb
@@ -28,7 +28,7 @@ describe Hbc::CLI::Create, :cask do
   it "drops a template down for the specified Cask" do
     described_class.run("new-cask")
     template = File.read(Hbc::CaskLoader.path("new-cask"))
-    expect(template).to eq <<-EOS.undent
+    expect(template).to eq <<~EOS
       cask 'new-cask' do
         version ''
         sha256 ''
diff --git a/Library/Homebrew/test/cask/cli/info_spec.rb b/Library/Homebrew/test/cask/cli/info_spec.rb
index e24eead11884a8665aeef421b0158d120405d607..0fc751e06d5becba509d5457093454868ef74910 100644
--- a/Library/Homebrew/test/cask/cli/info_spec.rb
+++ b/Library/Homebrew/test/cask/cli/info_spec.rb
@@ -8,7 +8,7 @@ describe Hbc::CLI::Info, :cask do
   it "displays some nice info about the specified Cask" do
     expect {
       described_class.run("local-caffeine")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       local-caffeine: 1.2.3
       http://example.com/local-caffeine
       Not installed
@@ -22,7 +22,7 @@ describe Hbc::CLI::Info, :cask do
 
   describe "given multiple Casks" do
     let(:expected_output) {
-      <<-EOS.undent
+      <<~EOS
         local-caffeine: 1.2.3
         http://example.com/local-caffeine
         Not installed
@@ -52,7 +52,7 @@ describe Hbc::CLI::Info, :cask do
   it "should print caveats if the Cask provided one" do
     expect {
       described_class.run("with-caveats")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       with-caveats: 1.2.3
       http://example.com/local-caffeine
       Not installed
@@ -78,7 +78,7 @@ describe Hbc::CLI::Info, :cask do
   it 'should not print "Caveats" section divider if the caveats block has no output' do
     expect {
       described_class.run("with-conditional-caveats")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       with-conditional-caveats: 1.2.3
       http://example.com/local-caffeine
       Not installed
@@ -93,7 +93,7 @@ describe Hbc::CLI::Info, :cask do
   it "prints languages specified in the Cask" do
     expect {
       described_class.run("with-languages")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       with-languages: 1.2.3
       http://example.com/local-caffeine
       Not installed
@@ -110,7 +110,7 @@ describe Hbc::CLI::Info, :cask do
   it 'does not print "Languages" section divider if the languages block has no output' do
     expect {
       described_class.run("without-languages")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       without-languages: 1.2.3
       http://example.com/local-caffeine
       Not installed
diff --git a/Library/Homebrew/test/cask/cli/install_spec.rb b/Library/Homebrew/test/cask/cli/install_spec.rb
index c918a3529663cd4e65e280d83ec8b3feefa7b469..25d6cdc9346764cf9c4716a28fd379f5e8a39019 100644
--- a/Library/Homebrew/test/cask/cli/install_spec.rb
+++ b/Library/Homebrew/test/cask/cli/install_spec.rb
@@ -6,7 +6,7 @@ describe Hbc::CLI::Install, :cask do
   it_behaves_like "a command that handles invalid options"
 
   it "displays the installation progress" do
-    output = Regexp.new <<-EOS.undent
+    output = Regexp.new <<~EOS
       ==> Downloading file:.*caffeine.zip
       ==> Verifying checksum for Cask local-caffeine
       ==> Installing Cask local-caffeine
diff --git a/Library/Homebrew/test/cask/cli/list_spec.rb b/Library/Homebrew/test/cask/cli/list_spec.rb
index 301ca9b899fa4220195ad99662168ee845ef2120..eef233accd193560ca567be4915d4288a4937885 100644
--- a/Library/Homebrew/test/cask/cli/list_spec.rb
+++ b/Library/Homebrew/test/cask/cli/list_spec.rb
@@ -12,7 +12,7 @@ describe Hbc::CLI::List, :cask do
 
     expect {
       described_class.run
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       local-caffeine
       local-transmission
     EOS
@@ -31,7 +31,7 @@ describe Hbc::CLI::List, :cask do
 
     expect {
       described_class.run("--full-name")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       local-caffeine
       local-transmission
       third-party/tap/third-party-cask
@@ -41,7 +41,7 @@ describe Hbc::CLI::List, :cask do
   describe "lists versions" do
     let(:casks) { ["local-caffeine", "local-transmission"] }
     let(:expected_output) {
-      <<-EOS.undent
+      <<~EOS
         local-caffeine 1.2.3
         local-transmission 2.61
       EOS
@@ -78,7 +78,7 @@ describe Hbc::CLI::List, :cask do
 
       expect {
         described_class.run("local-transmission", "local-caffeine")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         ==> Apps
         #{Hbc.appdir.join("Transmission.app")} (#{Hbc.appdir.join("Transmission.app").abv})
         ==> Apps
diff --git a/Library/Homebrew/test/cask/cli/outdated_spec.rb b/Library/Homebrew/test/cask/cli/outdated_spec.rb
index 5bbf18d21a6cff641fdd3716e982999cd65c0e95..1ee6a6d7dad8949d615c1c2e78bcbece119f89a9 100644
--- a/Library/Homebrew/test/cask/cli/outdated_spec.rb
+++ b/Library/Homebrew/test/cask/cli/outdated_spec.rb
@@ -23,7 +23,7 @@ describe Hbc::CLI::Outdated, :cask do
     it "checks all the installed Casks when no token is provided" do
       expect {
         described_class.run
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         local-caffeine (1.2.2) != 1.2.3
         local-transmission (2.60) != 2.61
       EOS
@@ -32,7 +32,7 @@ describe Hbc::CLI::Outdated, :cask do
     it "checks only the tokens specified in the command line" do
       expect {
         described_class.run("local-caffeine")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         local-caffeine (1.2.2) != 1.2.3
       EOS
     end
@@ -40,7 +40,7 @@ describe Hbc::CLI::Outdated, :cask do
     it 'ignores "auto_updates" and "latest" Casks even when their tokens are provided in the command line' do
       expect {
         described_class.run("local-caffeine", "auto-updates", "version-latest-string")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         local-caffeine (1.2.2) != 1.2.3
       EOS
     end
@@ -54,7 +54,7 @@ describe Hbc::CLI::Outdated, :cask do
     it "lists only the names (no versions) of the outdated Casks with --quiet" do
       expect {
         described_class.run("--verbose", "--quiet")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         local-caffeine
         local-transmission
       EOS
@@ -65,7 +65,7 @@ describe Hbc::CLI::Outdated, :cask do
     it 'includes the Casks with "auto_updates true" or "version latest" with --greedy' do
       expect {
         described_class.run("--greedy")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         auto-updates (2.57) != 2.61
         local-caffeine (1.2.2) != 1.2.3
         local-transmission (2.60) != 2.61
@@ -79,7 +79,7 @@ describe Hbc::CLI::Outdated, :cask do
 
       expect {
         described_class.run("--greedy")
-      }.to output(<<-EOS.undent).to_stdout
+      }.to output(<<~EOS).to_stdout
         local-caffeine (1.2.2) != 1.2.3
         local-transmission (2.60) != 2.61
         version-latest-string (latest) != latest
diff --git a/Library/Homebrew/test/cask/cli/reinstall_spec.rb b/Library/Homebrew/test/cask/cli/reinstall_spec.rb
index 95294b69586076aadfce53e760cebecdbd027878..5e551e5b5a2fb7f414e0189c87dd408cc72f4087 100644
--- a/Library/Homebrew/test/cask/cli/reinstall_spec.rb
+++ b/Library/Homebrew/test/cask/cli/reinstall_spec.rb
@@ -8,7 +8,7 @@ describe Hbc::CLI::Reinstall, :cask do
 
     Hbc::Installer.new(caffeine).install
 
-    output = Regexp.new <<-EOS.undent
+    output = Regexp.new <<~EOS
       ==> Downloading file:.*caffeine.zip
       Already downloaded: .*local-caffeine--1.2.3.zip
       ==> Verifying checksum for Cask local-caffeine
diff --git a/Library/Homebrew/test/cask/cli/search_spec.rb b/Library/Homebrew/test/cask/cli/search_spec.rb
index a4f796f3c64f39a95efc19f803bd716065473f73..cd1a7bd43c849ef26e17f94796fb5c69d04968f1 100644
--- a/Library/Homebrew/test/cask/cli/search_spec.rb
+++ b/Library/Homebrew/test/cask/cli/search_spec.rb
@@ -12,7 +12,7 @@ describe Hbc::CLI::Search, :cask do
 
     expect {
       Hbc::CLI::Search.run("local")
-    }.to output(<<-EOS.undent).to_stdout.as_tty
+    }.to output(<<~EOS).to_stdout.as_tty
       ==> Partial Matches
       local-caffeine
       local-transmission
@@ -24,7 +24,7 @@ describe Hbc::CLI::Search, :cask do
 
     expect {
       Hbc::CLI::Search.run("local")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       local-caffeine
       local-transmission
     EOS
@@ -35,7 +35,7 @@ describe Hbc::CLI::Search, :cask do
 
     expect {
       Hbc::CLI::Search.run("local")
-    }.to output(<<-EOS.undent).to_stdout
+    }.to output(<<~EOS).to_stdout
       local-caffeine
       local-transmission
     EOS
@@ -45,7 +45,7 @@ describe Hbc::CLI::Search, :cask do
   it "shows that there are no Casks matching a search term that did not result in anything" do
     expect {
       Hbc::CLI::Search.run("foo-bar-baz")
-    }.to output(<<-EOS.undent).to_stdout.as_tty
+    }.to output(<<~EOS).to_stdout.as_tty
       No Cask found for "foo-bar-baz".
     EOS
   end
@@ -84,7 +84,7 @@ describe Hbc::CLI::Search, :cask do
   it "accepts a regexp argument" do
     expect {
       Hbc::CLI::Search.run("/^local-c[a-z]ffeine$/")
-    }.to output(<<-EOS.undent).to_stdout.as_tty
+    }.to output(<<~EOS).to_stdout.as_tty
       ==> Regexp Matches
       local-caffeine
     EOS
@@ -93,7 +93,7 @@ describe Hbc::CLI::Search, :cask do
   it "returns both exact and partial matches" do
     expect {
       Hbc::CLI::Search.run("test-opera")
-    }.to output(<<-EOS.undent).to_stdout.as_tty
+    }.to output(<<~EOS).to_stdout.as_tty
       ==> Exact Match
       test-opera
       ==> Partial Matches
@@ -104,7 +104,7 @@ describe Hbc::CLI::Search, :cask do
   it "does not search the Tap name" do
     expect {
       Hbc::CLI::Search.run("caskroom")
-    }.to output(<<-EOS.undent).to_stdout.as_tty
+    }.to output(<<~EOS).to_stdout.as_tty
       No Cask found for "caskroom".
     EOS
   end
diff --git a/Library/Homebrew/test/cask/cli/uninstall_spec.rb b/Library/Homebrew/test/cask/cli/uninstall_spec.rb
index 80b7edbd38149b8dd10e8dcb02c5df7670824642..1ab8f7e4da89a991415ed9c5fd985407f3b053a2 100644
--- a/Library/Homebrew/test/cask/cli/uninstall_spec.rb
+++ b/Library/Homebrew/test/cask/cli/uninstall_spec.rb
@@ -10,7 +10,7 @@ describe Hbc::CLI::Uninstall, :cask do
 
     Hbc::Installer.new(caffeine).install
 
-    output = Regexp.new <<-EOS.undent
+    output = Regexp.new <<~EOS
       ==> Uninstalling Cask local-caffeine
       ==> Removing App '.*Caffeine.app'.
     EOS
@@ -107,7 +107,7 @@ describe Hbc::CLI::Uninstall, :cask do
       timestamped_versions.each do |timestamped_version|
         caskroom_path.join(".metadata", *timestamped_version, "Casks").tap(&:mkpath)
                      .join("#{token}.rb").open("w") do |caskfile|
-                       caskfile.puts <<-EOS.undent
+                       caskfile.puts <<~EOS
                          cask '#{token}' do
                            version '#{timestamped_version[0]}'
                          end
@@ -153,7 +153,7 @@ describe Hbc::CLI::Uninstall, :cask do
 
       saved_caskfile.dirname.mkpath
 
-      IO.write saved_caskfile, <<-EOS.undent
+      IO.write saved_caskfile, <<~EOS
         cask 'ive-been-renamed' do
           version :latest
 
diff --git a/Library/Homebrew/test/cask/dsl_spec.rb b/Library/Homebrew/test/cask/dsl_spec.rb
index 28cf6f4b2bc9ff11a73b1eb3f77e5edb0093414e..a17acfca68ecc508cd040262b55c10fd829c826f 100644
--- a/Library/Homebrew/test/cask/dsl_spec.rb
+++ b/Library/Homebrew/test/cask/dsl_spec.rb
@@ -20,7 +20,7 @@ describe Hbc::DSL, :cask do
     }
 
     it "prints a warning that it has encountered an unexpected method" do
-      expected = Regexp.compile(<<-EOS.undent.lines.map(&:chomp).join(""))
+      expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join(""))
         (?m)
         Warning:
         .*
@@ -232,7 +232,7 @@ describe Hbc::DSL, :cask do
       expect(cask.caveats).to be_empty
 
       cask = Hbc::Cask.new("cask-with-caveats") do
-        def caveats; <<-EOS.undent
+        def caveats; <<~EOS
           When you install this Cask, you probably want to know this.
           EOS
         end
diff --git a/Library/Homebrew/test/cask/pkg_spec.rb b/Library/Homebrew/test/cask/pkg_spec.rb
index 07443e76eedbff4bae8bf4503fea0ea9802e6c7e..f92d6854e29e4daa59598fa39b4ffc2357ed3004 100644
--- a/Library/Homebrew/test/cask/pkg_spec.rb
+++ b/Library/Homebrew/test/cask/pkg_spec.rb
@@ -123,7 +123,7 @@ describe Hbc::Pkg, :cask do
     end
 
     let(:pkg_info_plist) do
-      <<-EOS.undent
+      <<~EOS
         <?xml version="1.0" encoding="UTF-8"?>
         <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
         <plist version="1.0">
diff --git a/Library/Homebrew/test/cask/system_command_result_spec.rb b/Library/Homebrew/test/cask/system_command_result_spec.rb
index 4a077de7b3c6b25d2dfec8b63ce80c880a1a862b..d09f1220ce01698692d137d4d5fb08f5798fc0e0 100644
--- a/Library/Homebrew/test/cask/system_command_result_spec.rb
+++ b/Library/Homebrew/test/cask/system_command_result_spec.rb
@@ -5,7 +5,7 @@ describe Hbc::SystemCommand::Result, :cask do
     subject { described_class._parse_plist(command, input) }
     let(:command) { Hbc::SystemCommand.new("/usr/bin/true", {}) }
     let(:plist) {
-      <<-EOS.undent
+      <<~EOS
         <?xml version="1.0" encoding="UTF-8"?>
         <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
         <plist version="1.0">
@@ -54,7 +54,7 @@ describe Hbc::SystemCommand::Result, :cask do
 
     context "when output contains garbage" do
       let(:input) {
-        <<-EOS.undent
+        <<~EOS
           Hello there! I am in no way XML am I?!?!
 
             That's a little silly... you were expexting XML here!
diff --git a/Library/Homebrew/test/cmd/custom-external-command_spec.rb b/Library/Homebrew/test/cmd/custom-external-command_spec.rb
index d649786ec88e9e6d04e4d67b36e55a1a335d0d91..8e6ffe2ff2dfd45745b0517a541ecd3416e507fb 100644
--- a/Library/Homebrew/test/cmd/custom-external-command_spec.rb
+++ b/Library/Homebrew/test/cmd/custom-external-command_spec.rb
@@ -4,7 +4,7 @@ describe "brew custom-external-command", :integration_test do
       cmd = "custom-external-command-#{rand}"
       file = path/"brew-#{cmd}"
 
-      file.write <<-EOS.undent
+      file.write <<~EOS
         #!/bin/sh
         echo 'I am #{cmd}.'
       EOS
diff --git a/Library/Homebrew/test/cmd/install_spec.rb b/Library/Homebrew/test/cmd/install_spec.rb
index b6030f26ab0bd53f9d8ce5ca392f9266f9692ad1..8a9f7a0d2e61942fb074e423a110c1dc408fd40e 100644
--- a/Library/Homebrew/test/cmd/install_spec.rb
+++ b/Library/Homebrew/test/cmd/install_spec.rb
@@ -56,7 +56,7 @@ describe "brew install", :integration_test do
   end
 
   specify "install failures" do
-    path = setup_test_formula "testball1", <<-EOS.undent
+    path = setup_test_formula "testball1", <<~EOS
       version "1.0"
     EOS
 
@@ -66,7 +66,7 @@ describe "brew install", :integration_test do
       .and be_a_success
 
     FileUtils.rm path
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       version "2.0"
 
       devel do
@@ -120,7 +120,7 @@ describe "brew install", :integration_test do
   end
 
   it "can install keg-only Formulae" do
-    path_keg_only = setup_test_formula "testball1", <<-EOS.undent
+    path_keg_only = setup_test_formula "testball1", <<~EOS
       version "1.0"
 
       keg_only "test reason"
@@ -132,7 +132,7 @@ describe "brew install", :integration_test do
       .and be_a_success
 
     FileUtils.rm path_keg_only
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       version "2.0"
 
       keg_only "test reason"
@@ -162,7 +162,7 @@ describe "brew install", :integration_test do
       system "git", "commit", "-m", "Initial repo commit"
     end
 
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       version "1.0"
 
       head "file://#{repo_path}", :using => :git
@@ -205,7 +205,7 @@ describe "brew install", :integration_test do
   end
 
   it "succeeds when a non-fatal requirement isn't satisfied" do
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       class NonFatalRequirement < Requirement
         satisfy { false }
       end
@@ -220,7 +220,7 @@ describe "brew install", :integration_test do
   end
 
   it "fails when a fatal requirement isn't satisfied" do
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       class FatalRequirement < Requirement
         fatal true
         satisfy { false }
diff --git a/Library/Homebrew/test/cmd/irb_spec.rb b/Library/Homebrew/test/cmd/irb_spec.rb
index 44410fabe107344182d8c14db043d4de2bf3bc33..0423c6ab73a5fc59b87ee9bf62f154a6dfbd47e4 100644
--- a/Library/Homebrew/test/cmd/irb_spec.rb
+++ b/Library/Homebrew/test/cmd/irb_spec.rb
@@ -3,7 +3,7 @@ describe "brew irb", :integration_test do
     setup_test_formula "testball"
 
     irb_test = HOMEBREW_TEMP/"irb-test.rb"
-    irb_test.write <<-EOS.undent
+    irb_test.write <<~EOS
       "testball".f
       :testball.f
       exit
diff --git a/Library/Homebrew/test/cmd/link_spec.rb b/Library/Homebrew/test/cmd/link_spec.rb
index 78942b7a806b9b81f546ca76de171a343645ae15..bde321e4c129bd95597c2b3a7dc83d2f50d5ff4f 100644
--- a/Library/Homebrew/test/cmd/link_spec.rb
+++ b/Library/Homebrew/test/cmd/link_spec.rb
@@ -36,7 +36,7 @@ describe "brew link", :integration_test do
   end
 
   it "refuses to link keg-only Formulae" do
-    setup_test_formula "testball1", <<-EOS.undent
+    setup_test_formula "testball1", <<~EOS
       keg_only "just because"
     EOS
 
diff --git a/Library/Homebrew/test/cmd/options_spec.rb b/Library/Homebrew/test/cmd/options_spec.rb
index 33fe8b1078e4a508c8e00dcfd57e8aa145e27b6a..bb6b98a47c39a93cb0fc4d032b65c81cc220133e 100644
--- a/Library/Homebrew/test/cmd/options_spec.rb
+++ b/Library/Homebrew/test/cmd/options_spec.rb
@@ -1,6 +1,6 @@
 describe "brew options", :integration_test do
   it "prints a given Formula's options" do
-    setup_test_formula "testball", <<-EOS.undent
+    setup_test_formula "testball", <<~EOS
       depends_on "bar" => :recommended
     EOS
 
diff --git a/Library/Homebrew/test/cmd/style_spec.rb b/Library/Homebrew/test/cmd/style_spec.rb
index 9bc8fcab15e39710c6eb606763d7d97760627438..5c118f32ec63b9f9a2f35a774776ab9834fa53ec 100644
--- a/Library/Homebrew/test/cmd/style_spec.rb
+++ b/Library/Homebrew/test/cmd/style_spec.rb
@@ -19,7 +19,7 @@ describe "brew style" do
     it "returns RubocopResults when RuboCop reports offenses" do
       formula = dir/"my-formula.rb"
 
-      formula.write <<-'EOS'.undent
+      formula.write <<~'EOS'
         class MyFormula < Formula
 
         end
diff --git a/Library/Homebrew/test/cmd/switch_spec.rb b/Library/Homebrew/test/cmd/switch_spec.rb
index 00fe4ace60ba3b70a2cd4c8dcb456048b9394e34..48d7dcdd08d0a6364f077181e3299a9e50d38beb 100644
--- a/Library/Homebrew/test/cmd/switch_spec.rb
+++ b/Library/Homebrew/test/cmd/switch_spec.rb
@@ -10,7 +10,7 @@ describe "brew switch", :integration_test do
       .and not_to_output.to_stdout
       .and be_a_failure
 
-    setup_test_formula "testball", <<-EOS.undent
+    setup_test_formula "testball", <<~EOS
       keg_only "just because"
     EOS
 
diff --git a/Library/Homebrew/test/cmd/uses_spec.rb b/Library/Homebrew/test/cmd/uses_spec.rb
index 2a6f48cb7b885f3868f3db6600b0c482e9b129cc..4a8c446a5c18be73f3af5cc3049dca41d553d358 100644
--- a/Library/Homebrew/test/cmd/uses_spec.rb
+++ b/Library/Homebrew/test/cmd/uses_spec.rb
@@ -2,7 +2,7 @@ describe "brew uses", :integration_test do
   it "prints the Formulae a given Formula is used by" do
     setup_test_formula "foo"
     setup_test_formula "bar"
-    setup_test_formula "baz", <<-EOS.undent
+    setup_test_formula "baz", <<~EOS
       url "https://example.com/baz-1.0"
       depends_on "bar"
     EOS
diff --git a/Library/Homebrew/test/deps_spec.rb b/Library/Homebrew/test/deps_spec.rb
index 4c892c93d5d20a134e829cd60ebdf7350a6ac196..f0ec514d91eb6e13847d999cfe86574c800dfb91 100644
--- a/Library/Homebrew/test/deps_spec.rb
+++ b/Library/Homebrew/test/deps_spec.rb
@@ -2,7 +2,7 @@ describe "brew deps", :integration_test do
   before(:each) do
     setup_test_formula "foo"
     setup_test_formula "bar"
-    setup_test_formula "baz", <<-EOS.undent
+    setup_test_formula "baz", <<~EOS
       url "https://example.com/baz-1.0"
       depends_on "bar"
     EOS
diff --git a/Library/Homebrew/test/descriptions_spec.rb b/Library/Homebrew/test/descriptions_spec.rb
index e873c73b46d191c1709e82f097105552913c3742..c4f67cc4cfd67b469017b2d197953d61a243d969 100644
--- a/Library/Homebrew/test/descriptions_spec.rb
+++ b/Library/Homebrew/test/descriptions_spec.rb
@@ -19,7 +19,7 @@ describe Descriptions do
     descriptions_hash["somedev/external/foo"] = "External foo"
 
     expect { subject.print }.to output(
-      <<-EOS.undent
+      <<~EOS
         homebrew/core/foo: Core foo
         somedev/external/foo: External foo
       EOS
@@ -32,7 +32,7 @@ describe Descriptions do
     descriptions_hash["otherdev/external/foo"] = "Other external foo"
 
     expect { subject.print }.to output(
-      <<-EOS.undent
+      <<~EOS
         homebrew/core/foo: Core foo
         otherdev/external/foo: Other external foo
         somedev/external/foo: External foo
diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb
index 3e99bd06bc67ef6af1e50e4a45a744a0b0f757aa..8dd3aee728862748d0496da4ec1989e802dd7bdb 100644
--- a/Library/Homebrew/test/dev-cmd/audit_spec.rb
+++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb
@@ -18,7 +18,7 @@ describe FormulaText do
   def formula_text(name, body = nil, options = {})
     path = dir/"#{name}.rb"
 
-    path.write <<-EOS.undent
+    path.write <<~EOS
       class #{Formulary.class_s(name)} < Formula
         #{body}
       end
@@ -29,7 +29,7 @@ describe FormulaText do
   end
 
   specify "simple valid Formula" do
-    ft = formula_text "valid", <<-EOS.undent
+    ft = formula_text "valid", <<~EOS
       url "http://www.example.com/valid-1.0.tar.gz"
     EOS
 
@@ -49,7 +49,7 @@ describe FormulaText do
   end
 
   specify "#data?" do
-    ft = formula_text "data", <<-EOS.undent
+    ft = formula_text "data", <<~EOS
       patch :DATA
     EOS
 
@@ -77,7 +77,7 @@ describe FormulaAuditor do
 
   describe "#problems" do
     it "is empty by default" do
-      fa = formula_auditor "foo", <<-EOS.undent
+      fa = formula_auditor "foo", <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
         end
@@ -91,7 +91,7 @@ describe FormulaAuditor do
     specify "file permissions" do
       allow(File).to receive(:umask).and_return(022)
 
-      fa = formula_auditor "foo", <<-EOS.undent
+      fa = formula_auditor "foo", <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
         end
@@ -106,7 +106,7 @@ describe FormulaAuditor do
     end
 
     specify "DATA but no __END__" do
-      fa = formula_auditor "foo", <<-EOS.undent
+      fa = formula_auditor "foo", <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           patch :DATA
@@ -118,7 +118,7 @@ describe FormulaAuditor do
     end
 
     specify "__END__ but no DATA" do
-      fa = formula_auditor "foo", <<-EOS.undent
+      fa = formula_auditor "foo", <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
         end
@@ -138,7 +138,7 @@ describe FormulaAuditor do
     end
 
     specify "no issue" do
-      fa = formula_auditor "foo", <<-EOS.undent
+      fa = formula_auditor "foo", <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -152,7 +152,7 @@ describe FormulaAuditor do
 
   describe "#line_problems" do
     specify "pkgshare" do
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true
+      fa = formula_auditor "foo", <<~EOS, strict: true
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
         end
@@ -184,7 +184,7 @@ describe FormulaAuditor do
     # Formulae with "++" in their name would break various audit regexps:
     #   Error: nested *?+ in regexp: /^libxml++3\s/
     specify "++ in name" do
-      fa = formula_auditor "foolibc++", <<-EOS.undent, strict: true
+      fa = formula_auditor "foolibc++", <<~EOS, strict: true
         class Foolibcxx < Formula
           desc "foolibc++ is a test"
           url "http://example.com/foo-1.0.tgz"
@@ -205,7 +205,7 @@ describe FormulaAuditor do
     specify "#audit_github_repository when HOMEBREW_NO_GITHUB_API is set" do
       ENV["HOMEBREW_NO_GITHUB_API"] = "1"
 
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true, online: true
+      fa = formula_auditor "foo", <<~EOS, strict: true, online: true
         class Foo < Formula
           homepage "https://github.com/example/example"
           url "http://example.com/foo-1.0.tgz"
@@ -219,7 +219,7 @@ describe FormulaAuditor do
 
   describe "#audit_keg_only_style" do
     specify "keg_only_needs_downcasing" do
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true
+      fa = formula_auditor "foo", <<~EOS, strict: true
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
 
@@ -233,7 +233,7 @@ describe FormulaAuditor do
     end
 
     specify "keg_only_redundant_period" do
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true
+      fa = formula_auditor "foo", <<~EOS, strict: true
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
 
@@ -247,11 +247,11 @@ describe FormulaAuditor do
     end
 
     specify "keg_only_handles_block_correctly" do
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true
+      fa = formula_auditor "foo", <<~EOS, strict: true
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
 
-          keg_only <<-EOF.undent
+          keg_only <<~EOF
             this line starts with a lowercase word.
 
             This line does not but that shouldn't be a
@@ -266,7 +266,7 @@ describe FormulaAuditor do
     end
 
     specify "keg_only_handles_whitelist_correctly" do
-      fa = formula_auditor "foo", <<-EOS.undent, strict: true
+      fa = formula_auditor "foo", <<~EOS, strict: true
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
 
@@ -290,7 +290,7 @@ describe FormulaAuditor do
     before(:each) do
       @foo_version = Count.increment
 
-      origin_formula_path.write <<-EOS.undent
+      origin_formula_path.write <<~EOS
         class Foo#{@foo_version} < Formula
           url "https://example.com/foo-1.0.tar.gz"
           revision 2
diff --git a/Library/Homebrew/test/dev-cmd/test_spec.rb b/Library/Homebrew/test/dev-cmd/test_spec.rb
index 9ff365bfb9207558e8a720ec297a1cbaf18cf000..6e5d822bcdef87021581a506476ddd36f89e02e2 100644
--- a/Library/Homebrew/test/dev-cmd/test_spec.rb
+++ b/Library/Homebrew/test/dev-cmd/test_spec.rb
@@ -23,7 +23,7 @@ describe "brew test", :integration_test do
   end
 
   it "tests a given Formula" do
-    setup_test_formula "testball", <<-EOS.undent
+    setup_test_formula "testball", <<~EOS
       head "https://github.com/example/testball2.git"
 
       devel do
diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb
index 7b729312b7b7745038756bc7df19148853762af6..1e3c04b680f8567c008d45d1ebda08e102d8d5be 100644
--- a/Library/Homebrew/test/formula_installer_spec.rb
+++ b/Library/Homebrew/test/formula_installer_spec.rb
@@ -100,7 +100,7 @@ describe FormulaInstaller do
   specify "check installation sanity pinned dependency" do
     dep_name = "dependency"
     dep_path = CoreTap.new.formula_dir/"#{dep_name}.rb"
-    dep_path.write <<-EOS.undent
+    dep_path.write <<~EOS
       class #{Formulary.class_s(dep_name)} < Formula
         url "foo"
         version "0.2"
diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb
index 3180ad9a7b03d969570c6aeb697032f8db5bb32d..f091fdd03d2aa827662bf7e7abb77c40a3d5298a 100644
--- a/Library/Homebrew/test/formulary_spec.rb
+++ b/Library/Homebrew/test/formulary_spec.rb
@@ -6,7 +6,7 @@ describe Formulary do
   let(:formula_name) { "testball_bottle" }
   let(:formula_path) { CoreTap.new.formula_dir/"#{formula_name}.rb" }
   let(:formula_content) do
-    <<-EOS.undent
+    <<~EOS
       class #{subject.class_s(formula_name)} < Formula
         url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
         sha256 TESTBALL_SHA256
@@ -66,7 +66,7 @@ describe Formulary do
     context "if the Formula has the wrong class" do
       let(:formula_name) { "giraffe" }
       let(:formula_content) do
-        <<-EOS.undent
+        <<~EOS
           class Wrong#{subject.class_s(formula_name)} < Formula
           end
         EOS
diff --git a/Library/Homebrew/test/gpg2_requirement_spec.rb b/Library/Homebrew/test/gpg2_requirement_spec.rb
index a5501c84e43a1458d81d35bd1c4fa44ac11a8627..8b9040b82ee98fb7951b0796af7502eb2ed45fd3 100644
--- a/Library/Homebrew/test/gpg2_requirement_spec.rb
+++ b/Library/Homebrew/test/gpg2_requirement_spec.rb
@@ -7,7 +7,7 @@ describe GPG2Requirement do
   describe "#satisfied?" do
     it "returns true if GPG2 is installed" do
       ENV["PATH"] = dir/"bin"
-      (dir/"bin/gpg").write <<-EOS.undent
+      (dir/"bin/gpg").write <<~EOS
         #!/bin/bash
         echo 2.1.20
       EOS
diff --git a/Library/Homebrew/test/inreplace_spec.rb b/Library/Homebrew/test/inreplace_spec.rb
index 5be44f50db8bf864f430b5aba3021cc7b2d43ef0..330943be4501278a04dd9a943fc1ca3da13fc418 100644
--- a/Library/Homebrew/test/inreplace_spec.rb
+++ b/Library/Homebrew/test/inreplace_spec.rb
@@ -9,7 +9,7 @@ describe StringInreplaceExtension do
     context "flag" do
       context "with spaces" do
         let(:string) do
-          <<-EOS.undent
+          <<~EOS
             OTHER=def
             FLAG = abc
             FLAG2=abc
@@ -18,7 +18,7 @@ describe StringInreplaceExtension do
 
         it "is successfully replaced" do
           subject.change_make_var! "FLAG", "def"
-          expect(subject).to eq <<-EOS.undent
+          expect(subject).to eq <<~EOS
             OTHER=def
             FLAG=def
             FLAG2=abc
@@ -27,7 +27,7 @@ describe StringInreplaceExtension do
 
         it "is successfully appended" do
           subject.change_make_var! "FLAG", "\\1 def"
-          expect(subject).to eq <<-EOS.undent
+          expect(subject).to eq <<~EOS
             OTHER=def
             FLAG=abc def
             FLAG2=abc
@@ -37,7 +37,7 @@ describe StringInreplaceExtension do
 
       context "with tabs" do
         let(:string) do
-          <<-EOS.undent
+          <<~EOS
             CFLAGS\t=\t-Wall -O2
             LDFLAGS\t=\t-lcrypto -lssl
           EOS
@@ -45,7 +45,7 @@ describe StringInreplaceExtension do
 
         it "is successfully replaced" do
           subject.change_make_var! "CFLAGS", "-O3"
-          expect(subject).to eq <<-EOS.undent
+          expect(subject).to eq <<~EOS
             CFLAGS=-O3
             LDFLAGS\t=\t-lcrypto -lssl
           EOS
@@ -55,7 +55,7 @@ describe StringInreplaceExtension do
 
     context "empty flag between other flags" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           OTHER=def
           FLAG =
           FLAG2=abc
@@ -64,7 +64,7 @@ describe StringInreplaceExtension do
 
       it "is successfully replaced" do
         subject.change_make_var! "FLAG", "def"
-        expect(subject).to eq <<-EOS.undent
+        expect(subject).to eq <<~EOS
           OTHER=def
           FLAG=def
           FLAG2=abc
@@ -74,7 +74,7 @@ describe StringInreplaceExtension do
 
     context "empty flag" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           FLAG =
           mv file_a file_b
         EOS
@@ -82,7 +82,7 @@ describe StringInreplaceExtension do
 
       it "is successfully replaced" do
         subject.change_make_var! "FLAG", "def"
-        expect(subject).to eq <<-EOS.undent
+        expect(subject).to eq <<~EOS
           FLAG=def
           mv file_a file_b
         EOS
@@ -91,7 +91,7 @@ describe StringInreplaceExtension do
 
     context "shell-style variable" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           OTHER=def
           FLAG=abc
           FLAG2=abc
@@ -100,7 +100,7 @@ describe StringInreplaceExtension do
 
       it "is successfully replaced" do
         subject.change_make_var! "FLAG", "def"
-        expect(subject).to eq <<-EOS.undent
+        expect(subject).to eq <<~EOS
           OTHER=def
           FLAG=def
           FLAG2=abc
@@ -113,7 +113,7 @@ describe StringInreplaceExtension do
     context "flag" do
       context "with spaces" do
         let(:string) do
-          <<-EOS.undent
+          <<~EOS
             OTHER=def
             FLAG = abc
             FLAG2 = def
@@ -122,7 +122,7 @@ describe StringInreplaceExtension do
 
         it "is successfully removed" do
           subject.remove_make_var! "FLAG"
-          expect(subject).to eq <<-EOS.undent
+          expect(subject).to eq <<~EOS
             OTHER=def
             FLAG2 = def
           EOS
@@ -131,7 +131,7 @@ describe StringInreplaceExtension do
 
       context "with tabs" do
         let(:string) do
-          <<-EOS.undent
+          <<~EOS
             CFLAGS\t=\t-Wall -O2
             LDFLAGS\t=\t-lcrypto -lssl
           EOS
@@ -139,7 +139,7 @@ describe StringInreplaceExtension do
 
         it "is successfully removed" do
           subject.remove_make_var! "LDFLAGS"
-          expect(subject).to eq <<-EOS.undent
+          expect(subject).to eq <<~EOS
             CFLAGS\t=\t-Wall -O2
           EOS
         end
@@ -148,7 +148,7 @@ describe StringInreplaceExtension do
 
     context "multiple flags" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           OTHER=def
           FLAG = abc
           FLAG2 = def
@@ -158,7 +158,7 @@ describe StringInreplaceExtension do
 
       specify "are be successfully removed" do
         subject.remove_make_var! ["FLAG", "FLAG2"]
-        expect(subject).to eq <<-EOS.undent
+        expect(subject).to eq <<~EOS
           OTHER=def
           OTHER2=def
         EOS
@@ -169,7 +169,7 @@ describe StringInreplaceExtension do
   describe "#get_make_var" do
     context "with spaces" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           CFLAGS = -Wall -O2
           LDFLAGS = -lcrypto -lssl
         EOS
@@ -182,7 +182,7 @@ describe StringInreplaceExtension do
 
     context "with tabs" do
       let(:string) do
-        <<-EOS.undent
+        <<~EOS
           CFLAGS\t=\t-Wall -O2
           LDFLAGS\t=\t-lcrypto -lssl
         EOS
@@ -217,7 +217,7 @@ describe Utils::Inreplace do
   let(:file) { Tempfile.new("test") }
 
   before(:each) do
-    file.write <<-EOS.undent
+    file.write <<~EOS
       a
       b
       c
diff --git a/Library/Homebrew/test/java_requirement_spec.rb b/Library/Homebrew/test/java_requirement_spec.rb
index 05d4f3cdacad0c6a84fc2d3787b5e04d78cd1d29..685e250e08f1f06dbe9813841e8853f323b1368b 100644
--- a/Library/Homebrew/test/java_requirement_spec.rb
+++ b/Library/Homebrew/test/java_requirement_spec.rb
@@ -50,7 +50,7 @@ describe JavaRequirement do
       let(:java) { path/"java" }
 
       def setup_java_with_version(version)
-        IO.write java, <<-EOS.undent
+        IO.write java, <<~EOS
           #!/bin/sh
           echo 'java version "#{version}"'
         EOS
diff --git a/Library/Homebrew/test/language/python_spec.rb b/Library/Homebrew/test/language/python_spec.rb
index 02f6bf8d2473a04b8a5259476c19cf4b86f5d47a..d384ce602e4c2c29c96d91f469aec006961101d2 100644
--- a/Library/Homebrew/test/language/python_spec.rb
+++ b/Library/Homebrew/test/language/python_spec.rb
@@ -41,7 +41,7 @@ describe Language::Python::Virtualenv::Virtualenv do
               "--no-binary", ":all:", "--ignore-installed", "foo", "bar")
         .and_return(true)
 
-      subject.pip_install <<-EOS.undent
+      subject.pip_install <<~EOS
         foo
         bar
       EOS
diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb
index 0a905004ba9c397525d412a97a17a8d7a5cc5ee3..830ecb6aa6b856322d8ad95725c1e0f043f70393 100644
--- a/Library/Homebrew/test/missing_formula_spec.rb
+++ b/Library/Homebrew/test/missing_formula_spec.rb
@@ -116,7 +116,7 @@ describe Homebrew::MissingFormula do
       Tap.clear_cache
       tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
       tap_path.mkpath
-      (tap_path/"tap_migrations.json").write <<-EOS.undent
+      (tap_path/"tap_migrations.json").write <<~EOS
         { "migrated-formula": "homebrew/bar" }
       EOS
     end
diff --git a/Library/Homebrew/test/os_requirement_spec.rb b/Library/Homebrew/test/os_requirement_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..87f86231c1edf9a313c815df7727b1febd0163ca
--- /dev/null
+++ b/Library/Homebrew/test/os_requirement_spec.rb
@@ -0,0 +1,18 @@
+require "requirements/linux_requirement"
+require "requirements/macos_requirement"
+
+describe LinuxRequirement do
+  describe "#satisfied?" do
+    it "returns true if OS is Linux" do
+      expect(subject.satisfied?).to eq(OS.linux?)
+    end
+  end
+end
+
+describe MacOSRequirement do
+  describe "#satisfied?" do
+    it "returns true if OS is macOS" do
+      expect(subject.satisfied?).to eq(OS.mac?)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/pathname_spec.rb b/Library/Homebrew/test/pathname_spec.rb
index 69314e5f4938fdb8ae857eb01bb5736468ec8059..1349e602b7b9ca5db4ca4134016b33b08f515f33 100644
--- a/Library/Homebrew/test/pathname_spec.rb
+++ b/Library/Homebrew/test/pathname_spec.rb
@@ -79,12 +79,12 @@ describe Pathname do
       touch file
 
       file.append_lines("CONTENT")
-      expect(File.read(file)).to eq <<-EOS.undent
+      expect(File.read(file)).to eq <<~EOS
         CONTENT
       EOS
 
       file.append_lines("CONTENTS")
-      expect(File.read(file)).to eq <<-EOS.undent
+      expect(File.read(file)).to eq <<~EOS
         CONTENT
         CONTENTS
       EOS
diff --git a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb
index b1afdc3f94437dd66e42e9193be0f9470c7e3576..659750858f3fe543c75ab01eb87d708337f5292d 100644
--- a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do
 
   context "When auditing Bottle Block" do
     it "When there is revision in bottle block" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           bottle do
@@ -41,7 +41,7 @@ describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do
 
   context "When auditing Bottle Block with auto correct" do
     it "When there is revision in bottle block" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           bottle do
@@ -50,7 +50,7 @@ describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do
           end
         end
       EOS
-      corrected_source = <<-EOS.undent
+      corrected_source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           bottle do
diff --git a/Library/Homebrew/test/rubocops/caveats_cop_spec.rb b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb
index 4dbe65cfb8112f6fe52f236cfeb22dd1827117e4..68f79e08a78d393b19f86307a8656a9c9bb0bea0 100644
--- a/Library/Homebrew/test/rubocops/caveats_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb
@@ -8,20 +8,19 @@ describe RuboCop::Cop::FormulaAudit::Caveats do
 
   context "When auditing caveats" do
     it "When there is setuid mentioned in caveats" do
-      source = <<-EOS.undent
-      class Foo < Formula
-        homepage "http://example.com/foo"
-        url "http://example.com/foo-1.0.tgz"
-
-        def caveats
-          "setuid"
+      source = <<~EOS
+        class Foo < Formula
+          homepage "http://example.com/foo"
+          url "http://example.com/foo-1.0.tgz"
+           def caveats
+            "setuid"
+          end
         end
-      end
       EOS
 
       expected_offenses = [{  message: "Don't recommend setuid in the caveats, suggest sudo instead.",
                               severity: :convention,
-                              line: 6,
+                              line: 5,
                               column: 5,
                               source: source }]
 
diff --git a/Library/Homebrew/test/rubocops/checksum_cop_spec.rb b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb
index 2f508bbf52333ba72d4860aa35c430cb18ea9daa..ab70f2dcfbe8eba8189a76cd2916f559f256749d 100644
--- a/Library/Homebrew/test/rubocops/checksum_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do
 
   context "When auditing spec checksums" do
     it "When the checksum is empty" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
@@ -42,7 +42,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do
     end
 
     it "When the checksum is not 64 characters" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
@@ -76,7 +76,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do
     end
 
     it "When the checksum has invalid chars" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
@@ -116,7 +116,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do
 
   context "When auditing spec checksums" do
     it "When the checksum has upper case characters" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
@@ -150,7 +150,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do
     end
 
     it "When auditing stable blocks outside spec blocks" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           resource "foo-outside" do
@@ -185,7 +185,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do
 
   context "When auditing checksum with autocorrect" do
     it "When there is uppercase sha256" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
@@ -200,7 +200,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do
         end
       EOS
 
-      corrected_source = <<-EOS.undent
+      corrected_source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           stable do
diff --git a/Library/Homebrew/test/rubocops/class_cop_spec.rb b/Library/Homebrew/test/rubocops/class_cop_spec.rb
index 59252587c03b21c084312569453b8a572d05180e..3f210af1150cf34d511d6aaccf9948e3e66eb572 100644
--- a/Library/Homebrew/test/rubocops/class_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/class_cop_spec.rb
@@ -17,10 +17,10 @@ describe RuboCop::Cop::FormulaAudit::ClassName do
       }]
 
       formulas.each do |formula|
-        source = <<-EOS.undent
-        class Foo < #{formula["class"]}
-          url 'http://example.com/foo-1.0.tgz'
-        end
+        source = <<~EOS
+          class Foo < #{formula["class"]}
+            url 'http://example.com/foo-1.0.tgz'
+          end
         EOS
 
         expected_offenses = [{  message: "#{formula["class"]} is deprecated, use Formula instead",
@@ -38,12 +38,12 @@ describe RuboCop::Cop::FormulaAudit::ClassName do
     end
 
     it "with deprecated inheritance and autocorrect" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < AmazonWebServicesFormula
           url 'http://example.com/foo-1.0.tgz'
         end
       EOS
-      corrected_source = <<-EOS.undent
+      corrected_source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
         end
@@ -60,7 +60,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Test do
 
   context "When auditing formula" do
     it "without a test block" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
         end
diff --git a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb
index f093f4927979d3e2bd06a0bfab5bc92b0fa88d27..a4726c001b6c604af53c22f475106bbb3483c266 100644
--- a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
 
   context "When auditing formula components order" do
     it "When url precedes homepage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -29,7 +29,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
     end
 
     it "When `resource` precedes `depends_on`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "https://example.com/foo-1.0.tgz"
 
@@ -55,7 +55,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
     end
 
     it "When `test` precedes `plist`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "https://example.com/foo-1.0.tgz"
 
@@ -82,7 +82,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
     end
 
     it "When only one of many `depends_on` precedes `conflicts_with`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           depends_on "autoconf" => :build
           conflicts_with "visionmedia-watch"
@@ -116,13 +116,13 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
 
   context "When auditing formula components order with autocorrect" do
     it "When url precedes homepage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
         end
       EOS
-      correct_source = <<-EOS.undent
+      correct_source = <<~EOS
         class Foo < Formula
           homepage "http://example.com"
           url "http://example.com/foo-1.0.tgz"
@@ -134,7 +134,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
     end
 
     it "When `resource` precedes `depends_on`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "https://example.com/foo-1.0.tgz"
 
@@ -145,7 +145,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do
           depends_on "openssl"
         end
       EOS
-      correct_source = <<-EOS.undent
+      correct_source = <<~EOS
         class Foo < Formula
           url "https://example.com/foo-1.0.tgz"
 
diff --git a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
index 9fbe1590412a66edcbe3347fdfc7d53c361c7446..e899a9b07cb44f8c5d51c3c989592005cfc932e7 100644
--- a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do
 
   context "When auditing formula components common errors" do
     it "When url outside stable block" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           stable do
@@ -31,7 +31,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do
     end
 
     it "When both `head` and `head do` are present" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           head "http://example.com/foo.git"
           head do
@@ -54,7 +54,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do
     end
 
     it "When both `bottle :modifier` and `bottle do` are present" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           bottle do
diff --git a/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb b/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb
index 8874ecc963e0bffdb8df9469129c40af35145003..40efe8545b1e331b4406c4d84122241e98bc6e04 100644
--- a/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Conflicts do
 
   context "When auditing formula for conflicts with" do
     it "multiple conflicts_with" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class FooAT20 < Formula
           url 'http://example.com/foo-2.0.tgz'
           conflicts_with "mysql", "mariadb", "percona-server",
@@ -30,7 +30,7 @@ describe RuboCop::Cop::FormulaAudit::Conflicts do
     end
 
     it "no conflicts_with" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class FooAT20 < Formula
           url 'http://example.com/foo-2.0.tgz'
           desc 'Bar'
diff --git a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb
index 4816c3b2695afe3ddce06dcc54d5bd6914973218..0f0189aa8fc01e67ed92ba85457ebbbe163a6316 100644
--- a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do
 
   context "When auditing formula desc" do
     it "When there is no desc" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
         end
@@ -28,7 +28,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do
     end
 
     it "reports an offense when desc is an empty string" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc ''
@@ -49,14 +49,14 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do
     end
 
     it "When desc is too long" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'Bar#{"bar" * 29}'
         end
       EOS
 
-      msg = <<-EOS.undent
+      msg = <<~EOS
         Description is too long. "name: desc" should be less than 80 characters.
         Length is calculated as foo + desc. (currently 95)
       EOS
@@ -73,7 +73,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do
     end
 
     it "When desc is multiline string" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'Bar#{"bar" * 9}'\
@@ -81,7 +81,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do
         end
       EOS
 
-      msg = <<-EOS.undent
+      msg = <<~EOS
         Description is too long. "name: desc" should be less than 80 characters.
         Length is calculated as foo + desc. (currently 98)
       EOS
@@ -104,7 +104,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do
 
   context "When auditing formula desc" do
     it "When wrong \"command-line\" usage in desc" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'command line'
@@ -124,7 +124,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do
     end
 
     it "When an article is used in desc" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'An '
@@ -144,7 +144,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do
     end
 
     it "When an lowercase letter starts a desc" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'bar'
@@ -164,7 +164,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do
     end
 
     it "When formula name is in desc" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'Foo is a foobar'
@@ -184,13 +184,13 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do
     end
 
     it "autocorrects all rules" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc ' an bar: commandline foo '
         end
       EOS
-      correct_source = <<-EOS.undent
+      correct_source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           desc 'an bar: command-line'
diff --git a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb
index 6c7f248baa8c57f448ef5da5564cfeb4cb1e1a24..be9dddae6adb7375a6e82e5dc67b4bef148da319 100644
--- a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do
 
   context "When auditing homepage" do
     it "When there is no homepage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
         end
@@ -28,7 +28,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do
     end
 
     it "Homepage with ftp" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           homepage "ftp://example.com/foo"
           url "http://example.com/foo-1.0.tgz"
@@ -65,7 +65,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do
       }
 
       formula_homepages.each do |name, homepage|
-        source = <<-EOS.undent
+        source = <<~EOS
           class #{name.capitalize} < Formula
             homepage "#{homepage}"
             url "http://example.com/#{name}-1.0.tgz"
diff --git a/Library/Homebrew/test/rubocops/lines_cop_spec.rb b/Library/Homebrew/test/rubocops/lines_cop_spec.rb
index 9705e82789f6fca50738003047646dc39f7bccbb..f96a4fd48b0e5a5ee4656c07182b0c57ec59f1b9 100644
--- a/Library/Homebrew/test/rubocops/lines_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/lines_cop_spec.rb
@@ -25,11 +25,11 @@ describe RuboCop::Cop::FormulaAudit::Lines do
       }]
 
       formulae.each do |formula|
-        source = <<-EOS.undent
-        class Foo < Formula
-          url 'http://example.com/foo-1.0.tgz'
-          depends_on :#{formula["dependency"]}
-        end
+        source = <<~EOS
+          class Foo < Formula
+            url 'http://example.com/foo-1.0.tgz'
+            depends_on :#{formula["dependency"]}
+          end
         EOS
         if formula.key?("correct")
           offense = ":#{formula["dependency"]} is deprecated. Usage should be \"#{formula["correct"]}\""
@@ -57,7 +57,7 @@ describe RuboCop::Cop::FormulaAudit::ClassInheritance do
 
   context "When auditing lines" do
     it "inconsistent space in class inheritance" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo<Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -84,7 +84,7 @@ describe RuboCop::Cop::FormulaAudit::Comments do
 
   context "When auditing formula" do
     it "commented cmake call" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -106,7 +106,7 @@ describe RuboCop::Cop::FormulaAudit::Comments do
     end
 
     it "default template comments" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           # PLEASE REMOVE
           desc "foo"
@@ -128,7 +128,7 @@ describe RuboCop::Cop::FormulaAudit::Comments do
     end
 
     it "commented out depends_on" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -155,12 +155,12 @@ describe RuboCop::Cop::FormulaAudit::AssertStatements do
   subject(:cop) { described_class.new }
 
   it "assert ...include usage" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          assert File.read("inbox").include?("Sample message 1")
-        end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        assert File.read("inbox").include?("Sample message 1")
+      end
     EOS
 
     expected_offenses = [{ message: "Use `assert_match` instead of `assert ...include?`",
@@ -177,12 +177,12 @@ describe RuboCop::Cop::FormulaAudit::AssertStatements do
   end
 
   it "assert ...exist? without a negation" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          assert File.exist? "default.ini"
-        end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        assert File.exist? "default.ini"
+      end
     EOS
 
     expected_offenses = [{ message: 'Use `assert_predicate <path_to_file>, :exist?` instead of `assert File.exist? "default.ini"`',
@@ -199,12 +199,12 @@ describe RuboCop::Cop::FormulaAudit::AssertStatements do
   end
 
   it "assert ...exist? with a negation" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          assert !File.exist?("default.ini")
-        end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        assert !File.exist?("default.ini")
+      end
     EOS
 
     expected_offenses = [{ message: 'Use `refute_predicate <path_to_file>, :exist?` instead of `assert !File.exist?("default.ini")`',
@@ -221,12 +221,12 @@ describe RuboCop::Cop::FormulaAudit::AssertStatements do
   end
 
   it "assert ...executable? without a negation" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          assert File.executable? f
-        end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        assert File.executable? f
+      end
     EOS
 
     expected_offenses = [{ message: "Use `assert_predicate <path_to_file>, :executable?` instead of `assert File.executable? f`",
@@ -247,14 +247,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   subject(:cop) { described_class.new }
 
   it "unless build.without? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return unless build.without? "bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return unless build.without? "bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: 'Use if build.with? "bar" instead of unless build.without? "bar"',
@@ -271,14 +271,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "unless build.with? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return unless build.with? "bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return unless build.with? "bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: 'Use if build.without? "bar" instead of unless build.with? "bar"',
@@ -295,14 +295,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "negated build.with? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if !build.with? "bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if !build.with? "bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Don't negate 'build.with?': use 'build.without?'",
@@ -319,14 +319,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "negated build.without? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if !build.without? "bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if !build.without? "bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Don't negate 'build.without?': use 'build.with?'",
@@ -343,14 +343,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "unnecessary build.without? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if build.without? "--without-bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if build.without? "--without-bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Don't duplicate 'without': Use `build.without? \"bar\"` to check for \"--without-bar\"",
@@ -367,14 +367,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "unnecessary build.with? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if build.with? "--with-bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if build.with? "--with-bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Don't duplicate 'with': Use `build.with? \"bar\"` to check for \"--with-bar\"",
@@ -391,14 +391,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "build.include? conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if build.include? "without-bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if build.include? "without-bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Use build.without? \"bar\" instead of build.include? 'without-bar'",
@@ -415,14 +415,14 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "build.include? with dashed args conditional" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
-          def post_install
-            return if build.include? "--bar"
-          end
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
+        def post_install
+          return if build.include? "--bar"
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Reference 'bar' without dashes",
@@ -439,15 +439,15 @@ describe RuboCop::Cop::FormulaAudit::OptionDeclarations do
   end
 
   it "def options usage" do
-    source = <<-EOS.undent
-        class Foo < Formula
-          desc "foo"
-          url 'http://example.com/foo-1.0.tgz'
+    source = <<~EOS
+      class Foo < Formula
+        desc "foo"
+        url 'http://example.com/foo-1.0.tgz'
 
-          def options
-            [["--bar", "desc"]]
-          end
+        def options
+          [["--bar", "desc"]]
         end
+      end
     EOS
 
     expected_offenses = [{ message: "Use new-style option definitions",
@@ -469,7 +469,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
 
   context "When auditing formula" do
     it "FileUtils usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -491,7 +491,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "long inreplace block vars" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -514,8 +514,8 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       end
     end
 
-    it "invalid rebuild statement" do
-      source = <<-EOS.undent
+    it "an invalid rebuild statement" do
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -540,7 +540,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "OS.linux? check" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -567,7 +567,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "fails_with :llvm block" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -594,8 +594,8 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       end
     end
 
-    it "def test deprecated usage" do
-      source = <<-EOS.undent
+    it "def test's deprecated usage" do
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -620,7 +620,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "deprecated skip_clean call" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -628,9 +628,9 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
         end
       EOS
 
-      expected_offenses = [{ message: <<-EOS.undent.chomp,
-                              `skip_clean :all` is deprecated; brew no longer strips symbols
-                                      Pass explicit paths to prevent Homebrew from removing empty folders.
+      expected_offenses = [{ message: <<~EOS.chomp,
+        `skip_clean :all` is deprecated; brew no longer strips symbols
+                Pass explicit paths to prevent Homebrew from removing empty folders.
                              EOS
                              severity: :convention,
                              line: 4,
@@ -645,7 +645,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "build.universal? deprecated usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -669,7 +669,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "build.universal? deprecation exempted formula" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Wine < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -680,11 +680,11 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       EOS
 
       inspect_source(source, "/homebrew-core/Formula/wine.rb")
-      expect(cop.offenses).to eq([])
+      expect(cop.offenses).to be_empty
     end
 
     it "deprecated ENV.universal_binary usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -707,8 +707,23 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       end
     end
 
+    it "ENV.universal_binary deprecation exempted formula" do
+      source = <<~EOS
+        class Wine < Formula
+          desc "foo"
+          url 'http://example.com/foo-1.0.tgz'
+          if build?
+            ENV.universal_binary
+          end
+        end
+      EOS
+
+      inspect_source(source, "/homebrew-core/Formula/wine.rb")
+      expect(cop.offenses).to be_empty
+    end
+
     it "deprecated ENV.x11 usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -731,8 +746,8 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       end
     end
 
-    it "ruby-macho alternative to install_name_tool usage" do
-      source = <<-EOS.undent
+    it "install_name_tool usage instead of ruby-macho" do
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -754,7 +769,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "ruby-macho alternatives audit exempted formula" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Cctools < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -763,11 +778,11 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       EOS
 
       inspect_source(source, "/homebrew-core/Formula/cctools.rb")
-      expect(cop.offenses).to eq([])
+      expect(cop.offenses).to be_empty
     end
 
     it "npm install without language::Node args" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -789,7 +804,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "npm install without language::Node args in kibana(exempted formula)" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class KibanaAT44 < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -798,11 +813,11 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
       EOS
 
       inspect_source(source, "/homebrew-core/Formula/kibana@4.4.rb")
-      expect(cop.offenses).to eq([])
+      expect(cop.offenses).to be_empty
     end
 
     it "depends_on with an instance as argument" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -824,7 +839,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "old style OS check" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -846,7 +861,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "non glob DIR usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -869,7 +884,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "system call to fileUtils Method" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -891,7 +906,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "top-level function def outside class body" do
-      source = <<-EOS.undent
+      source = <<~EOS
         def test
            nil
         end
@@ -915,7 +930,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "Using ARGV to check options" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -939,7 +954,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it 'man+"man8" usage' do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -963,7 +978,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "hardcoded gcc compiler" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -987,7 +1002,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "hardcoded g++ compiler" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1011,7 +1026,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "hardcoded llvm-g++ compiler" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1035,7 +1050,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "hardcoded gcc compiler" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1059,7 +1074,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "formula path shortcut : man" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1083,7 +1098,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "formula path shortcut : libexec" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1107,7 +1122,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "formula path shortcut : info" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1131,7 +1146,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "formula path shortcut : man8" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1155,7 +1170,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "dependecies which have to vendored" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1177,7 +1192,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "manually setting env" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1199,7 +1214,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "dependencies with invalid options" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1221,7 +1236,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "inspecting version manually" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1245,7 +1260,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "deprecated ENV.fortran usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1269,7 +1284,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "deprecated ARGV.include? (--HEAD) usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1293,7 +1308,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "deprecated MACOS_VERSION const usage" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1317,7 +1332,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "deprecated if build.with? conditional dependency" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1339,7 +1354,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "unless conditional dependency with build.without?" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
@@ -1361,7 +1376,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do
     end
 
     it "unless conditional dependency with build.include?" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           desc "foo"
           url 'http://example.com/foo-1.0.tgz'
diff --git a/Library/Homebrew/test/rubocops/options_cop_spec.rb b/Library/Homebrew/test/rubocops/options_cop_spec.rb
index c27389a68b47a32bec096e90b9094fe40690f54f..1ed6ee740d08e3c43ee5827de2c3a6000532efa2 100644
--- a/Library/Homebrew/test/rubocops/options_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/options_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Options do
 
   context "When auditing options" do
     it "32-bit" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           option "32-bit", "with 32-bit"
@@ -35,7 +35,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do
 
   context "When auditing options strictly" do
     it "with universal" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           option :universal
@@ -56,7 +56,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do
     end
 
     it "with deprecated options" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           option :cxx11
@@ -80,7 +80,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do
     end
 
     it "with misc deprecated options" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           option "without-check"
@@ -109,7 +109,7 @@ describe RuboCop::Cop::NewFormulaAudit::Options do
 
   context "When auditing options for a new formula" do
     it "with deprecated options" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
           deprecated_option "examples" => "with-examples"
diff --git a/Library/Homebrew/test/rubocops/patches_cop_spec.rb b/Library/Homebrew/test/rubocops/patches_cop_spec.rb
index 4f9ca2df84b9131d204ac6934f112c99010c0691..fdecb676efa4b4e1fe2cbfee77b01de8c9c4b54b 100644
--- a/Library/Homebrew/test/rubocops/patches_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/patches_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do
 
   context "When auditing legacy patches" do
     it "When there is no legacy patch" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url 'http://example.com/foo-1.0.tgz'
         end
@@ -18,7 +18,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do
     end
 
     it "Formula with `def patches`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           homepage "ftp://example.com/foo"
           url "http://example.com/foo-1.0.tgz"
@@ -51,7 +51,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do
         "https://github.com/dlang/dub/pull/1221.patch",
       ]
       patch_urls.each do |patch_url|
-        source = <<-EOS.undent
+        source = <<~EOS
           class Foo < Formula
             homepage "ftp://example.com/foo"
             url "http://example.com/foo-1.0.tgz"
@@ -63,56 +63,56 @@ describe RuboCop::Cop::FormulaAudit::Patches do
 
         inspect_source(source)
         expected_offense = if patch_url =~ %r{/raw\.github\.com/}
-          [{ message: <<-EOS.undent.chomp,
-               GitHub/Gist patches should specify a revision:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            GitHub/Gist patches should specify a revision:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 12,
              source: source }]
         elsif patch_url =~ %r{macports/trunk}
-          [{ message: <<-EOS.undent.chomp,
-               MacPorts patches should specify a revision instead of trunk:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            MacPorts patches should specify a revision instead of trunk:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 33,
              source: source }]
         elsif patch_url =~ %r{^http://trac\.macports\.org}
-          [{ message: <<-EOS.undent.chomp,
-               Patches from MacPorts Trac should be https://, not http:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            Patches from MacPorts Trac should be https://, not http:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 5,
              source: source }]
         elsif patch_url =~ %r{^http://bugs\.debian\.org}
-          [{ message: <<-EOS.undent.chomp,
-               Patches from Debian should be https://, not http:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            Patches from Debian should be https://, not http:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 5,
              source: source }]
         elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)}
-          [{ message: <<-EOS.undent,
-               use GitHub pull request URLs:
-                 https://github.com/foo/foo-bar/pull/100.patch
-               Rather than patch-diff:
-                 https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch
+          [{ message: <<~EOS,
+            use GitHub pull request URLs:
+              https://github.com/foo/foo-bar/pull/100.patch
+            Rather than patch-diff:
+              https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch
              EOS
              severity: :convention,
              line: 5,
              column: 5,
              source: source }]
         elsif patch_url =~ %r{https?://github\.com/.+/.+/(?:commit|pull)/[a-fA-F0-9]*.(?:patch|diff)}
-          [{ message: <<-EOS.undent,
-               GitHub patches should use the full_index parameter:
-                 #{patch_url}?full_index=1
+          [{ message: <<~EOS,
+            GitHub patches should use the full_index parameter:
+              #{patch_url}?full_index=1
              EOS
              severity: :convention,
              line: 5,
@@ -126,7 +126,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do
     end
 
     it "Formula with nested `def patches`" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           homepage "ftp://example.com/foo"
           url "http://example.com/foo-1.0.tgz"
@@ -145,9 +145,9 @@ describe RuboCop::Cop::FormulaAudit::Patches do
                              line: 4,
                              column: 2,
                              source: source },
-                           { message: <<-EOS.undent.chomp,
-                               Patches from MacPorts Trac should be https://, not http:
-                               http://trac.macports.org/export/68507/trunk/dports/net/trafshow/files/
+                           { message: <<~EOS.chomp,
+                             Patches from MacPorts Trac should be https://, not http:
+                             http://trac.macports.org/export/68507/trunk/dports/net/trafshow/files/
                              EOS
                              severity: :convention,
                              line: 8,
@@ -172,7 +172,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do
         "https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch",
       ]
       patch_urls.each do |patch_url|
-        source = <<-EOS.undent
+        source = <<~EOS
           class Foo < Formula
             homepage "ftp://example.com/foo"
             url "http://example.com/foo-1.0.tgz"
@@ -185,47 +185,47 @@ describe RuboCop::Cop::FormulaAudit::Patches do
 
         inspect_source(source)
         expected_offense = if patch_url =~ %r{/raw\.github\.com/}
-          [{ message: <<-EOS.undent.chomp,
-               GitHub/Gist patches should specify a revision:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            GitHub/Gist patches should specify a revision:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 16,
              source: source }]
         elsif patch_url =~ %r{macports/trunk}
-          [{ message: <<-EOS.undent.chomp,
-               MacPorts patches should specify a revision instead of trunk:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            MacPorts patches should specify a revision instead of trunk:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 37,
              source: source }]
         elsif patch_url =~ %r{^http://trac\.macports\.org}
-          [{ message: <<-EOS.undent.chomp,
-               Patches from MacPorts Trac should be https://, not http:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            Patches from MacPorts Trac should be https://, not http:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 9,
              source: source }]
         elsif patch_url =~ %r{^http://bugs\.debian\.org}
-          [{ message: <<-EOS.undent.chomp,
-               Patches from Debian should be https://, not http:
-               #{patch_url}
+          [{ message: <<~EOS.chomp,
+            Patches from Debian should be https://, not http:
+            #{patch_url}
              EOS
              severity: :convention,
              line: 5,
              column: 9,
              source: source }]
         elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)}
-          [{ message: <<-EOS.undent,
-               use GitHub pull request URLs:
-                 https://github.com/foo/foo-bar/pull/100.patch
-               Rather than patch-diff:
-                 https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch
+          [{ message: <<~EOS,
+            use GitHub pull request URLs:
+              https://github.com/foo/foo-bar/pull/100.patch
+            Rather than patch-diff:
+              https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch
              EOS
              severity: :convention,
              line: 5,
diff --git a/Library/Homebrew/test/rubocops/text_cop_spec.rb b/Library/Homebrew/test/rubocops/text_cop_spec.rb
index 49080177035356e9aa697de89ab67981b9bdbeae..dbddff1adb7ed9e37601b4e100d9b80add93287f 100644
--- a/Library/Homebrew/test/rubocops/text_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/text_cop_spec.rb
@@ -8,7 +8,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
 
   context "When auditing formula text" do
     it "with both openssl and libressl optional dependencies" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -32,7 +32,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "with both openssl and libressl dependencies" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -56,7 +56,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When xcodebuild is called without SYMROOT" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -81,7 +81,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When xcodebuild is called without any args" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -106,7 +106,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When go get is executed" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -131,7 +131,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When xcodebuild is executed" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -156,7 +156,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When scons is executed" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -180,8 +180,8 @@ describe RuboCop::Cop::FormulaAudit::Text do
       end
     end
 
-    it "When plist_options are not defined when using a formula-defined plist" do
-      source = <<-EOS.undent
+    it "When plist_options are not defined when using a formula-defined plist", :ruby23 do
+      source = <<~RUBY
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -190,19 +190,20 @@ describe RuboCop::Cop::FormulaAudit::Text do
             system "xcodebuild", "foo", "bar"
           end
 
-          def plist; <<-EOS.undent
-            <?xml version="1.0" encoding="UTF-8"?>
-            <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-            <plist version="1.0">
-            <dict>
-              <key>Label</key>
-              <string>org.nrpe.agent</string>
-            </dict>
-            </plist>
-            \EOS
+          def plist
+            <<~XML
+              <?xml version="1.0" encoding="UTF-8"?>
+              <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+              <plist version="1.0">
+              <dict>
+                <key>Label</key>
+                <string>org.nrpe.agent</string>
+              </dict>
+              </plist>
+            XML
           end
         end
-      EOS
+      RUBY
 
       expected_offenses = [{  message: "Please set plist_options when using a formula-defined plist.",
                               severity: :convention,
@@ -218,7 +219,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When language/go is require'd" do
-      source = <<-EOS.undent
+      source = <<~EOS
         require "language/go"
 
         class Foo < Formula
@@ -245,7 +246,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When formula uses virtualenv and also `setuptools` resource" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
@@ -275,7 +276,7 @@ describe RuboCop::Cop::FormulaAudit::Text do
     end
 
     it "When Formula.factory(name) is used" do
-      source = <<-EOS.undent
+      source = <<~EOS
         class Foo < Formula
           url "http://example.com/foo-1.0.tgz"
           homepage "http://example.com"
diff --git a/Library/Homebrew/test/rubocops/urls_cop_spec.rb b/Library/Homebrew/test/rubocops/urls_cop_spec.rb
index ad939a1a2a8d45785338f18dc1863a479c3edaa0..0bda7f1102803728b18b48e9ab8189025dbccd85 100644
--- a/Library/Homebrew/test/rubocops/urls_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/urls_cop_spec.rb
@@ -54,7 +54,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do
         "col" => 2,
       }, {
         "url" => "http://prdownloads.sourceforge.net/foo/foo-1.tar.gz",
-        "msg" => <<-EOS.undent.chomp,
+        "msg" => <<~EOS.chomp,
           Don't use prdownloads in SourceForge urls (url is http://prdownloads.sourceforge.net/foo/foo-1.tar.gz).
                   See: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/
         EOS
@@ -69,7 +69,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do
         "col" => 2,
       }, {
         "url" => "http://http.debian.net/debian/dists/foo/",
-        "msg" => <<-EOS.undent,
+        "msg" => <<~EOS,
           Please use a secure mirror for Debian URLs.
           We recommend:
             https://mirrors.ocf.berkeley.edu/debian/dists/foo/
@@ -101,7 +101,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do
         "col" => 2,
       }, {
         "url" => "https://codeload.github.com/foo/bar/tar.gz/v0.1.1",
-        "msg" => <<-EOS.undent,
+        "msg" => <<~EOS,
           Use GitHub archive URLs:
             https://github.com/foo/bar/archive/v0.1.1.tar.gz
           Rather than codeload:
@@ -114,7 +114,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do
         "col" => 2,
       }]
       formulas.each do |formula|
-        source = <<-EOS.undent
+        source = <<~EOS
           class Foo < Formula
             desc "foo"
             url "#{formula["url"]}"
@@ -141,7 +141,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do
         "col" => 4,
       }]
       formulas.each do |formula|
-        source = <<-EOS.undent
+        source = <<~EOS
           class Foo < Formula
             desc "foo"
             url "https://foo.com"
@@ -169,12 +169,12 @@ describe RuboCop::Cop::FormulaAudit::Urls do
     end
 
     it "with duplicate mirror" do
-      source = <<-EOS.undent
-          class Foo < Formula
-            desc "foo"
-            url "https://ftpmirror.fnu.org/foo/foo-1.0.tar.gz"
-            mirror "https://ftpmirror.fnu.org/foo/foo-1.0.tar.gz"
-          end
+      source = <<~EOS
+        class Foo < Formula
+          desc "foo"
+          url "https://ftpmirror.fnu.org/foo/foo-1.0.tar.gz"
+          mirror "https://ftpmirror.fnu.org/foo/foo-1.0.tar.gz"
+        end
       EOS
 
       expected_offenses = [{ message: "URL should not be duplicated as a mirror: https://ftpmirror.fnu.org/foo/foo-1.0.tar.gz",
@@ -204,13 +204,13 @@ describe RuboCop::Cop::FormulaAuditStrict::PyPiUrls do
         "corrected_url" =>"https://files.pythonhosted.org/packages/source/foo/foo-0.1.tar.gz",
       }]
       formulas.each do |formula|
-        source = <<-EOS.undent
+        source = <<~EOS
           class Foo < Formula
             desc "foo"
             url "#{formula["url"]}"
           end
         EOS
-        corrected_source = <<-EOS.undent
+        corrected_source = <<~EOS
           class Foo < Formula
             desc "foo"
             url "#{formula["corrected_url"]}"
diff --git a/Library/Homebrew/test/sandbox_spec.rb b/Library/Homebrew/test/sandbox_spec.rb
index 38ff4da75e2c314d810de9bed286fd1bd44c8fac..774337967335259dae06c28667f4262514f46846 100644
--- a/Library/Homebrew/test/sandbox_spec.rb
+++ b/Library/Homebrew/test/sandbox_spec.rb
@@ -49,7 +49,7 @@ describe Sandbox do
     it "ignores bogus Python error" do
       ENV["HOMEBREW_VERBOSE"] = "1"
 
-      with_bogus_error = <<-EOS.undent
+      with_bogus_error = <<~EOS
         foo
         Mar 17 02:55:06 sandboxd[342]: Python(49765) deny file-write-unlink /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/distutils/errors.pyc
         bar
diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb
index a4ac845477ef34a6658479138f7e82029c0687b6..a5821ee14714cb81c8da91a25d9f0109a00f0a46 100644
--- a/Library/Homebrew/test/spec_helper.rb
+++ b/Library/Homebrew/test/spec_helper.rb
@@ -131,7 +131,7 @@ RSpec.configure do |config|
       files_after_test = find_files
 
       diff = Set.new(@__files_before_test) ^ Set.new(files_after_test)
-      expect(diff).to be_empty, <<-EOS.undent
+      expect(diff).to be_empty, <<~EOS
         file leak detected:
         #{diff.map { |f| "  #{f}" }.join("\n")}
       EOS
diff --git a/Library/Homebrew/test/string_spec.rb b/Library/Homebrew/test/string_spec.rb
index ce26d70d446f9d4cab76d501aa88ba2f848e6a30..6de89dc99a70713d6f3138ac14509952850acea7 100644
--- a/Library/Homebrew/test/string_spec.rb
+++ b/Library/Homebrew/test/string_spec.rb
@@ -1,41 +1,5 @@
 require "extend/string"
 
-describe String do
-  describe "#undent" do
-    it "removes leading whitespace, taking the first line as reference" do
-      string = <<-EOS.unindent
-                hi
-                ........my friend over
-                  there
-      EOS
-
-      expect(string).to eq("hi\n........my friend over\n  there\n")
-    end
-
-    it "removes nothing if the text is not indented" do
-      string = <<-EOS.unindent
-        hi
-        I'm not indented
-      EOS
-
-      expect(string).to eq("hi\nI'm not indented\n")
-    end
-
-    it "can be nested" do
-      nested_string = <<-EOS.undent
-        goodbye
-      EOS
-
-      string = <<-EOS.undent
-        hello
-        #{nested_string}
-      EOS
-
-      expect(string).to eq("hello\ngoodbye\n\n")
-    end
-  end
-end
-
 describe StringInreplaceExtension do
   subject { string.extend(described_class) }
   let(:string) { "foobar" }
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-caveats.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-caveats.rb
index d5d90114a92ee3d97c7d2900f36d6cb94a851c4d..5624f486ec9d7311bbed214205df356497af0dab 100644
--- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-caveats.rb
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-caveats.rb
@@ -8,7 +8,7 @@ cask 'with-caveats' do
   app 'Caffeine.app'
 
   # simple string is evaluated at compile-time
-  caveats <<-EOS.undent
+  caveats <<~EOS
     Here are some things you might want to know.
   EOS
   # do block is evaluated at install-time
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-script-app.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-script-app.rb
index f5f3ae5dde5bfce79ec031468ec65b81370d9e8f..885f3b15786d37093f3e0cfd770227988fb242fb 100644
--- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-script-app.rb
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-script-app.rb
@@ -8,7 +8,7 @@ cask 'with-uninstall-script-app' do
   app 'MyFancyApp/MyFancyApp.app'
 
   postflight do
-    IO.write "#{appdir}/MyFancyApp.app/uninstall.sh", <<-EOS.undent
+    IO.write "#{appdir}/MyFancyApp.app/uninstall.sh", <<~EOS
       #!/bin/sh
       /bin/rm -r "#{appdir}/MyFancyApp.app"
     EOS
diff --git a/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb
index 7bf2ce96a3e4acc2abaec89210ba1f2d1b9beceb..da01580c23b3c3e566062041cb1ee2984b290d65 100644
--- a/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb
+++ b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb
@@ -123,7 +123,7 @@ RSpec.shared_context "integration test" do
   def setup_test_formula(name, content = nil)
     case name
     when /^testball/
-      content = <<-EOS.undent
+      content = <<~EOS
         desc "Some test"
         homepage "https://example.com/#{name}"
         url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
@@ -145,18 +145,18 @@ RSpec.shared_context "integration test" do
         # something here
       EOS
     when "foo"
-      content = <<-EOS.undent
+      content = <<~EOS
         url "https://example.com/#{name}-1.0"
       EOS
     when "bar"
-      content = <<-EOS.undent
+      content = <<~EOS
         url "https://example.com/#{name}-1.0"
         depends_on "foo"
       EOS
     end
 
     Formulary.core_path(name).tap do |formula_path|
-      formula_path.write <<-EOS.undent
+      formula_path.write <<~EOS
         class #{Formulary.class_s(name)} < Formula
           #{content}
         end
diff --git a/Library/Homebrew/test/tap_spec.rb b/Library/Homebrew/test/tap_spec.rb
index 957f9bdd2698d8dd74cf3e5f2a4fde8a6666da8f..d69eb615cc36d0550375bd1cb932c7ae5fc3b708 100644
--- a/Library/Homebrew/test/tap_spec.rb
+++ b/Library/Homebrew/test/tap_spec.rb
@@ -19,7 +19,7 @@ describe Tap do
   end
 
   def setup_tap_files
-    formula_file.write <<-EOS.undent
+    formula_file.write <<~EOS
       class Foo < Formula
         url "https://example.com/foo-1.0.tar.gz"
       end
@@ -28,11 +28,11 @@ describe Tap do
     alias_file.parent.mkpath
     ln_s formula_file, alias_file
 
-    (path/"formula_renames.json").write <<-EOS.undent
+    (path/"formula_renames.json").write <<~EOS
       { "oldname": "foo" }
     EOS
 
-    (path/"tap_migrations.json").write <<-EOS.undent
+    (path/"tap_migrations.json").write <<~EOS
       { "removed-formula": "homebrew/foo" }
     EOS
 
@@ -327,7 +327,7 @@ describe CoreTap do
 
   specify "files" do
     formula_file = subject.formula_dir/"foo.rb"
-    formula_file.write <<-EOS.undent
+    formula_file.write <<~EOS
       class Foo < Formula
         url "https://example.com/foo-1.0.tar.gz"
       end
diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb
index 3b5355b151f9ba97d71c53655295334bcd7131f2..0c2ae5161ef87e9806e4327fef86996dd87e82a2 100644
--- a/Library/Homebrew/test/utils_spec.rb
+++ b/Library/Homebrew/test/utils_spec.rb
@@ -98,7 +98,7 @@ describe "globally-scoped helper methods" do
     let(:shell) { dir/"myshell" }
 
     it "starts an interactive shell session" do
-      IO.write shell, <<-EOS.undent
+      IO.write shell, <<~EOS
         #!/bin/sh
         echo called > "#{dir}/called"
       EOS
diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb
index d0393afa655cfdc8f22febbc92b9ea4c00f2d17d..7d3e129f69bfae2d6d705205b151efd13af03560 100644
--- a/Library/Homebrew/test/version_spec.rb
+++ b/Library/Homebrew/test/version_spec.rb
@@ -248,9 +248,9 @@ describe Version do
       end
 
       failure_message do |expected|
-        message = <<-EOS
-        expected: %s
-        detected: %s
+        message = <<~EOS
+          expected: %s
+          detected: %s
         EOS
         format(message, expected, detected)
       end
diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb
index 237ffa74e8266df68d27280cce84c186ed08703c..2211ed75b08107b050903ac684271e17a8e29006 100644
--- a/Library/Homebrew/utils.rb
+++ b/Library/Homebrew/utils.rb
@@ -93,7 +93,7 @@ def odeprecated(method, replacement = nil, disable: false, disable_on: nil, call
   end
   caller_message ||= backtrace[1]
 
-  message = <<-EOS.undent
+  message = <<~EOS
     Calling #{method} is #{verb}!
     #{replacement_message}
     #{caller_message}#{tap_message}
@@ -223,7 +223,7 @@ module Homebrew
     end
 
     return if which(executable)
-    odie <<-EOS.undent
+    odie <<~EOS
       The '#{name}' gem is installed but couldn't find '#{executable}' in the PATH:
       #{ENV["PATH"]}
     EOS
@@ -347,7 +347,7 @@ def which_editor
   end
   editor ||= "/usr/bin/vim"
 
-  opoo <<-EOS.undent
+  opoo <<~EOS
     Using #{editor} because no editor was set in the environment.
     This may change in the future, so we recommend setting EDITOR,
     or HOMEBREW_EDITOR to your preferred text editor.
diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb
index df0811e9548e09e5f67578d3d0e3d7d9dd022579..f88d5240366c82ee1e58fbf7b2175a92d6baa2ca 100644
--- a/Library/Homebrew/utils/github.rb
+++ b/Library/Homebrew/utils/github.rb
@@ -16,7 +16,7 @@ module GitHub
 
   class RateLimitExceededError < Error
     def initialize(reset, error)
-      super <<-EOS.undent
+      super <<~EOS
         GitHub API Error: #{error}
         Try again in #{pretty_ratelimit_reset(reset)}, or create a personal access token:
           #{ALL_SCOPES_URL}
@@ -33,12 +33,12 @@ module GitHub
     def initialize(error)
       message = "GitHub #{error}\n"
       if ENV["HOMEBREW_GITHUB_API_TOKEN"]
-        message << <<-EOS.undent
+        message << <<~EOS
           HOMEBREW_GITHUB_API_TOKEN may be invalid or expired; check:
           #{Formatter.url("https://github.com/settings/tokens")}
         EOS
       else
-        message << <<-EOS.undent
+        message << <<~EOS
           The GitHub credentials in the macOS keychain may be invalid.
           Clear them with:
             printf "protocol=https\\nhost=github.com\\n" | git credential-osxkeychain erase
@@ -104,7 +104,7 @@ module GitHub
 
         case GitHub.api_credentials_type
         when :keychain
-          onoe <<-EOS.undent
+          onoe <<~EOS
             Your macOS keychain GitHub credentials do not have sufficient scope!
             Scopes they need: #{needed_human_scopes}
             Scopes they have: #{credentials_scopes}
@@ -112,7 +112,7 @@ module GitHub
             and then set HOMEBREW_GITHUB_API_TOKEN as the authentication method instead.
           EOS
         when :environment
-          onoe <<-EOS.undent
+          onoe <<~EOS
             Your HOMEBREW_GITHUB_API_TOKEN does not have sufficient scope!
             Scopes they need: #{needed_human_scopes}
             Scopes it has: #{credentials_scopes}
diff --git a/Library/Homebrew/utils/link.rb b/Library/Homebrew/utils/link.rb
index 095dba20915f964c394c798480b01e89fc77178d..96bfd602e3a5d377bc9465700cbb3862b35f52f1 100644
--- a/Library/Homebrew/utils/link.rb
+++ b/Library/Homebrew/utils/link.rb
@@ -22,7 +22,7 @@ module Utils
       end
 
       return if conflicts.empty?
-      onoe <<-EOS.undent
+      onoe <<~EOS
         Could not link:
         #{conflicts.join("\n")}
 
diff --git a/Library/Homebrew/utils/ruby.sh b/Library/Homebrew/utils/ruby.sh
index 9a3ab2e81880ecded678199dfcc32367ff44472c..b16531e9f083fc1c23c630d3eb71cfae7aebec1a 100644
--- a/Library/Homebrew/utils/ruby.sh
+++ b/Library/Homebrew/utils/ruby.sh
@@ -40,7 +40,7 @@ setup-ruby-path() {
         ruby_old_version="$("$HOMEBREW_RUBY_PATH" -rrubygems -e "puts Gem::Version.new('$minimum_ruby_version') > Gem::Version.new(RUBY_VERSION)")"
       fi
 
-      if [[ "$ruby_old_version" == "true" || -n "$HOMEBREW_FORCE_VENDOR_RUBY" ]]
+      if [[ -z "$HOMEBREW_RUBY_PATH" || "$ruby_old_version" == "true" || -n "$HOMEBREW_FORCE_VENDOR_RUBY" ]]
       then
         brew vendor-install ruby
         if [[ ! -x "$vendor_ruby_path" ]]
diff --git a/docs/Manpage.md b/docs/Manpage.md
index f9299114796ed75f8ac0648208f638a993a57a79..59969a3e857aca01beb2dd6e7530d78de80a7d25 100644
--- a/docs/Manpage.md
+++ b/docs/Manpage.md
@@ -770,7 +770,7 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note
     Additionally, the date used in new manpages will match those in the existing
     manpages (to allow comparison without factoring in the date).
 
-  * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] [`--warn-on-publish-failure`] `patch-source` [`patch-source`]:
+  * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] [`--warn-on-publish-failure`] [`--bintray-org=``bintray-org`] [`--test-bot-user=``test-bot-user`] `patch-source` [`patch-source`]:
 
     Gets a patch from a GitHub commit or pull request and applies it to Homebrew.
     Optionally, installs the formulae changed by the patch.
@@ -814,6 +814,12 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note
     If `--warn-on-publish-failure` was passed, do not exit if there's a
     failure publishing bottles on Bintray.
 
+    If `--bintray-org=``bintray-org` is passed, publish at the given Bintray
+    organisation.
+
+    If `--test-bot-user=``test-bot-user` is passed, pull the bottle block
+    commit from the specified user on GitHub.
+
   * `release-notes` [`--markdown`] [`previous_tag`] [`end_ref`]:
     Output the merged pull requests on Homebrew/brew between two Git refs.
     If no `previous_tag` is provided it defaults to the newest tag.
diff --git a/docs/Xcode.md b/docs/Xcode.md
index 21ad66f95dc1066b809cbbb8f3328c53f48350dc..18ced07ed0177e05102e4aa44c72c4053e291213 100644
--- a/docs/Xcode.md
+++ b/docs/Xcode.md
@@ -12,7 +12,8 @@ Tools available for your platform:
 | 10.9  | 6.2   | 6.2                |
 | 10.10 | 7.2.1 | 7.2                |
 | 10.11 | 8.2.1 | 8.2                |
-| 10.12 | 8.3.3 | 8.3                |
+| 10.12 | 9.0.1 | 9.0.1              |
+| 10.13 | 9.0.1 | 9.0.1              |
 
 ## Compiler version database
 
@@ -73,6 +74,8 @@ Tools available for your platform:
 | 8.3.1 | —       | —       | —            | —          | 8.1 (802.0.41)  | —          |
 | 8.3.2 | —       | —       | —            | —          | 8.1 (802.0.42)  | —          |
 | 8.3.3 | —       | —       | —            | —          | 8.1 (802.0.42)  | —          |
+| 9.0.0 | —       | —       | —            | —          | 9.0 (900.0.37)  | —          |
+| 9.0.1 | —       | —       | —            | —          | 9.0 (900.0.38)  | —          |
 
 ## References to Xcode and compiler versions in code
 When a new Xcode release is made, the following things need to be
diff --git a/manpages/brew.1 b/manpages/brew.1
index b3d84027521d8906ab68ba4dadf46525d6004ea5..69ee416d7236dc81e8e71ac2482c4c4e13d9eedc 100644
--- a/manpages/brew.1
+++ b/manpages/brew.1
@@ -797,7 +797,7 @@ Generate Homebrew\'s manpages\.
 If \fB\-\-fail\-if\-changed\fR is passed, the command will return a failing status code if changes are detected in the manpage outputs\. This can be used for CI to be notified when the manpages are out of date\. Additionally, the date used in new manpages will match those in the existing manpages (to allow comparison without factoring in the date)\.
 .
 .TP
-\fBpull\fR [\fB\-\-bottle\fR] [\fB\-\-bump\fR] [\fB\-\-clean\fR] [\fB\-\-ignore\-whitespace\fR] [\fB\-\-resolve\fR] [\fB\-\-branch\-okay\fR] [\fB\-\-no\-pbcopy\fR] [\fB\-\-no\-publish\fR] [\fB\-\-warn\-on\-publish\-failure\fR] \fIpatch\-source\fR [\fIpatch\-source\fR]:
+\fBpull\fR [\fB\-\-bottle\fR] [\fB\-\-bump\fR] [\fB\-\-clean\fR] [\fB\-\-ignore\-whitespace\fR] [\fB\-\-resolve\fR] [\fB\-\-branch\-okay\fR] [\fB\-\-no\-pbcopy\fR] [\fB\-\-no\-publish\fR] [\fB\-\-warn\-on\-publish\-failure\fR] [\fB\-\-bintray\-org=\fR\fIbintray\-org\fR] [\fB\-\-test\-bot\-user=\fR\fItest\-bot\-user\fR] \fIpatch\-source\fR [\fIpatch\-source\fR]:
 .
 .IP
 Gets a patch from a GitHub commit or pull request and applies it to Homebrew\. Optionally, installs the formulae changed by the patch\.
@@ -844,6 +844,12 @@ If \fB\-\-no\-publish\fR is passed, do not publish bottles to Bintray\.
 .IP
 If \fB\-\-warn\-on\-publish\-failure\fR was passed, do not exit if there\'s a failure publishing bottles on Bintray\.
 .
+.IP
+If \fB\-\-bintray\-org=\fR\fIbintray\-org\fR is passed, publish at the given Bintray organisation\.
+.
+.IP
+If \fB\-\-test\-bot\-user=\fR\fItest\-bot\-user\fR is passed, pull the bottle block commit from the specified user on GitHub\.
+.
 .TP
 \fBrelease\-notes\fR [\fB\-\-markdown\fR] [\fIprevious_tag\fR] [\fIend_ref\fR]
 Output the merged pull requests on Homebrew/brew between two Git refs\. If no \fIprevious_tag\fR is provided it defaults to the newest tag\. If no \fIend_ref\fR is provided it defaults to \fBorigin/master\fR\.