doesitarm/helpers/build-homebrew-list.js
ThatGuySam d45b587434 Finish axios migration via shared native HTTP helper
Replace all in-scope axios callsites with a new helpers/http.js wrapper over native fetch, including JSON/text GET, JSON POST, HEAD checks, and transient 5xx retry behavior; update all browser, build, script, and proxy API clients to use it; add focused unit tests; and remove axios from package dependencies.

Constraint: Preserve API/build and deployment behavior while lowering transport surface area.

Rejected: inline fetch replacements at each callsite | rejected to avoid inconsistent error/retry semantics.

Confidence: high

Scope-risk: moderate

Directive: Keep helper in place as the transport boundary and update tests when changing request semantics.

Tested: pnpm run -s typecheck, pnpm -s run test-prebuild, pnpm -s run test, pnpm -s run test:browser, pnpm -s run netlify-build, smoke GETs on /apple-silicon-app-test and /apple-silicon-app-test/?version=2

Not-tested: branch/netlify deployment health in CI pipeline after merge
2026-04-06 12:09:16 -05:00

310 lines
9.5 KiB
JavaScript

// import { promises as fs } from 'fs'
// import MarkdownIt from 'markdown-it'
// import slugify from 'slugify'
// import statuses from './statuses'
// import parseDate from './parse-github-date'
const marked = require('marked')
const HTMLParser = require(`node-html-parser`)
import { getAppEndpoint } from './app-derived'
import { getJson } from './http.js'
const statusesTranslations = {
// brew install -s succeeds on Apple Silicon. The software works well enough natively.
'🥇': 'native',
// The formula has been updated with depends_on :arch => [:x86_64, :build]. The software works well enough on Rosetta.
'🥈': 'rosetta',
// The formula has known issues on macOS 11, though most features work. The issues are described in the Comments field.
'🥉': 'rosetta',
// The formula has been updated with depends_on :arch => :x86_64. The software has been deemed to work on Intel only (for now).
'🚫': 'no',
// The formula has been found to need more analysis/work.
'⚠️': 'no',
'': 'unreported'
}
const statusesMessages = {
'🥇': '✅ Yes, Native Apple Silicon Support',
'🥈': '✳️ Yes, works via Rosetta 2',
'🥉': '⏹ Known issues on macOS 11, though most features work',
'⚠️': '⏹ No, not yet, support is still in progress',
'🚫': '🚫 No, not yet supported only works on Intel-based Macs',
'': '🔶 Unknown, more info needed'
}
class MakeHomebrewList {
constructor() {
// Data from the issue
this.issueTableRowData = null
// Data from the official Homebrew API
this.allFormulaeArray = null
this.allFormulae = null
}
getStatusText ( formula ) {
// Match status to Sheet Status
return statusesMessages[formula.status]
}
searchFormulaeForName (name) {
for (const formula of this.allFormulaeArray) {
// Check normal name
if (formula.name === name) return formula
// Check normal oldname
if (formula.oldname === name) return formula
// Check aliases
if ((formula.aliases !== undefined) && formula.aliases.includes(name)) return formula
}
return null
}
hasArm64Formula( formulaData ) {
// Check the official list first since it's data is newer and more frequently updated
const hasStableFormula = (formulaData.bottle.stable !== undefined)
return hasStableFormula && (formulaData.bottle.stable.files['arm64_big_sur'] !== undefined)
}
formulaIsNative (formulae) {
// Search Formulae from Homebrew API
const formulaData = this.allFormulae[formulae.fullName] || this.searchFormulaeForName(formulae.name)
// console.log('formulae.fullName', formulae.fullName)
// If this formula doesn't exist
// then return false
if (formulaData !== Object(formulaData)) {
return false
}
// console.log('formulaData', formulaData)
// console.log('formulae', formulae)
// Check the official list first since it's data is newer and more frequently updated
const hasStableFormula = (formulaData.bottle.stable !== undefined)
const hasArm64Formula = hasStableFormula && (formulaData.bottle.stable.files['arm64_big_sur'] !== undefined)
return this.hasArm64Formula( formulaData )
}
parseStatus (formulae) {
// Match status to Sheet Status
return statusesTranslations[formulae.status]
}
async make () {
// Fetch Gihub Issue List
const [
issueResponse,
allFormulaeResponse
] = await Promise.all([
// Fetch Gihub Issue List
getJson( process.env.HOMEBREW_SOURCE ),
// Fetch Official Homebrew Formulae List
getJson( 'https://formulae.brew.sh/api/formula.json' )
])
// Extract commit from response data
const issueMarkdown = issueResponse.data.repository.issue.body
// Parse markdown
const issueHTML = marked(issueMarkdown)
// Parse Markdown HTML into a dom
const dom = HTMLParser.parse(issueHTML)
// Store the original array
this.allFormulaeArray = allFormulaeResponse
// Extract list from allFormulaeResponse and map into an object for easy access
this.allFormulae = Object.fromEntries(allFormulaeResponse.map(formula => {
return [
formula.full_name,
formula
]
}))
// console.log('allFormulae', allFormulae)
// Map table Headings
// [ 'Formula', 'Works1on 11.0', 'Comments' ]
// const tableHeadings = dom.querySelectorAll('thead th').map(th => th.rawText)
const headings = [
'fullName',
'status',
'comments'
]
// Map Formulae List within table
const tableRows = dom.querySelectorAll('table tr')
// Remove the first row (Headings)
tableRows.shift()
this.issueTableRowData = tableRows.map( tr => {
// Map Table Cells
const cellsData = tr.querySelectorAll('td').map((td, i) => {
const column = headings[i]
if (td.childNodes.length === 0) return ''
if (column === 'comments') return td.innerHTML
return td.rawText
})
const formulaeRow = Object.fromEntries(cellsData.map( (cellData, i) => {
return [ headings[i], cellData ]
}))
formulaeRow.name = formulaeRow.fullName.split(' ')[0]
formulaeRow.links = []
// formulaeRow.links = tr.querySelectorAll('a').map( a => {
// const href = a.getAttribute('href')
// return {
// href,
// label: a.rawText,
// // a
// }
// })
// if (formulaeRow.links.length !== 0) console.log('formulaeRow', formulaeRow.links)
const isNativeInApi = this.formulaIsNative(formulaeRow)
if (isNativeInApi) {
// Update status
formulaeRow.status = '🥇'
}
return formulaeRow
})
// console.log('dom', dom.length)
// console.log('issueHTML', issueHTML)
// console.log('formulaeWithStatus', formulaeWithStatus)
const formulaeList = new Map()
const category = {
slug: 'homebrew'
}
// for (const formula of Object.values( this.allFormulae )) {
// const isNative = this.hasArm64Formula( formula )
// const status = isNative ? 'native' : 'unreported'
// const statusText = isNative ? '✅ Yes, Native Apple Silicon Support' : '🔶 Formula has not yet been reported to be native to Apple Silicon'
// const slug = formula.full_name
// formulaeList.set(formula.full_name, {
// name: formula.full_name,
// status: status,
// // url: `https://formulae.brew.sh/formula/${formulae.name}`,
// text: statusText,
// slug,
// endpoint: getAppEndpoint({
// slug,
// category
// }),//`/formula/${slug}`,
// category,
// content: null,npm
// relatedLinks: [
// {
// href: `https://formulae.brew.sh/formula/${formula.name}`,
// label: formula.full_name,
// // a
// },
// // ...formula.links
// ],
// // reports: [
// // formulae
// // ]
// })
// }
for (const formulae of this.issueTableRowData) {
// If this formulae status is empty
// then skip this formulae
// if (formulae.status.length === 0) continue
// If this formulae emoji status is not in statusesTranslations
// then skip this formulae
if (!statusesTranslations.hasOwnProperty(formulae.status)) continue
// Generate slug
const slug = formulae.name
// slugify(formulae.name, {
// lower: true,
// strict: true
// })
formulaeList.set(formulae.name, {
name: formulae.name,
status: this.parseStatus(formulae),
// url: `https://formulae.brew.sh/formula/${formulae.name}`,
text: this.getStatusText(formulae),
slug,
endpoint: getAppEndpoint({
slug,
category
}),//`/formula/${slug}`,
category,
content: '',//formulae.comments,
relatedLinks: [
{
href: `https://formulae.brew.sh/formula/${formulae.name}`,
label: formulae.name,
// a
},
...formulae.links
],
// reports: [
// formulae
// ]
})
}
// console.log('formulaeList', formulaeList)
return Array.from( formulaeList , ([_, data]) => data )
}
}
export default async function () {
const maker = new MakeHomebrewList()
return await maker.make()
}