diff --git a/Library/Homebrew/formula_auditor.rb b/Library/Homebrew/formula_auditor.rb index d555e99d22..1b17af76c6 100644 --- a/Library/Homebrew/formula_auditor.rb +++ b/Library/Homebrew/formula_auditor.rb @@ -612,11 +612,10 @@ module Homebrew metadata = SharedAudits.eol_data(name, formula.version.major.to_s) metadata ||= SharedAudits.eol_data(name, formula.version.major_minor.to_s) - return if metadata.blank? || (eol = metadata["eol"]).blank? + return if metadata.blank? || (metadata.dig("result", "isEol") != true) - is_eol = eol == true - is_eol ||= eol.is_a?(String) && (eol_date = Date.parse(eol)) <= Date.today - return unless is_eol + eol_from = metadata.dig("result", "eolFrom") + eol_date = Date.parse(eol_from) if eol_from.present? message = "Product is EOL" message += " since #{eol_date}" if eol_date.present? diff --git a/Library/Homebrew/test/utils/shared_audits_spec.rb b/Library/Homebrew/test/utils/shared_audits_spec.rb index 995fe9c79a..9d95b84ea9 100644 --- a/Library/Homebrew/test/utils/shared_audits_spec.rb +++ b/Library/Homebrew/test/utils/shared_audits_spec.rb @@ -1,8 +1,58 @@ # frozen_string_literal: true require "utils/shared_audits" +require "utils/curl" RSpec.describe SharedAudits do + let(:eol_json_text) do + <<~JSON + { + "schema_version" : "1.0.0", + "generated_at": "2025-01-02T01:23:45+00:00", + "result": { + "name": "1.2", + "codename": null, + "label": "1.2", + "releaseDate": "2024-01-01", + "isLts": false, + "ltsFrom": null, + "isEol": true, + "eolFrom": "2025-01-01", + "isMaintained": false, + "latest": { + "name": "1.0.0", + "date": "2024-01-01", + "link": "https://example.com/1.0.0" + } + } + } + JSON + end + + def mock_curl_output(stdout: "", success: true) + status = instance_double(Process::Status, success?: success) + curl_output = instance_double(SystemCommand::Result, stdout:, status:) + allow(Utils::Curl).to receive(:curl_output).and_return curl_output + end + + describe "::eol_data" do + it "returns a parsed JSON object if the product is found" do + mock_curl_output stdout: eol_json_text + expect(described_class.eol_data("product", "cycle")&.dig("result", "isEol")).to be(true) + expect(described_class.eol_data("product", "cycle")&.dig("result", "eolFrom")).to eq("2025-01-01") + end + + it "returns nil if the product is not found" do + mock_curl_output stdout: "" + expect(described_class.eol_data("none", "cycle")).to be_nil + end + + it "returns nil if api call fails" do + mock_curl_output success: false + expect(described_class.eol_data("", "")).to be_nil + end + end + describe "::github_tag_from_url" do it "finds tags in archive urls" do url = "https://github.com/a/b/archive/refs/tags/v1.2.3.tar.gz" diff --git a/Library/Homebrew/utils/shared_audits.rb b/Library/Homebrew/utils/shared_audits.rb index 9ba8c9f9ac..4dec1963cc 100644 --- a/Library/Homebrew/utils/shared_audits.rb +++ b/Library/Homebrew/utils/shared_audits.rb @@ -11,11 +11,19 @@ module SharedAudits sig { params(product: String, cycle: String).returns(T.nilable(T::Hash[String, T.untyped])) } def self.eol_data(product, cycle) @eol_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped])) - @eol_data["#{product}/#{cycle}"] ||= begin - result = Utils::Curl.curl_output("--location", "https://endoflife.date/api/#{product}/#{cycle}.json") - json = JSON.parse(result.stdout) if result.status.success? - json = nil if json&.dig("message")&.include?("Product not found") - json + key = "#{product}/#{cycle}" + return @eol_data[key] if @eol_data.key?(key) + + result = Utils::Curl.curl_output( + "--location", + "https://endoflife.date/api/v1/products/#{product}/releases/#{cycle}", + ) + return unless result.status.success? + + @eol_data[key] = begin + JSON.parse(result.stdout) + rescue JSON::ParserError + nil end end