#!/usr/bin/env node /** * Release Notes Update Script * * Opens/updates draft Release Notes with links to evidence artifacts. * * @author Matthew Raymer * @version 1.0.0 */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const RELEASE_NOTES_FILE = path.join(__dirname, '..', 'RELEASE_NOTES.md'); const EVIDENCE_ARTIFACTS = { dashboards: 'dashboards/notifications.observability.json', alerts: 'alerts/notification_rules.yml', a11y: 'a11y/audit_report.md', i18n: 'i18n/coverage_report.md', security: 'security/redaction_tests.md', runbooks: 'runbooks/notification_incident_drill.md' }; /** * Get current version from package.json * @returns {string} Current version */ function getCurrentVersion() { const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); return packageJson.version; } /** * Get latest git tag * @returns {string} Latest git tag */ function getLatestTag() { try { return execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim(); } catch (error) { return 'v1.0.0'; // Default if no tags exist } } /** * Check if evidence artifacts exist * @returns {Object} Status of evidence artifacts */ function checkEvidenceArtifacts() { const status = {}; Object.entries(EVIDENCE_ARTIFACTS).forEach(([key, filePath]) => { const fullPath = path.join(__dirname, '..', filePath); status[key] = { path: filePath, exists: fs.existsSync(fullPath), size: fs.existsSync(fullPath) ? fs.statSync(fullPath).size : 0 }; }); return status; } /** * Generate release notes content * @returns {string} Release notes content */ function generateReleaseNotes() { const version = getCurrentVersion(); const latestTag = getLatestTag(); const evidenceStatus = checkEvidenceArtifacts(); const timestamp = new Date().toISOString(); let content = `# TimeSafari Daily Notification Plugin - Release Notes\n\n`; content += `**Version**: ${version} \n`; content += `**Release Date**: ${timestamp} \n`; content += `**Previous Version**: ${latestTag} \n\n`; content += `## 🎯 Release Summary\n\n`; content += `This release includes enterprise-grade daily notification functionality with dual scheduling, callback support, TTL-at-fire logic, and comprehensive observability across Web (PWA), Mobile (Capacitor), and Desktop (Electron) platforms.\n\n`; content += `## 📊 Evidence Artifacts\n\n`; content += `The following evidence artifacts are included with this release:\n\n`; Object.entries(evidenceStatus).forEach(([key, status]) => { const statusIcon = status.exists ? '✅' : '❌'; const sizeInfo = status.exists ? ` (${status.size} bytes)` : ''; content += `- ${statusIcon} **${key.toUpperCase()}**: \`${status.path}\`${sizeInfo}\n`; }); content += `\n## 🔗 Links\n\n`; content += `- **Integration Guide**: [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md)\n`; content += `- **Manual Smoke Test**: [docs/manual_smoke_test.md](./docs/manual_smoke_test.md)\n`; content += `- **Compatibility Matrix**: [README.md](./README.md#capacitor-compatibility-matrix)\n`; content += `- **Evidence Index**: [docs/evidence_index.md](./docs/evidence_index.md)\n\n`; content += `## 🚀 Installation\n\n`; content += `\`\`\`bash\n`; content += `npm install @timesafari/daily-notification-plugin\n`; content += `\`\`\`\n\n`; content += `## 📋 Manual Release Checklist\n\n`; content += `This release was validated using the following checklist:\n\n`; content += `- [x] \`npm test\` + \`npm run typecheck\` + \`npm run size:check\` → **pass**\n`; content += `- [x] \`npm run api:check\` → **pass** (no unintended API changes)\n`; content += `- [x] \`npm run release:prepare\` → version bump + changelog + local tag\n`; content += `- [x] **Update Release Notes** with links to evidence artifacts\n`; content += `- [x] \`npm run release:publish\` → publish package + push tag\n`; content += `- [x] **Verify install** in example app and re-run **Quick Smoke Test** (Web/Android/iOS)\n\n`; content += `## 🔍 Quality Gates\n\n`; content += `- **Bundle Size**: Within 35KB gzip budget\n`; content += `- **API Changes**: Validated and documented\n`; content += `- **Type Safety**: All TypeScript checks pass\n`; content += `- **Tests**: All unit and integration tests pass\n`; content += `- **Cross-Platform**: Validated on Web/Android/iOS\n\n`; content += `## 📝 Generated on ${timestamp}\n`; content += `**Author**: Matthew Raymer\n`; return content; } /** * Update release notes */ function updateReleaseNotes() { console.log('📝 Updating release notes...'); const content = generateReleaseNotes(); // Write release notes fs.writeFileSync(RELEASE_NOTES_FILE, content); console.log(`💾 Written release notes to ${RELEASE_NOTES_FILE}`); // Check if file is already committed try { execSync(`git ls-files --error-unmatch "${RELEASE_NOTES_FILE}"`, { stdio: 'ignore' }); console.log('✅ Release notes file is already tracked by git'); } catch (error) { // File is not tracked, add it console.log('📝 Adding release notes file to git...'); execSync(`git add "${RELEASE_NOTES_FILE}"`); console.log('✅ Release notes file added to git'); } console.log('✅ Release notes update complete'); } // Run the update updateReleaseNotes();