Files
gitea-github-cron/notify.js
T
2026-09-29 17:38:19 +00:00

81 lines
3.1 KiB
JavaScript

const { readFileSync } = require('node:fs');
const { NTFY_TOPIC_URL, RUN_URL, STEP_OUTCOME, TEST_NOTIFICATION, WORKFLOW_KIND } = process.env;
// [noun, key]: shown as "**n** noun(s)" and hidden when zero
const labels = {
mirror: ['Mirror sync', 'mirror-facts.json', [
['org', 'orgsChecked'],
['repo', 'reposFound'],
['created org', 'orgsCreated'],
['org visibility change', 'orgVisibilityChanged'],
['new mirror', 'mirrorsCreated'],
['repo visibility change', 'repoVisibilityChanged'],
['description update', 'descriptionsUpdated'],
['failure', 'failures']
]],
rename: ['Missing-mirror check', 'rename-facts.json', [
['org', 'orgsChecked'],
['mirror', 'mirrorsFound'],
['renamed mirror', 'renamed'],
['skipped org', 'skippedOrgs'],
['failure', 'failures']
]],
releases: ['Release sync', 'release-facts.json', [
['mirror', 'mirrorsChecked'],
['GitHub release', 'releasesFound'],
['created release', 'created'],
['updated release', 'updated'],
['uploaded asset', 'assetsUploaded'],
['unsynced tag', 'unsyncedTags'],
['failure', 'failures']
]],
issues: ['Issue sync', 'issue-facts.json', [
['mirror', 'mirrorsChecked'],
['GitHub issue', 'issuesFound'],
['created issue', 'created'],
['updated issue', 'updated'],
['created comment', 'commentsCreated'],
['updated comment', 'commentsUpdated'],
['failure', 'failures']
]]
};
const md = text => String(text).replace(/[\\`*_[\]#]/g, '\\$&');
(async () => {
if (!NTFY_TOPIC_URL || !labels[WORKFLOW_KIND]) throw new Error('Notification URL or workflow kind missing');
const test = TEST_NOTIFICATION === 'true', ok = STEP_OUTCOME === 'success';
const [label, file, fields] = labels[WORKFLOW_KIND];
const title = test ? 'Gitea backup notification test' : `${label}: ${ok ? 'succeeded' : 'failed'}`;
const lines = test ? ['Notification-only test. No mirror or rename ran.'] : [];
if (!test) {
try {
const facts = JSON.parse(readFileSync(file, 'utf8'));
const counts = fields.filter(([, k]) => facts[k]).map(([noun, k]) => `**${facts[k]}** ${noun}${facts[k] === 1 ? '' : 's'}`);
if (counts.length) lines.push(counts.join(' · '));
const changes = facts.changes || [];
if (changes.length) lines.push('', ...changes.slice(0, 10).map(c => `- ${md(c)}`));
if (changes.length > 10) lines.push(`- *…and ${changes.length - 10} more; see run logs.*`);
if (facts.fatal) lines.push('', '**Fatal error**; see run logs.');
} catch {
lines.push('No run summary available; see run logs.');
}
}
const res = await fetch(NTFY_TOPIC_URL, {
method: 'POST',
headers: {
Priority: '1', Title: title, Markdown: 'yes',
Tags: test ? 'test_tube' : ok ? 'white_check_mark' : 'x',
...(RUN_URL && { Actions: `view, Open run, ${RUN_URL}` })
},
body: lines.join('\n') || 'Nothing to report.',
signal: AbortSignal.timeout(30_000)
});
if (!res.ok) throw new Error(`ntfy returned HTTP ${res.status}`);
console.log(`ntfy notification sent: ${title}`);
})().catch(error => {
console.error(`Notification failed: ${error.message}`);
process.exitCode = 1;
});