From 8b4b48bc2d22f6912e29f287cd6149cbb3381c05 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Thu, 26 Jun 2025 13:36:25 +0200 Subject: [PATCH 1/3] Add instructions and a script for hosting Office Hours. --- content/wiki/office-hours.md | 4 +- content/wiki/preparing_office_hours.md | 107 +++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 content/wiki/preparing_office_hours.md diff --git a/content/wiki/office-hours.md b/content/wiki/office-hours.md index 4947229d..92aa6c54 100644 --- a/content/wiki/office-hours.md +++ b/content/wiki/office-hours.md @@ -1,10 +1,12 @@ +++ -title = "Office hours" +title = "Office Hours" +++ Linebender holds weekly office hours, open to anyone interested in following the project. The best place to find more information is the [#office-hours] stream on the Zulip chat. The invite is also available through [Google Calendar]. A [Google drive folder](https://drive.google.com/drive/folders/1mmrRnlpYb3j5P0ewAOcY_zj2tTH5u5aO) holds the archives of the notes. +If you'd like to prepare and host the Office Hours call, see [this document](@/wiki/preparing_office_hours.md). + [#office-hours]: https://xi.zulipchat.com/#narrow/stream/359642-office-hours [Google Calendar]: https://calendar.google.com/calendar/event?action=TEMPLATE&tmeid=OWkzamUwZ3JyNmwwM2pkMW9qcWhldTMwNGpfMjAyMzA1MTFUMTUwMDAwWiBmMDE1ZGYyODAzY2UwNTQ2N2ZkODE1NTdiYWQ3Nzg2NzVlMWZlMDg3MGI5NTBjNTAxMDZkNWI0ZmViMjhhMTZhQGc&tmsrc=f015df2803ce05467fd81557bad778675e1fe0870b950c50106d5b4feb28a16a%40group.calendar.google.com&scp=ALL diff --git a/content/wiki/preparing_office_hours.md b/content/wiki/preparing_office_hours.md new file mode 100644 index 00000000..0e03c9de --- /dev/null +++ b/content/wiki/preparing_office_hours.md @@ -0,0 +1,107 @@ ++++ +title = "Preparing Office Hours" ++++ + +If you've volunteered to host Office Hours, the first step will be to create the agenda document. + +Create a copy of [this template file](https://docs.google.com/document/d/10wUKjCAEIvYCQLnp_QpdFrAXkO0sBnarG2abbI7mduQ/edit?tab=t.0) in the [Office Hours Google drive folder](https://drive.google.com/drive/folders/1mmrRnlpYb3j5P0ewAOcY_zj2tTH5u5aO) (you may need additional permissions). + +You then need to fill out the sections about the different projects, listing the important changes. + +You may want to use a script like the following to programmatically get a list of recently-changed issues and PRs: + +```js +// Copyright 2025 the Linebender Authors +// SPDX-License-Identifier: Apache-2.0 + +// Create new token at https://github.com/settings/tokens/new +// The token is necessary because otherwise you're likely to hit rate limits +const token = ""; + +const repos = [ + "linebender/xilem", + "linebender/vello", + "linebender/parley", + "rust-mobile/android-view", + "AccessKit/accesskit", + "linebender/interpoli", + "linebender/peniko", + "linebender/kurbo", + "linebender/color", + "linebender/kompari", + "linebender/tiny-skia", + "linebender/simplecss", + "linebender/svgtypes", + "linebender/resvg", + "linebender/bevy_vello", + "linebender/velato", + "linebender/vello_svg", + "linebender/linebender.github.io", +]; + +const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); + +for (const repo of repos) { + const url = + `https://api.github.com/repos/${repo}/issues?since=${since}&state=all&per_page=100`; + + const res = await fetch(url, { + headers: { + "Authorization": `token ${token}`, + "Accept": "application/vnd.github.v3+json", + }, + }); + + if (!res.ok) { + console.error(`Error: ${res.status} ${res.statusText}`); + Deno.exit(1); + } + + const issues = await res.json(); + + const results = await Promise.all(issues.map(async (issue) => { + const isPR = !!issue.pull_request; + const createdRecently = new Date(issue.created_at) >= new Date(since); + + let label = "ACTIVITY"; + + if (!isPR && createdRecently) { + label = "open issue"; + } else if (isPR) { + // Fetch PR details to determine draft/merged status + const prRes = await fetch(issue.pull_request.url, { + headers: { + "Authorization": `token ${token}`, + "Accept": "application/vnd.github.v3+json", + }, + }); + + if (prRes.ok) { + const pr = await prRes.json(); + if (pr.merged_at && new Date(pr.merged_at) >= new Date(since)) { + label = "merged"; + } else if (pr.draft && createdRecently) { + label = "draft"; + } else if (!pr.draft && createdRecently) { + label = "awaiting review"; + } + } + } + + return `[#${issue.number}](${issue.html_url}) (${label}) - ${issue.title}`; + })); + + console.log(`=== REPO: ${repo} ===`); + for (const line of results.reverse()) { + console.log(line); + } + console.log(""); +} +``` + +Once this is done, follow the checklist at the bottom of the template. +It includes tasks like changing the links, making the document public, linking to it on Zulip, etc. + +The last step is to connect to the designated Google Meet room (possibly one you've created yourself) and start hosting the discussions. + +Have fun! From fff49ba7fa9ae06f6d6d73a8b6c1120392f047ee Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Sat, 5 Jul 2025 12:42:17 +0200 Subject: [PATCH 2/3] Update script --- content/wiki/preparing_office_hours.md | 108 ++++++++++++++++--------- 1 file changed, 71 insertions(+), 37 deletions(-) diff --git a/content/wiki/preparing_office_hours.md b/content/wiki/preparing_office_hours.md index 0e03c9de..2ad0a410 100644 --- a/content/wiki/preparing_office_hours.md +++ b/content/wiki/preparing_office_hours.md @@ -8,9 +8,12 @@ Create a copy of [this template file](https://docs.google.com/document/d/10wUKjC You then need to fill out the sections about the different projects, listing the important changes. -You may want to use a script like the following to programmatically get a list of recently-changed issues and PRs: +We've written a Deno script to automate the process, but you could also collect it manually using [Github's "pulse" pages](https://github.com/linebender/vello/pulse) or write your own script. +This programmatically fetches a list of recently-changed issues and PRs: ```js +// collect-issues.mjs + // Copyright 2025 the Linebender Authors // SPDX-License-Identifier: Apache-2.0 @@ -41,54 +44,81 @@ const repos = [ const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); +const headers = { + "User-Agent": "linebender-scrapping-script", + "Authorization": `token ${token}`, + "X-GitHub-Api-Version": "2022-11-28", + "Accept": "application/vnd.github+json", +}; + for (const repo of repos) { - const url = - `https://api.github.com/repos/${repo}/issues?since=${since}&state=all&per_page=100`; - - const res = await fetch(url, { - headers: { - "Authorization": `token ${token}`, - "Accept": "application/vnd.github.v3+json", - }, - }); - - if (!res.ok) { - console.error(`Error: ${res.status} ${res.statusText}`); - Deno.exit(1); + let issues = []; + let page = 1; + + while (true) { + // See https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues + const url = + `https://api.github.com/repos/${repo}/issues?since=${since}&state=all&per_page=100&page=${page}`; + + const res = await fetch(url, { headers }); + + if (!res.ok) { + throw new Error(`Error: ${res.status} ${res.statusText}`); + } + + let pageItems = await res.json(); + issues.push(...pageItems); + + if (pageItems.length < 100) + break; } - const issues = await res.json(); + const repoName = repo.split("/").at(-1); const results = await Promise.all(issues.map(async (issue) => { - const isPR = !!issue.pull_request; + const isPR = issue.pull_request != null; + const isOpen = issue.state == "open"; const createdRecently = new Date(issue.created_at) >= new Date(since); - let label = "ACTIVITY"; + let label; + + // ISSUE + if (!isPR) { + label = "[ISSUE ACTIVITY]"; + if (createdRecently) { + label = "(open issue)"; + } else if (!isOpen) { + label = "(issue closed)"; + } + } + + // PR + else { + label = "[PR ACTIVITY]"; - if (!isPR && createdRecently) { - label = "open issue"; - } else if (isPR) { // Fetch PR details to determine draft/merged status - const prRes = await fetch(issue.pull_request.url, { - headers: { - "Authorization": `token ${token}`, - "Accept": "application/vnd.github.v3+json", - }, - }); - - if (prRes.ok) { - const pr = await prRes.json(); - if (pr.merged_at && new Date(pr.merged_at) >= new Date(since)) { - label = "merged"; - } else if (pr.draft && createdRecently) { - label = "draft"; - } else if (!pr.draft && createdRecently) { - label = "awaiting review"; - } + const prRes = await fetch(issue.pull_request.url, { headers }); + + if (!prRes.ok) { + throw new Error(`Error fetching PR #${issue.number}: ${prRes.status} ${prRes.statusText}`); + } + + const pr = await prRes.json(); + if (pr.merged_at && new Date(pr.merged_at) >= new Date(since)) { + label = "🎉"; + } else if (!isOpen && !createdRecently) { + label = "(pr closed)"; + } else if (pr.draft && isOpen && createdRecently) { + label = "(draft)"; + } else if (!pr.draft && isOpen && createdRecently) { + label = "(awaiting review)"; } } - return `[#${issue.number}](${issue.html_url}) (${label}) - ${issue.title}`; + // The logic above should leave a lot of items with a label + // of [ISSUE/PR ACTIVITY]. These should be investigated manually. + + return `[${repoName}#${issue.number}](${issue.html_url}) ${label} - ${issue.title}`; })); console.log(`=== REPO: ${repo} ===`); @@ -99,6 +129,10 @@ for (const repo of repos) { } ``` +Note that the script shows you virtually all issues/PR which saw activity in the last seven days. +Some of them may not be worth mentioning, or have already been mentioned in previous meetings, etc. +You should still do some manual sorting with the results. + Once this is done, follow the checklist at the bottom of the template. It includes tasks like changing the links, making the document public, linking to it on Zulip, etc. From 1756b8bf2a64339d73de6191c965e153a0e9a181 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Sat, 5 Jul 2025 12:45:50 +0200 Subject: [PATCH 3/3] Added error message for unset token --- content/wiki/preparing_office_hours.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/content/wiki/preparing_office_hours.md b/content/wiki/preparing_office_hours.md index 2ad0a410..cca2cd69 100644 --- a/content/wiki/preparing_office_hours.md +++ b/content/wiki/preparing_office_hours.md @@ -19,7 +19,7 @@ This programmatically fetches a list of recently-changed issues and PRs: // Create new token at https://github.com/settings/tokens/new // The token is necessary because otherwise you're likely to hit rate limits -const token = ""; +const github_token = ""; const repos = [ "linebender/xilem", @@ -46,11 +46,18 @@ const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); const headers = { "User-Agent": "linebender-scrapping-script", - "Authorization": `token ${token}`, + "Authorization": `token ${github_token}`, "X-GitHub-Api-Version": "2022-11-28", "Accept": "application/vnd.github+json", }; +if (github_token == "") { + console.error("==="); + console.error("You need to set the value of `github_token` in the script!"); + console.error("==="); + throw new Error("Invalid token"); +} + for (const repo of repos) { let issues = []; let page = 1;