Better Dead Domain CI Report [skip ci]

This commit is contained in:
SukkaW
2026-08-09 17:48:28 +08:00
parent 071be4bf74
commit 14f5811242

View File

@@ -8,10 +8,18 @@ import type { RunnerGeoIP } from './lib/get-runner-geoip';
import cliProgress from 'cli-progress'; import cliProgress from 'cli-progress';
import { newQueue } from '@henrygd/queue'; import { newQueue } from '@henrygd/queue';
import { DOMAIN_ALIVE_REASON_MESSAGES } from 'domain-alive';
import type { DomainAliveReason } from 'domain-alive';
const queue = newQueue(32); const queue = newQueue(32);
const domainCollator = new Intl.Collator('en');
const deadDomains: string[] = []; interface DeadDomain {
domain: string,
reason: DomainAliveReason
}
const deadDomains: DeadDomain[] = [];
/** /**
* Append this shard's result to the GitHub Actions job summary, if running in * Append this shard's result to the GitHub Actions job summary, if running in
@@ -23,23 +31,27 @@ function formatGeo(geo: RunnerGeoIP | null): string {
return `${geo.ip}${geo.city}, ${geo.region}, ${geo.country} (AS${geo.asn} ${geo.asOrg})`; return `${geo.ip}${geo.city}, ${geo.region}, ${geo.country} (AS${geo.asn} ${geo.asOrg})`;
} }
function writeJobSummary(shardLabel: string, dead: string[], geo: RunnerGeoIP | null) { function writeJobSummary(shardLabel: string, dead: DeadDomain[], geo: RunnerGeoIP | null) {
const summaryPath = process.env.GITHUB_STEP_SUMMARY; const summaryPath = process.env.GITHUB_STEP_SUMMARY;
if (!summaryPath) return; if (!summaryPath) return;
const sortedDead = dead.toSorted((a, b) => domainCollator.compare(a.domain, b.domain));
let summary = `## Dead domains — shard ${shardLabel}\n\n` let summary = `## Dead domains — shard ${shardLabel}\n\n`
+ `Runner egress: \`${formatGeo(geo)}\`\n\n` + `Runner egress: \`${formatGeo(geo)}\`\n\n`
+ `Found **${dead.length}** dead domain(s) in this shard.\n\n`; + `Found **${dead.length}** dead domain(s) in this shard.\n\n`;
if (dead.length > 0) { if (dead.length > 0) {
summary += '<details><summary>Show list</summary>\n\n' summary += '<details><summary>Show list and reasons</summary>\n\n'
+ '```\n' + '| Domain | Reason | Explanation |\n'
+ dead.join('\n') + '\n' + '| --- | --- | --- |\n'
+ '```\n\n' + sortedDead.map(({ domain, reason }) => (
`| \`${domain}\` | \`${reason}\` | ${DOMAIN_ALIVE_REASON_MESSAGES[reason]} |`
)).join('\n') + '\n\n'
+ '</details>\n\n' + '</details>\n\n'
// Machine-recoverable copy so the union can be scraped from summaries. // Preserve the original machine-recoverable domain list format.
+ '```json\n' + '```json\n'
+ JSON.stringify(dead) + '\n' + JSON.stringify(sortedDead.map(({ domain }) => domain)) + '\n'
+ '```\n\n'; + '```\n\n';
} }
@@ -74,23 +86,26 @@ function writeJobSummary(shardLabel: string, dead: string[], geo: RunnerGeoIP |
for (let i = 0, len = shardDomains.length; i < len; i++) { for (let i = 0, len = shardDomains.length; i < len; i++) {
const { domain, includeAllSubdomain } = shardDomains[i]; const { domain, includeAllSubdomain } = shardDomains[i];
queue.add(async () => { queue.add(async () => {
let registerableDomainAlive, registerableDomain, alive: boolean | undefined;
if (includeAllSubdomain) { if (includeAllSubdomain) {
// we only need to check apex domain, because we don't know if there is any stripped subdomain // we only need to check apex domain, because we don't know if there is any stripped subdomain
({ alive: registerableDomainAlive, registerableDomain } = await isRegisterableDomainAlive(domain)); const { alive, registerableDomain, reason } = await isRegisterableDomainAlive(domain);
} else { bar.increment();
({ alive, registerableDomainAlive, registerableDomain } = await isDomainAlive(domain));
if (!alive && registerableDomain) {
deadDomains.push({ domain: '.' + registerableDomain, reason });
}
return;
} }
const { alive, registerableDomainAlive, registerableDomain, reason } = await isDomainAlive(domain);
bar.increment(); bar.increment();
if (!registerableDomainAlive) { if (!registerableDomainAlive) {
if (registerableDomain) { if (registerableDomain) {
deadDomains.push('.' + registerableDomain); deadDomains.push({ domain: '.' + registerableDomain, reason });
} }
} else if (!alive && !includeAllSubdomain && alive != null) { } else if (!alive) {
deadDomains.push(domain); deadDomains.push({ domain, reason });
} }
}); });
} }
@@ -101,7 +116,7 @@ function writeJobSummary(shardLabel: string, dead: string[], geo: RunnerGeoIP |
console.log(); console.log();
console.log(); console.log();
console.log(`[shard ${shardLabel}]`, JSON.stringify(deadDomains)); console.log(`[shard ${shardLabel}]`, JSON.stringify(deadDomains.map(({ domain }) => domain)));
writeJobSummary(shardLabel, deadDomains, geo); writeJobSummary(shardLabel, deadDomains, geo);
})(); })();