diff --git a/bin/readme b/bin/readme new file mode 100644 index 0000000..25bc3e6 --- /dev/null +++ b/bin/readme @@ -0,0 +1,160 @@ +#!/usr/bin/env node + +import { readFileSync, writeFileSync } from "node:fs"; + +const token = process.env.GH_TOKEN; +const org = "umts"; +const stacks = { + rails: "rails-CC0000?logo=ruby-on-rails", + sinatra: "sinatra-000000?logo=ruby-sinatra", + react: "react-61DAFB?logo=react&logoColor=black", +}; + +async function fetchRepos() { + const query = ` + query($org: String!, $cursor: String) { + organization(login: $org) { + repositories(first: 100, after: $cursor, isArchived: false) { + pageInfo { + hasNextPage + endCursor + } + nodes { + name + visibility + homepageUrl + defaultBranchRef { + name + } + repositoryTopics(first: 100) { + nodes { + topic { name } + } + } + repositoryCustomPropertyValues(first: 100) { + nodes { + propertyName + value + } + } + } + } + } + } + `; + const repos = []; + let cursor = null; + while (true) { + const res = await fetch("https://api.github.com/graphql", { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ query, variables: { org, cursor } }), + }); + const json = await res.json(); + if (json.errors || !json.data) { + console.error(JSON.stringify(json, null, 2)); + process.exit(1); + } + const { nodes, pageInfo } = json.data.organization.repositories; + repos.push(...nodes); + if (!pageInfo.hasNextPage) break; + cursor = pageInfo.endCursor; + } + return repos + .filter((r) => + r.repositoryCustomPropertyValues.nodes.some( + (p) => p.propertyName === "dashboard" && p.value.includes("true"), + ), + ) + .sort((r1, r2) => { + const s1 = repoStackOrder(r1); + const s2 = repoStackOrder(r2); + return s1 !== s2 ? s1 - s2 : r1.name.localeCompare(r2.name); + }); +} + +function repoStackOrder(repo) { + const topics = repo.repositoryTopics.nodes.map((n) => n.topic.name); + const stackKeys = Object.keys(stacks); + const idx = stackKeys.findIndex((k) => topics.includes(k)); + return idx === -1 ? stackKeys.length : idx; +} + +function renderDashboard(repos) { + const apps = repos.filter((r) => r.homepageUrl); + const appRows = apps + .map( + (repo) => ` + ${renderRepoCell(repo)} + ${renderSiteCell(repo)} + ${renderStackCell(repo)} +`, + ) + .join("\n"); + + const others = repos.filter((r) => !r.homepageUrl); + const otherRows = others + .map( + (repo) => ` + ${renderRepoCell(repo)} + + ${renderStackCell(repo)} +`, + ) + .join("\n"); + + return ` + + + + + ${appRows} + + + + + + ${otherRows} + +
Applications
Libraries, Tools & Infrastructure
` + .trim() + .replace(/\s+/g, " "); +} + +function renderRepoCell(repo) { + const checksBadge = !repo.defaultBranchRef + ? "" + : repo.visibility === "PRIVATE" + ? `` + : ``; + return ` + + ${repo.name} + + ${checksBadge} +`; +} + +function renderStackCell(repo) { + const topics = repo.repositoryTopics.nodes.map((n) => n.topic.name); + const match = Object.keys(stacks).find((k) => topics.includes(k)); + if (!match) return ``; + return ` + +`; +} + +function renderSiteCell(repo) { + return ` + ${repo.homepageUrl} + +`; +} + +const repos = await fetchRepos(); +const dashboard = renderDashboard(repos); +const template = readFileSync("profile/README.template.md", "utf-8"); +writeFileSync("profile/README.md", template.replace("{{dashboard}}", dashboard)); diff --git a/profile/README.md b/profile/README.md new file mode 100644 index 0000000..5fb98fa --- /dev/null +++ b/profile/README.md @@ -0,0 +1,11 @@ +# UMass Transportation Services + +
Applications
incidents https://incidents.pvta.com
jobapps https://transit-jobapps.admin.umass.edu
round-three https://umasstransit.org
screaming-dinosaur https://umts-oncall.admin.umass.edu
stop-project https://stop-project.admin.umass.edu
umaps-rails https://hub.parking.umass.edu
umasstransit.rodeo https://umasstransit.rodeo
dev-training-web https://umts-dt.admin.umass.edu
fleetfocus-api https://transit-fuel-api.admin.umass.edu
gtfs_cache https://gtfs-cache.admin.umass.edu
strap https://strap.umasstransit.it
departure-board https://pvta-departures.admin.umass.edu
public-message-board https://pvta-public-messages.admin.umass.edu
BusInfoBoard https://umts.github.io/BusInfoBoard
Libraries, Tools & Infrastructure
gtfs-react-hooks
eam-mileage-import
homebrew-umts-dev
pmp
renovate-config
umts-ansible
+ +## Resources +- [Azure](https://portal.azure.com/) +- [Confluence](https://umass-amherst.atlassian.net/wiki/spaces/AFIT/overview) +- [GenAI Platform](https://genai.umass.edu) +- [Jenkins](https://jenkins.umass.edu) +- [ServiceNow](https://umass.service-now.com) +- [Sharepoint](https://umass.sharepoint.com/sites/TransportationIT) diff --git a/profile/README.template.md b/profile/README.template.md new file mode 100644 index 0000000..b992617 --- /dev/null +++ b/profile/README.template.md @@ -0,0 +1,11 @@ +# UMass Transportation Services + +{{dashboard}} + +## Resources +- [Azure](https://portal.azure.com/) +- [Confluence](https://umass-amherst.atlassian.net/wiki/spaces/AFIT/overview) +- [GenAI Platform](https://genai.umass.edu) +- [Jenkins](https://jenkins.umass.edu) +- [ServiceNow](https://umass.service-now.com) +- [Sharepoint](https://umass.sharepoint.com/sites/TransportationIT)