From dbcf334a07cf62d8580d80c9b962383abb857ce5 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 14 Jul 2025 14:37:13 +0100 Subject: [PATCH] Check source state of fontc inputs --- .ci/dashboard/src/data/fontc_review.json.js | 14 ++ .ci/dashboard/src/data/metadata.json.py | 2 +- .ci/dashboard/src/source_check.md | 170 ++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 .ci/dashboard/src/data/fontc_review.json.js create mode 100644 .ci/dashboard/src/source_check.md diff --git a/.ci/dashboard/src/data/fontc_review.json.js b/.ci/dashboard/src/data/fontc_review.json.js new file mode 100644 index 000000000..e68c308d8 --- /dev/null +++ b/.ci/dashboard/src/data/fontc_review.json.js @@ -0,0 +1,14 @@ +const sheet = await fetch( + "https://docs.google.com/spreadsheets/d/1ao3k56FwQy6W0Ll5QbU_wpuKEvNPYcn8YyEU9_L8O4Q/gviz/tq?tqx=out:json&tq" +); +const text = await sheet.text(); +const json = JSON.parse(text.substr(47).slice(0, -2)); +var families = {}; +for (const row of json.table.rows) { + const family = row.c[0]?.v.split("/")?.pop(); + const reviewed = row.c[3]?.v; + if (family && reviewed) { + families[family] = reviewed; + } +} +console.log(JSON.stringify(families)); diff --git a/.ci/dashboard/src/data/metadata.json.py b/.ci/dashboard/src/data/metadata.json.py index c73502b54..ad6c4fb33 100644 --- a/.ci/dashboard/src/data/metadata.json.py +++ b/.ci/dashboard/src/data/metadata.json.py @@ -10,7 +10,7 @@ from gftools.push.items import parse_html repo_path = os.environ["GF_REPO_PATH"] metadata = {} -for directory in glob(os.path.join(repo_path, "ofl", "*")): +for directory in list(glob(os.path.join(repo_path, "ofl", "*"))) + list(glob(os.path.join(repo_path, "apache", "*"))): if not os.path.isdir(directory): continue diff --git a/.ci/dashboard/src/source_check.md b/.ci/dashboard/src/source_check.md new file mode 100644 index 000000000..d3d855f51 --- /dev/null +++ b/.ci/dashboard/src/source_check.md @@ -0,0 +1,170 @@ +--- +title: Fontc crater sources check +toc: false +--- + +```js +const fontc = await FileAttachment("./data/fontc.json").json(); +const metadata = await FileAttachment("./data/metadata.json").json(); +const servers = await FileAttachment("./data/servers.json").json(); +const github = await FileAttachment("./data/github.json").json(); +const reviewlist = await FileAttachment("./data/fontc_review.json").json(); + +const family_to_directory = Object.fromEntries( + Object.entries(metadata).map(([k, v]) => [v.name, k]) +); +const directory_to_family = Object.fromEntries( + Object.entries(metadata).map(([k, v]) => [k, v.name]) +); + +function fontcKeyToGithubAndSha(key, state) { + let match = key.match( + /^([^\/]+)\/([^\/]+)\/(\S+)\?([a-f0-9]+) \((\S+)\) (\S+)/ + ); + if (match) { + let [_, owner, repo, path, sha, config, mode] = match; + return { repo: `${owner}/${repo}`, sha, state }; + } +} + +let fontcResults = []; +for (let successKey of Object.keys(fontc.lastRun.success)) { + let githubAndSha = fontcKeyToGithubAndSha(successKey, "success"); + if (githubAndSha) { + fontcResults.push(githubAndSha); + } +} +for (let failureKey of Object.keys(fontc.lastRun.failure)) { + let githubAndSha = fontcKeyToGithubAndSha(failureKey, "failure"); + if (githubAndSha) { + fontcResults.push(githubAndSha); + } +} + +const summary = Object.keys(metadata).map((directory) => { + const md = metadata[directory]; + const family = md.name; + let source_state = "Good"; + if (!md.source) { + source_state = "No source information"; + } else if (!md.source.repositoryUrl) { + source_state = "Missing repository URL"; + } else if (!md.source.commit) { + source_state = "Missing SHA"; + } else if (!md.source.configYaml) { + source_state = "Missing config.yaml"; + } else if (!md.source.commit.match(/^[0-9a-f]{40}$/)) { + source_state = "Invalid commit hash"; + } + let github = + md.source?.repositoryUrl && + md.source.repositoryUrl.match(/github.com\/([^/]+\/[^/]+)/)?.[1]; + let fontcResult = fontcResults.find((r) => r.repo === github); + let fontcState = "unknown"; + if (fontcResult) { + fontcState = fontcResult.state; + } + if (source_state != "Good" && fontcState == "unknown") { + fontcState = "insufficient source data: " + source_state; + } + return { + directory, + family, + slug: + source_state == "Good" + ? github + "/" + md.source?.configYaml + "@" + md.source?.commit + : "", + github, + sha: md.source?.commit, + source_state, + fontc_state: fontcState, + reviewed: reviewlist[directory], + }; +}); + +let successes = summary.filter((s) => s.fontc_state == "success"); +let failures = summary.filter((s) => s.fontc_state == "failure"); +let insufficient = summary.filter((s) => + s.fontc_state.startsWith("insufficient source data") +); +let unknown = summary.filter((s) => s.fontc_state == "unknown"); +let reviewed = summary.filter((s) => s.reviewed == "yes"); +``` + +
+

Total families: ${ summary.length }

+

Ran: ${ successes.length } + ${ failures.length} = ${ successes.length + failures.length }

+

Insufficient source data: ${ insufficient.length }
+ Unaccounted: ${ unknown.length }

+

Reviewed: ${ reviewed.length } (${ Math.floor(reviewed.length / summary.length * 100) }%)

+
+ +```js +const domain = ["success", "failure", "insufficient source data", "unknown"]; +display( + Plot.plot({ + y: { percent: true }, + color: { legend: true }, + marks: [ + Plot.waffleY( + summary, + Plot.groupX( + { y: "count" }, + { + x: 0, + fill: (d) => + d.fontc_state.replace( + /insufficient source data: (.+)/, + "insufficient source data" + ), + offset: "normalize", + order: domain, + } + ) + ), + ], + width: 500, + height: 400, + color: { + domain, + range: ["#1a821aff", "#8d2929ff", "#4ee5f9ff", "#aaaaaa"], + legend: true, + }, + }) +); +``` + +## Details + +```js +display( + Inputs.table(summary, { + rows: 200, + columns: ["directory", "family", "slug", "fontc_state", "reviewed"], + sort: "fontc_state", + reverse: true, + header: { + directory: "Directory", + family: "Family name", + slug: "Source", + fontc_state: "Fontc state", + reviewed: "Reviewed", + }, + format: { + fontc_state: (d) => { + if (d == "success") { + return html`${d}`; + } else if (d == "failure") { + return html`${d}`; + } else if (d.startsWith("insufficient source data")) { + return html`${d.replace("insufficient source data:", "")}`; + } else { + return html`${d}`; + } + }, + }, + }) +); +``` -- 2.47.2