From 8f0cdd3284771fdc05a8a638fc2659647e17a862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 14 Nov 2025 15:55:18 +0100 Subject: [PATCH 1/6] Add a funding page with people that have GitHub Sponsors --- locales/en-US/funding.ftl | 5 +++++ src/main.rs | 7 +++++-- src/render.rs | 26 ++++++++++++++++++++++---- src/teams.rs | 32 ++++++++++++++++++++++++++++++++ templates/funding.html.hbs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 locales/en-US/funding.ftl create mode 100644 templates/funding.html.hbs diff --git a/locales/en-US/funding.ftl b/locales/en-US/funding.ftl new file mode 100644 index 000000000..0cfd5b2e5 --- /dev/null +++ b/locales/en-US/funding.ftl @@ -0,0 +1,5 @@ +## Works in conjunction with team data from teams.ftl + +## funding.html.hbs +funding-page-title = Funding +funding-intro = Rust contributors shown on this page can be funded via GitHub Sponsors. Consider sponsoring them if you want to support the development of Rust. diff --git a/src/main.rs b/src/main.rs index 0605c061f..060fb60f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use crate::assets::compile_assets; use crate::i18n::{TeamHelper, create_loader}; use crate::redirect::create_redirects; -use crate::render::{RenderCtx, render_directory, render_governance, render_index}; +use crate::render::{RenderCtx, render_directory, render_funding, render_governance, render_index}; use crate::rust_version::fetch_rust_version; use crate::teams::{encode_zulip_stream, load_rust_teams}; use anyhow::Context; @@ -110,8 +110,11 @@ fn main() -> anyhow::Result<()> { ".well-known/security.txt", )?; + let all_team_members = ctx.teams.all_team_members(); + render_index(&ctx)?; - render_governance(&ctx)?; + render_governance(&ctx, &all_team_members)?; + render_funding(&ctx, &all_team_members)?; render_directory(&ctx, "community")?; render_directory(&ctx, "learn")?; render_directory(&ctx, "policies")?; diff --git a/src/render.rs b/src/render.rs index 4c6601dd2..554e9dff0 100644 --- a/src/render.rs +++ b/src/render.rs @@ -2,7 +2,7 @@ use crate::assets::AssetFiles; use crate::fs::{copy_dir_all, ensure_directory}; use crate::i18n::{EXPLICIT_LOCALE_INFO, LocaleInfo, SUPPORTED_LOCALES}; use crate::rust_version::RustVersion; -use crate::teams::{PageData, RustTeamData}; +use crate::teams::{AllTeamMembers, PageData, RustTeamData}; use crate::{BaseUrl, ENGLISH, LAYOUT}; use anyhow::Context; use handlebars::Handlebars; @@ -179,7 +179,10 @@ pub fn render_index(render_ctx: &RenderCtx) -> anyhow::Result<()> { }) } -pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> { +pub fn render_governance( + render_ctx: &RenderCtx, + all_team_members: &AllTeamMembers, +) -> anyhow::Result<()> { let data = render_ctx.teams.index_data(); // Index page @@ -226,13 +229,12 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> { })?; // Page with all team members - let all_team_members_data = render_ctx.teams.all_team_members(); for_all_langs("governance/people/index.html", |dst_path, lang| { render_ctx .page( "governance/all-team-members", "governance-all-team-members-title", - &all_team_members_data, + all_team_members, lang, ) .render(dst_path) @@ -263,6 +265,22 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> { Ok(()) } +pub fn render_funding( + render_ctx: &RenderCtx, + all_team_members: &AllTeamMembers, +) -> anyhow::Result<()> { + let data = render_ctx.teams.funding_data(all_team_members); + + // Index page + for_all_langs("funding/index.html", |dst_path, lang| { + render_ctx + .page("funding", "funding-page-title", &data, lang) + .render(dst_path) + })?; + + Ok(()) +} + /// Render all templates found in the given directory. pub fn render_directory(render_ctx: &RenderCtx, category: &str) -> anyhow::Result<()> { for dir in std::fs::read_dir(render_ctx.template_dir.join(category))? { diff --git a/src/teams.rs b/src/teams.rs index 16b66003b..00af52bc5 100644 --- a/src/teams.rs +++ b/src/teams.rs @@ -346,6 +346,27 @@ impl RustTeamData { people } + pub fn funding_data(&self, all_team_members: &AllTeamMembers) -> FundingData { + let people_with_sponsors = all_team_members + .active + .iter() + .filter_map(|member| { + let person = self.people.get(&member.github)?; + if person.github_sponsors { + Some(FundablePerson { + name: member.name.clone(), + github: member.github.to_string(), + }) + } else { + None + } + }) + .collect(); + FundingData { + people: people_with_sponsors, + } + } + fn get_toplevel_team_url<'a>(&'a self, mut team: &'a Team) -> Option { while !is_toplevel_team(team) { let Some(parent) = &team.subteam_of else { @@ -476,6 +497,17 @@ pub struct PersonData { alumni_teams: Vec, } +#[derive(Serialize)] +pub struct FundablePerson { + name: String, + github: String, +} + +#[derive(Serialize)] +pub struct FundingData { + people: Vec, +} + pub fn load_rust_teams() -> anyhow::Result { println!("Downloading Team API data"); diff --git a/templates/funding.html.hbs b/templates/funding.html.hbs new file mode 100644 index 000000000..504137574 --- /dev/null +++ b/templates/funding.html.hbs @@ -0,0 +1,31 @@ +{{#*inline "member"}} + +{{/inline}} + +{{#*inline "page"}} +
+
+

{{fluent "funding-intro"}}

+
+
+ +
+
+ {{#each data.people as |member|}} + {{> member member=member baseurl=../baseurl }} + {{/each}} +
+
+ +{{/inline}} +{{~> (lookup this "parent")~}} From cb00f44e8f7b75eda591fe2a0dd8f9a69fb41690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 14 Nov 2025 16:07:11 +0100 Subject: [PATCH 2/6] Randomize the order of people on the funding page --- templates/components/layout.html.hbs | 1 + templates/funding.html.hbs | 32 ++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/templates/components/layout.html.hbs b/templates/components/layout.html.hbs index 3b4ed812e..5dde6df02 100644 --- a/templates/components/layout.html.hbs +++ b/templates/components/layout.html.hbs @@ -69,5 +69,6 @@ {{> components/footer}} + {{#> script }}{{/script}} diff --git a/templates/funding.html.hbs b/templates/funding.html.hbs index 504137574..68275793d 100644 --- a/templates/funding.html.hbs +++ b/templates/funding.html.hbs @@ -1,5 +1,5 @@ {{#*inline "member"}} -
+
-
+
{{#each data.people as |member|}} {{> member member=member baseurl=../baseurl }} {{/each}} @@ -28,4 +28,32 @@
{{/inline}} + +{{#*inline "script"}} + +{{/inline}} {{~> (lookup this "parent")~}} From 9d9f38c91b08bd76384a772094d27abc613142be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 14 Nov 2025 16:11:51 +0100 Subject: [PATCH 3/6] Add a link from the "All Project members" page to the "Funding" page --- templates/governance/all-team-members.html.hbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/governance/all-team-members.html.hbs b/templates/governance/all-team-members.html.hbs index 2aec52cf8..6f4f0d4d2 100644 --- a/templates/governance/all-team-members.html.hbs +++ b/templates/governance/all-team-members.html.hbs @@ -20,6 +20,9 @@

{{#fluent "governance-all-team-members-intro"}}{{#fluentparam "count"}}{{len data.active}}{{/fluentparam}}{{/fluent}}

+
From d92abfd1471163e27b83e5b9fdc4b6ed7fe2cc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 15 Nov 2025 21:43:19 +0100 Subject: [PATCH 4/6] Remove script inline --- templates/components/layout.html.hbs | 1 - templates/funding.html.hbs | 50 +++++++++++++--------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/templates/components/layout.html.hbs b/templates/components/layout.html.hbs index 5dde6df02..3b4ed812e 100644 --- a/templates/components/layout.html.hbs +++ b/templates/components/layout.html.hbs @@ -69,6 +69,5 @@ {{> components/footer}} - {{#> script }}{{/script}} diff --git a/templates/funding.html.hbs b/templates/funding.html.hbs index 68275793d..a95eaa2de 100644 --- a/templates/funding.html.hbs +++ b/templates/funding.html.hbs @@ -25,35 +25,33 @@ {{> member member=member baseurl=../baseurl }} {{/each}}
- - -{{/inline}} - -{{#*inline "script"}} - + document.addEventListener("DOMContentLoaded", () => { + // Shuffle people to reduce ordering bias + const wrapper = document.querySelector("#people"); + const children = Array(...wrapper.children); + shuffle(children); + wrapper.replaceChildren(...children); + }); + + + {{/inline}} + {{~> (lookup this "parent")~}} From 981f33f56245358e0bdbe11f6612531bba1ad4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 16 Nov 2025 15:14:12 +0100 Subject: [PATCH 5/6] Build tools-install into a separate script --- src/assets.rs | 30 ++++++++++++++-------- templates/components/tools/rustup.html.hbs | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/assets.rs b/src/assets.rs index 8836a66c0..90aef0155 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -56,36 +56,44 @@ fn concat_files( files: &[&str], directory: &str, extension: &str, + prefix: &str, ) -> anyhow::Result { let mut concatted = String::new(); for filestem in files { - let vendor_path = root_dir + let file_path = root_dir .join("static") .join(directory) .join(format!("{filestem}.{extension}")); - let contents = fs::read_to_string(vendor_path) - .with_context(|| anyhow::anyhow!("couldn't read vendor {extension}"))?; + let contents = fs::read_to_string(file_path) + .with_context(|| anyhow::anyhow!("couldn't read {prefix} {extension}"))?; concatted.push_str(&contents); } - let file_sha = format!("vendor_{}", hash_string(&concatted)); + let file_sha = format!("{prefix}_{}", hash_string(&concatted)); let out_file_path = out_dir .join("static") .join(directory) .join(format!("{file_sha}.{extension}")); write_file(Path::new(&out_file_path), concatted.as_bytes()) - .with_context(|| anyhow::anyhow!("couldn't write vendor {extension}"))?; + .with_context(|| anyhow::anyhow!("couldn't write {prefix} {extension}"))?; relative_url(&out_file_path, out_dir) } fn concat_vendor_css(root_dir: &Path, out_dir: &Path, files: Vec<&str>) -> anyhow::Result { - concat_files(root_dir, out_dir, &files, "styles", "css") + concat_files(root_dir, out_dir, &files, "styles", "css", "vendor") } -fn concat_app_js(root_dir: &Path, out_dir: &Path, files: Vec<&str>) -> anyhow::Result { - concat_files(root_dir, out_dir, &files, "scripts", "js") +fn build_js_file(root_dir: &Path, out_dir: &Path, file: &str) -> anyhow::Result { + concat_files( + root_dir, + out_dir, + &[file], + "scripts", + "js", + Path::new(file).file_stem().unwrap().to_str().unwrap(), + ) } #[derive(Serialize, Debug)] @@ -97,7 +105,7 @@ pub struct CSSFiles { #[derive(Serialize, Debug)] pub struct JSFiles { - app: String, + tools_install: String, } #[derive(Serialize, Debug)] @@ -116,7 +124,7 @@ pub fn compile_assets( let app_css_file = compile_sass(root_dir, out_dir, "app", base_url)?; let fonts_css_file = compile_sass(root_dir, out_dir, "fonts", base_url)?; let vendor_css_file = concat_vendor_css(root_dir, out_dir, vec!["tachyons"])?; - let app_js_file = concat_app_js(root_dir, out_dir, vec!["tools-install"])?; + let tools_install_js = build_js_file(root_dir, out_dir, "tools-install")?; Ok(AssetFiles { css: CSSFiles { @@ -125,7 +133,7 @@ pub fn compile_assets( vendor: format!("{base_url}/{vendor_css_file}"), }, js: JSFiles { - app: format!("{base_url}/{app_js_file}"), + tools_install: format!("{base_url}/{tools_install_js}"), }, }) } diff --git a/templates/components/tools/rustup.html.hbs b/templates/components/tools/rustup.html.hbs index d1f5660d5..fc7e909b1 100644 --- a/templates/components/tools/rustup.html.hbs +++ b/templates/components/tools/rustup.html.hbs @@ -38,4 +38,4 @@ - + From c099a6f254e02abd2a464c6973f6303391f245b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 16 Nov 2025 15:16:35 +0100 Subject: [PATCH 6/6] Add a separate JS file for shuffling the funding page --- src/assets.rs | 3 +++ static/scripts/funding-shuffle.js | 23 +++++++++++++++++++++++ templates/funding.html.hbs | 27 +-------------------------- 3 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 static/scripts/funding-shuffle.js diff --git a/src/assets.rs b/src/assets.rs index 90aef0155..9c327a7dd 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -106,6 +106,7 @@ pub struct CSSFiles { #[derive(Serialize, Debug)] pub struct JSFiles { tools_install: String, + funding_shuffle: String, } #[derive(Serialize, Debug)] @@ -125,6 +126,7 @@ pub fn compile_assets( let fonts_css_file = compile_sass(root_dir, out_dir, "fonts", base_url)?; let vendor_css_file = concat_vendor_css(root_dir, out_dir, vec!["tachyons"])?; let tools_install_js = build_js_file(root_dir, out_dir, "tools-install")?; + let funding_shuffle_js = build_js_file(root_dir, out_dir, "funding-shuffle")?; Ok(AssetFiles { css: CSSFiles { @@ -134,6 +136,7 @@ pub fn compile_assets( }, js: JSFiles { tools_install: format!("{base_url}/{tools_install_js}"), + funding_shuffle: format!("{base_url}/{funding_shuffle_js}"), }, }) } diff --git a/static/scripts/funding-shuffle.js b/static/scripts/funding-shuffle.js new file mode 100644 index 000000000..09d9954b9 --- /dev/null +++ b/static/scripts/funding-shuffle.js @@ -0,0 +1,23 @@ +// From https://stackoverflow.com/a/2450976/1107768 +function shuffle(array) { + let currentIndex = array.length; + + // While there remain elements to shuffle... + while (currentIndex !== 0) { + // Pick a remaining element... + let randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], array[currentIndex]]; + } +} + +document.addEventListener("DOMContentLoaded", () => { + // Shuffle people to reduce ordering bias + const wrapper = document.querySelector("#people"); + const children = Array(...wrapper.children); + shuffle(children); + wrapper.replaceChildren(...children); +}); diff --git a/templates/funding.html.hbs b/templates/funding.html.hbs index a95eaa2de..62d9a9e84 100644 --- a/templates/funding.html.hbs +++ b/templates/funding.html.hbs @@ -25,33 +25,8 @@ {{> member member=member baseurl=../baseurl }} {{/each}} - + - {{/inline}} {{~> (lookup this "parent")~}}