const fs = require('fs'); const path = require('path'); const matter = require('gray-matter'); const POSTS_DIR = path.join(__dirname, '../content/post'); function parseDateFlexible(input) { if (input instanceof Date) return Number.isNaN(input.getTime()) ? null : input; if (input == null) return null; const raw = String(input).trim(); if (!raw) return null; const direct = new Date(raw); if (!Number.isNaN(direct.getTime())) return direct; const digits = raw.replace(/\D/g, ''); if (digits.length === 14 || digits.length === 12) { const y = Number(digits.slice(0, 4)); const mo = Number(digits.slice(4, 6)); const d = Number(digits.slice(6, 8)); const h = Number(digits.slice(8, 10)); const mi = Number(digits.slice(10, 12)); const s = digits.length === 14 ? Number(digits.slice(12, 14)) : 0; if ([y, mo, d, h, mi, s].some(n => !Number.isFinite(n))) return null; const dt = new Date(y, mo - 1, d, h, mi, s); return Number.isNaN(dt.getTime()) ? null : dt; } if (digits.length === 8) { const y = Number(digits.slice(0, 4)); const mo = Number(digits.slice(4, 6)); const d = Number(digits.slice(6, 8)); if ([y, mo, d].some(n => !Number.isFinite(n))) return null; return new Date(y, mo - 1, d); } return null; } function isWithinDays(date, days) { if (!(date instanceof Date) || Number.isNaN(date.getTime())) return false; const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); return date >= cutoff; } function collectPublicPosts() { try { if (!fs.existsSync(POSTS_DIR)) return []; const posts = []; function scanDir(dir) { const files = fs.readdirSync(dir); for (const file of files) { const fullPath = path.join(dir, file); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { scanDir(fullPath); } else if (file.endsWith('.md')) { try { const content = fs.readFileSync(fullPath, 'utf8'); const parsed = matter(content); if (parsed.data.status === 'hidden' || parsed.data.draft === true) continue; const date = parsed.data.date ? parseDateFlexible(parsed.data.date) : null; posts.push({ title: parsed.data.title ? String(parsed.data.title) : file.replace('.md', ''), date, }); } catch (e) { /* skip */ } } } } scanDir(POSTS_DIR); return posts; } catch (e) { console.error('Error collecting posts:', e); return []; } } async function main() { let report = ""; const nowStr = new Date().toLocaleString(); const tz = process.env.TZ || ''; const repo = process.env.GITHUB_REPOSITORY || ''; const refName = process.env.GITHUB_REF_NAME || ''; const sha = process.env.GITHUB_SHA ? process.env.GITHUB_SHA.slice(0, 7) : ''; const runId = process.env.GITHUB_RUN_ID || ''; const serverUrl = process.env.GITHUB_SERVER_URL || 'https://github.com'; const runUrl = repo && runId ? `${serverUrl}/${repo}/actions/runs/${runId}` : ''; report += `### 🚀 部署信息\n`; report += `- 时间: ${nowStr}\n`; if (tz) report += `- 时区: ${tz}\n`; if (repo) report += `- 仓库: ${repo}\n`; if (refName) report += `- 分支: ${refName}\n`; if (sha) report += `- 提交: ${sha}\n`; if (runUrl) report += `- Actions: ${runUrl}\n`; report += `\n`; const posts = collectPublicPosts(); const totalPublic = posts.length; const dated = posts.filter(p => p.date instanceof Date && !Number.isNaN(p.date.getTime())); const undatedCount = totalPublic - dated.length; const recent7 = dated.filter(p => isWithinDays(p.date, 7)); const recent30 = dated.filter(p => isWithinDays(p.date, 30)); report += `### 📦 内容概览\n`; report += `- 公开文章: ${totalPublic}\n`; report += `- 近7天更新: ${recent7.length}\n`; report += `- 近30天更新: ${recent30.length}\n`; if (undatedCount > 0) report += `- 缺少日期: ${undatedCount}\n`; report += `\n`; report += `### 🧩 环境\n`; report += `- Node: ${process.version}\n`; if (process.env.RUNNER_OS) report += `- Runner: ${process.env.RUNNER_OS}\n`; report += `\n`; console.log(report); if (process.env.GITHUB_ENV) { const delimiter = `EOF_${Math.random().toString(36).substr(2, 9)}`; fs.appendFileSync(process.env.GITHUB_ENV, `DEPLOY_REPORT<<${delimiter}\n${report}\n${delimiter}\n`); } } main();