From cb99b3fa510fb9b4474bc71d4951b85dbbdc1eb1 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sun, 8 Nov 2026 07:57:02 +0000 Subject: [PATCH] Preserve past corporate sponsor acknowledgments --- gems/kettle-jem/lib/kettle/jem.rb | 49 ++++++++++++++----- .../config_metadata_template_spec.rb | 26 ++++++++-- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/gems/kettle-jem/lib/kettle/jem.rb b/gems/kettle-jem/lib/kettle/jem.rb index f98085488..e3260962e 100644 --- a/gems/kettle-jem/lib/kettle/jem.rb +++ b/gems/kettle-jem/lib/kettle/jem.rb @@ -6063,7 +6063,7 @@ def transfer_changelog_context(project_root, env: ENV) "feature.appraisals" => File.file?(File.join(project_root, "Appraisals")), "feature.rubyforum" => !rubyforum_facts(config, env, package_name: transfer_changelog_package_name(project_root)).empty?, "feature.rubyforum_project_tag" => !normalize_rubyforum_tag(project_tag).empty?, - "feature.corporate_sponsors" => sponsors.any?, + "feature.corporate_sponsors" => sponsors.any? { |sponsor| readme_corporate_sponsor_active?(sponsor, Date.today) || readme_corporate_sponsor_past?(sponsor, Date.today) }, "feature.organization_logo" => readme_top_logo_options(config).any? || readme_h2_synopsis_logo_options(config).any?, "feature.structuredmerge_driver" => config.fetch("git_drivers", "semantic-diff").to_s == "semantic-diff", "feature.dedicated_version_gem" => version_gem_mode == "dedicated" || File.file?(File.join(project_root, "lib", entrypoint, "version_gem.rb")), @@ -17882,10 +17882,39 @@ def readme_logo_facts(config, package_name:, github_org:, repository: {}) def readme_corporate_sponsors_facts(config, env) sponsors = readme_corporate_sponsors(config, env) + today = Date.today + active = sponsors.select { |sponsor| readme_corporate_sponsor_active?(sponsor, today) }.map do |sponsor| + next sponsor unless sponsor[:members] + + sponsor.merge(members: sponsor[:members].select { |member| readme_corporate_sponsor_active?(member, today) }) + end + past = sponsors.select { |sponsor| readme_corporate_sponsor_past?(sponsor, today) } + past_links = past.map do |sponsor| + link = "[#{sponsor.fetch(:name)}](#{sponsor.fetch(:url)})" + members = Array(sponsor[:members]).select { |member| !member[:since] || member[:since] <= today } + unless members.empty? + links = members.map { |member| "[#{member.fetch(:name)}](#{member.fetch(:url)})" }.join(", ") + link = "#{link} (backed by #{links})" + end + link + end + sponsors.each do |sponsor| + next unless readme_corporate_sponsor_active?(sponsor, today) + + members = Array(sponsor[:members]).select { |member| readme_corporate_sponsor_past?(member, today) } + next if members.empty? + + links = members.map { |member| "[#{member.fetch(:name)}](#{member.fetch(:url)})" }.join(", ") + past_links << "#{links} (formerly backing [#{sponsor.fetch(:name)}](#{sponsor.fetch(:url)}))" + end + detail = readme_corporate_sponsors_detail(active) + past_detail = past_links.empty? ? "" : "Past sponsors: #{past_links.join(', ')}" compact_hash( - entries: sponsors, - row: readme_corporate_sponsors_row(sponsors), - detail: readme_corporate_sponsors_detail(sponsors) + entries: active, + past_entries: past, + row: readme_corporate_sponsors_row(active), + detail: [detail, past_detail].reject(&:empty?).join("\n\n"), + past_detail: past_detail ) end @@ -17895,15 +17924,11 @@ def readme_corporate_sponsors(config, env) readme_config["corporate_sponsors"] || readme_config["sponsors"] ) inherited = normalize_readme_corporate_sponsors_json(env["KETTLE_JEM_CORPORATE_SPONSORS_JSON"]) - sponsors = deduplicate_readme_corporate_sponsors(configured + inherited) - today = Date.today - sponsors - .select { |sponsor| readme_corporate_sponsor_active?(sponsor, today) } - .map do |sponsor| - next sponsor unless sponsor[:members] + deduplicate_readme_corporate_sponsors(configured + inherited) + end - sponsor.merge(members: sponsor[:members].select { |member| readme_corporate_sponsor_active?(member, today) }) - end + def readme_corporate_sponsor_past?(sponsor, today) + (!sponsor[:since] || sponsor[:since] <= today) && sponsor[:until] && sponsor[:until] < today end def readme_corporate_sponsor_active?(sponsor, today) diff --git a/gems/kettle-jem/spec/integration/config_metadata_template_spec.rb b/gems/kettle-jem/spec/integration/config_metadata_template_spec.rb index f1dc701b7..df161cf04 100644 --- a/gems/kettle-jem/spec/integration/config_metadata_template_spec.rb +++ b/gems/kettle-jem/spec/integration/config_metadata_template_spec.rb @@ -1286,7 +1286,7 @@ end end - it "filters corporate sponsors by their since/until window" do + it "renders expired corporate sponsors separately and hides future sponsors" do tmp_root = File.expand_path("../tmp", __dir__) FileUtils.mkdir_p(tmp_root) Dir.mktmpdir("kettle-jem-readme-sponsor-window-slice", tmp_root) do |root| @@ -1330,6 +1330,7 @@ {KJ|README:CORPORATE_SPONSORS} ## 🦷 FLOSS Funding + {KJ|README:CORPORATE_SPONSORS_DETAIL} MARKDOWN }) @@ -1341,11 +1342,30 @@ expect(final_content).to include("Corporate sponsor:") expect(final_content).to include(%(Active Corp "Former Fund", "url" => "https://former.example", "img_src" => "https://former.example/logo.svg", "until" => yesterday, + "members" => [ + {"name" => "Former Member", "url" => "https://member.example", "until" => yesterday}, + {"name" => "Future Member", "url" => "https://future.example", "since" => tomorrow} + ] + }] + facts = described_class.send(:readme_corporate_sponsors_facts, {}, {"KETTLE_JEM_CORPORATE_SPONSORS_JSON" => JSON.generate(inherited)}) + + expect(facts[:row].to_s).to be_empty + expect(facts[:detail]).to include("Past sponsors: [Former Fund](https://former.example) (backed by [Former Member](https://member.example))") + expect(facts[:detail]).not_to include("Future Member") + expect(facts[:past_entries].size).to eq(1) + end + it "rejects corporate sponsor entries with invalid since/until dates" do expect { described_class.send( @@ -1410,7 +1430,7 @@ expect(final_content).to include( %(- Umbrella Fund [Umbrella Fund](https://umbrella.example) — backed by [Member One](https://one.example), [Member Two](https://two.example)) ) - expect(final_content).not_to include("Lapsed Member") + expect(final_content).to include("Past sponsors: [Lapsed Member](https://lapsed.example) (formerly backing [Umbrella Fund](https://umbrella.example))") expect(template_report.dig(:metadata, :template_tokens)).to include( "KJ|README:CORPORATE_SPONSORS_DETAIL" => a_string_including("backed by") )