One small script, shared by all three stacks. Store your API key as the SITEASSETS_API_KEY repository secret — never commit it.
js
// scripts/brand-audit.mjs
// Usage: node scripts/brand-audit.mjs <preview-url> [strict|warn|report-only]
const STRATEGY = process.argv[3] ?? 'warn';
const MIN_SCORE = 80;
const res = await fetch('https://siteassets.dev/api/v1/brand-audit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.SITEASSETS_API_KEY}`,
},
body: JSON.stringify({ url: process.argv[2] }),
});
// Service unavailable (5xx / network) must NOT fail the pipeline by default.
if (res.status >= 500) {
console.warn('::warning::SiteAssets audit service unavailable — skipping gate.');
process.exit(0);
}
const report = await res.json();
// 422 audit_failed = the TARGET SITE could not be fetched. That is a real
// finding (broken preview URL), so treat it as a failure in strict mode.
if (res.status === 422) {
console.error(`Audit failed: ${report.error.message} (${report.error.kind})`);
process.exit(STRATEGY === 'strict' ? 1 : 0);
}
if (!res.ok) {
console.error(`Audit request error: ${report.error?.code} ${report.error?.message}`);
process.exit(0); // auth/quota problems should not block unrelated deploys
}
console.log(`Brand score: ${report.score}/100`);
for (const f of report.findings.filter((f) => f.severity === 'error')) {
console.log(`::error::[${f.category}] ${f.title}`);
for (const e of f.evidence ?? []) console.log(` — ${e}`);
}
console.log(`Full report: https://siteassets.dev/brand-asset-audit`);
// Gate: strict fails on score below threshold; warn only annotates.
const errors = report.findings.filter((f) => f.severity === 'error').length;
if (STRATEGY === 'strict' && (report.score < MIN_SCORE || errors > 0)) {
console.error(`::error::Brand audit failed (${report.score}/100, ${errors} errors).`);
process.exit(1);
}
if (STRATEGY === 'warn' && (report.score < MIN_SCORE || errors > 0)) {
console.warn('::warning::Brand audit below threshold — see logs.');
}
process.exit(0);