2026-03-01 09:42:39 +08:00
|
|
|
const fs = require('fs');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const matter = require('gray-matter');
|
2026-03-13 12:24:47 +08:00
|
|
|
const axios = require('axios');
|
2026-03-01 09:42:39 +08:00
|
|
|
|
|
|
|
|
const FRIEND_CIRCLE_PATH = path.join(__dirname, '../themes/Ying/static/json/friend_circle_data.json');
|
|
|
|
|
const LINK_SUMMARY_PATH = path.join(__dirname, '../failed_links_summary.txt');
|
|
|
|
|
const POSTS_DIR = path.join(__dirname, '../content/post');
|
2026-03-13 12:24:47 +08:00
|
|
|
const HUGO_CONFIG_PATH = path.join(__dirname, '../hugo.toml');
|
2026-03-01 09:42:39 +08:00
|
|
|
|
2026-03-01 20:29:23 +08:00
|
|
|
function formatDateYYYYMMDDHHmm(date) {
|
2026-03-13 12:24:47 +08:00
|
|
|
const y = date.getFullYear();
|
|
|
|
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
|
|
const d = String(date.getDate()).padStart(2, '0');
|
|
|
|
|
const h = String(date.getHours()).padStart(2, '0');
|
|
|
|
|
const mm = String(date.getMinutes()).padStart(2, '0');
|
2026-03-01 20:29:23 +08:00
|
|
|
return `${y}${m}${d}${h}${mm}`;
|
2026-03-01 09:42:39 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-01 09:42:39 +08:00
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
function stripHtml(input) {
|
|
|
|
|
if (input == null) return '';
|
|
|
|
|
return String(input)
|
|
|
|
|
.replace(/<[^>]*>/g, '')
|
|
|
|
|
.replace(/\s+/g, ' ')
|
|
|
|
|
.trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function truncate(input, maxLen) {
|
|
|
|
|
const s = input == null ? '' : String(input);
|
|
|
|
|
if (s.length <= maxLen) return s;
|
|
|
|
|
return `${s.slice(0, Math.max(0, maxLen - 1))}…`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
const dt = new Date(y, mo - 1, d);
|
|
|
|
|
return Number.isNaN(dt.getTime()) ? null : dt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDateMDY(date) {
|
|
|
|
|
try {
|
|
|
|
|
return date.toLocaleDateString('en-US');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectPublicPosts() {
|
|
|
|
|
try {
|
|
|
|
|
if (!fs.existsSync(POSTS_DIR)) return [];
|
|
|
|
|
|
|
|
|
|
const posts = [];
|
2026-03-01 09:42:39 +08:00
|
|
|
|
|
|
|
|
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);
|
2026-03-13 12:24:47 +08:00
|
|
|
const isPublic = parsed.data.status !== 'hidden' && parsed.data.draft !== true;
|
|
|
|
|
if (!isPublic) continue;
|
|
|
|
|
|
|
|
|
|
const date = parsed.data.date ? parseDateFlexible(parsed.data.date) : null;
|
|
|
|
|
const title = parsed.data.title ? String(parsed.data.title) : file.replace('.md', '');
|
|
|
|
|
const slugRaw = parsed.data.slug != null ? String(parsed.data.slug).trim() : '';
|
|
|
|
|
const slug = slugRaw || (date && !Number.isNaN(date.getTime()) ? formatDateYYYYMMDDHHmm(date) : '');
|
|
|
|
|
posts.push({
|
|
|
|
|
title,
|
|
|
|
|
date,
|
|
|
|
|
slug
|
|
|
|
|
});
|
2026-03-01 09:42:39 +08:00
|
|
|
} catch (e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scanDir(POSTS_DIR);
|
2026-03-13 12:24:47 +08:00
|
|
|
return posts;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Error collecting posts:', e);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getFileMetaString(filePath) {
|
|
|
|
|
try {
|
|
|
|
|
const stat = fs.statSync(filePath);
|
|
|
|
|
const mtime = stat.mtime ? stat.mtime.toLocaleString() : '';
|
|
|
|
|
const sizeKb = typeof stat.size === 'number' ? Math.max(1, Math.round(stat.size / 1024)) : 0;
|
|
|
|
|
return mtime ? `${sizeKb}KB, 更新于 ${mtime}` : `${sizeKb}KB`;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readHugoConfig() {
|
|
|
|
|
try {
|
|
|
|
|
if (!fs.existsSync(HUGO_CONFIG_PATH)) return null;
|
|
|
|
|
const raw = fs.readFileSync(HUGO_CONFIG_PATH, 'utf8');
|
|
|
|
|
const lines = raw.split(/\r?\n/);
|
|
|
|
|
|
|
|
|
|
let baseURL = '';
|
|
|
|
|
let inArtalk = false;
|
|
|
|
|
let artalkServer = '';
|
|
|
|
|
let artalkSite = '';
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
const trimmed = line.trim();
|
|
|
|
|
if (!trimmed) continue;
|
|
|
|
|
|
|
|
|
|
if (!baseURL) {
|
|
|
|
|
const mBase = trimmed.match(/^baseURL\s*=\s*"([^"]+)"\s*$/);
|
|
|
|
|
if (mBase) baseURL = mBase[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (/^\[params\.artalk\]\s*$/.test(trimmed)) {
|
|
|
|
|
inArtalk = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (inArtalk && /^\[.*\]\s*$/.test(trimmed)) break;
|
|
|
|
|
if (!inArtalk) continue;
|
|
|
|
|
|
|
|
|
|
const mServer = trimmed.match(/^server\s*=\s*"([^"]+)"\s*$/);
|
|
|
|
|
if (mServer) {
|
|
|
|
|
artalkServer = mServer[1];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const mSite = trimmed.match(/^site\s*=\s*"([^"]+)"\s*$/);
|
|
|
|
|
if (mSite) {
|
|
|
|
|
artalkSite = mSite[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const conf = {};
|
|
|
|
|
if (baseURL) conf.baseURL = baseURL;
|
|
|
|
|
if (artalkServer && artalkSite) conf.artalk = { server: artalkServer, site: artalkSite };
|
|
|
|
|
return Object.keys(conf).length > 0 ? conf : null;
|
2026-03-01 09:42:39 +08:00
|
|
|
} catch (e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
function readArtalkConfig() {
|
|
|
|
|
try {
|
|
|
|
|
if (!fs.existsSync(HUGO_CONFIG_PATH)) return null;
|
|
|
|
|
const raw = fs.readFileSync(HUGO_CONFIG_PATH, 'utf8');
|
|
|
|
|
const lines = raw.split(/\r?\n/);
|
|
|
|
|
let inSection = false;
|
|
|
|
|
let server = '';
|
|
|
|
|
let site = '';
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
const trimmed = line.trim();
|
|
|
|
|
if (!trimmed) continue;
|
|
|
|
|
|
|
|
|
|
if (/^\[params\.artalk\]\s*$/.test(trimmed)) {
|
|
|
|
|
inSection = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (inSection && /^\[.*\]\s*$/.test(trimmed)) break;
|
|
|
|
|
if (!inSection) continue;
|
|
|
|
|
|
|
|
|
|
const mServer = trimmed.match(/^server\s*=\s*"([^"]+)"\s*$/);
|
|
|
|
|
if (mServer) {
|
|
|
|
|
server = mServer[1];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const mSite = trimmed.match(/^site\s*=\s*"([^"]+)"\s*$/);
|
|
|
|
|
if (mSite) {
|
|
|
|
|
site = mSite[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!server || !site) return null;
|
|
|
|
|
return { server, site };
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchArtalkCommentsForPage({ server, site, pageKey, limit }) {
|
|
|
|
|
const base = String(server || '').replace(/\/+$/, '');
|
|
|
|
|
const siteName = String(site || '').trim();
|
|
|
|
|
const key = String(pageKey || '').trim();
|
|
|
|
|
if (!base || !siteName || !key) return [];
|
|
|
|
|
|
|
|
|
|
const url = `${base}/api/v2/comments`;
|
|
|
|
|
try {
|
|
|
|
|
const resp = await axios.get(url, {
|
|
|
|
|
params: { site_name: siteName, page_key: key, limit: limit || 50, offset: 0 },
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
validateStatus: (s) => s >= 200 && s < 400
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const payload = resp && resp.data ? resp.data : null;
|
|
|
|
|
const list = payload && Array.isArray(payload.comments) ? payload.comments : [];
|
|
|
|
|
return list;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 09:42:39 +08:00
|
|
|
async function main() {
|
|
|
|
|
let report = "";
|
|
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
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`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const hugoConf = readHugoConfig();
|
|
|
|
|
const artalk = hugoConf && hugoConf.artalk ? hugoConf.artalk : readArtalkConfig();
|
|
|
|
|
const baseURL = hugoConf && hugoConf.baseURL ? String(hugoConf.baseURL).replace(/\/+$/, '') : 'https://usj.cc';
|
|
|
|
|
|
|
|
|
|
if (artalk) {
|
|
|
|
|
const postsByDate = posts
|
|
|
|
|
.filter(p => p && typeof p.slug === 'string' && p.slug.trim())
|
|
|
|
|
.filter(p => p.date instanceof Date && !Number.isNaN(p.date.getTime()))
|
|
|
|
|
.slice()
|
|
|
|
|
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
|
|
|
|
.slice(0, 15);
|
|
|
|
|
|
|
|
|
|
const pageKeys = [...new Set(postsByDate.map(p => `/${String(p.slug).trim()}.html`))];
|
|
|
|
|
const allComments = [];
|
|
|
|
|
|
|
|
|
|
for (const pageKey of pageKeys) {
|
|
|
|
|
const pageComments = await fetchArtalkCommentsForPage({ server: artalk.server, site: artalk.site, pageKey, limit: 100 });
|
|
|
|
|
pageComments.forEach(c => {
|
|
|
|
|
const dateRaw = c && (c.date || c.created_at || c.createdAt || c.time);
|
|
|
|
|
const date = dateRaw ? new Date(dateRaw) : null;
|
|
|
|
|
allComments.push({
|
|
|
|
|
nick: c && (c.nick || c.nickname || c.name || c.user_name || c.userName) ? (c.nick || c.nickname || c.name || c.user_name || c.userName) : '匿名',
|
|
|
|
|
content: c && (c.content_marked || c.content || c.text || c.body),
|
|
|
|
|
date,
|
|
|
|
|
pageKey,
|
|
|
|
|
link: `${baseURL}${pageKey}`
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const latest = allComments
|
|
|
|
|
.filter(c => c.date instanceof Date && !Number.isNaN(c.date.getTime()))
|
|
|
|
|
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
|
|
|
|
.slice(0, 10);
|
|
|
|
|
|
|
|
|
|
report += `### Artalk 评论 (近10条)\n`;
|
|
|
|
|
report += `- 站点: ${artalk.site}\n`;
|
|
|
|
|
report += `- 服务: ${artalk.server}\n`;
|
|
|
|
|
if (latest.length === 0) report += `(无评论或获取失败)\n`;
|
|
|
|
|
latest.forEach((c) => {
|
|
|
|
|
const content = truncate(stripHtml(c.content), 60);
|
|
|
|
|
const dateStr = formatDateMDY(c.date);
|
|
|
|
|
report += `- [${c.nick}] ${content} (${dateStr}) -> ${c.link}\n`;
|
|
|
|
|
});
|
|
|
|
|
report += `\n`;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
2026-03-01 09:42:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (fs.existsSync(LINK_SUMMARY_PATH)) {
|
|
|
|
|
const linkSummary = fs.readFileSync(LINK_SUMMARY_PATH, 'utf8').trim();
|
|
|
|
|
report += `### 🔗 友链检测\n`;
|
2026-03-13 12:24:47 +08:00
|
|
|
const linkMeta = getFileMetaString(LINK_SUMMARY_PATH);
|
|
|
|
|
if (linkMeta) report += `- 结果: ${linkMeta}\n`;
|
|
|
|
|
report += `${linkSummary || '(无异常)'}\n\n`;
|
2026-03-01 09:42:39 +08:00
|
|
|
} else {
|
|
|
|
|
report += `### 🔗 友链检测\n(无检测结果)\n\n`;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Error reading link summary:", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (fs.existsSync(FRIEND_CIRCLE_PATH)) {
|
|
|
|
|
const data = JSON.parse(fs.readFileSync(FRIEND_CIRCLE_PATH, 'utf8'));
|
2026-03-13 12:24:47 +08:00
|
|
|
const sevenDaysAgo = new Date();
|
|
|
|
|
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
2026-03-01 09:42:39 +08:00
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
const allPostsSorted = (data.posts || [])
|
|
|
|
|
.map(p => {
|
|
|
|
|
const dt = parseDateFlexible(p && p.date);
|
|
|
|
|
return Object.assign({}, p, { __date: dt });
|
|
|
|
|
})
|
|
|
|
|
.filter(p => p.__date instanceof Date && !Number.isNaN(p.__date.getTime()))
|
|
|
|
|
.sort((a, b) => b.__date.getTime() - a.__date.getTime());
|
2026-03-01 09:42:39 +08:00
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
const recentPosts = allPostsSorted.filter(post => post.__date > sevenDaysAgo);
|
|
|
|
|
|
|
|
|
|
const authorSet = new Set(recentPosts.map(p => p.author).filter(Boolean));
|
|
|
|
|
|
|
|
|
|
report += `### 👥 朋友圈近况 (近7天)\n`;
|
|
|
|
|
const circleMeta = getFileMetaString(FRIEND_CIRCLE_PATH);
|
|
|
|
|
if (circleMeta) report += `- 数据: ${circleMeta}\n`;
|
|
|
|
|
report += `- 更新: ${recentPosts.length} 条, 活跃作者: ${authorSet.size}\n`;
|
|
|
|
|
const toShow = recentPosts.length > 0 ? recentPosts : allPostsSorted;
|
|
|
|
|
if (toShow.length === 0) {
|
2026-03-01 09:42:39 +08:00
|
|
|
report += `无更新\n`;
|
2026-03-13 12:24:47 +08:00
|
|
|
report += `\n`;
|
|
|
|
|
} else {
|
|
|
|
|
if (recentPosts.length === 0) {
|
|
|
|
|
report += `- 近7天无更新,展示最近10条:\n`;
|
|
|
|
|
}
|
|
|
|
|
const limitedPosts = toShow.slice(0, 10);
|
|
|
|
|
limitedPosts.forEach(post => {
|
|
|
|
|
const dateStr = formatDateMDY(post.__date);
|
|
|
|
|
const author = post.author || '未知作者';
|
|
|
|
|
const link = post.link || post.url || '';
|
|
|
|
|
const blogUrl = post.blogUrl ? ` ${post.blogUrl}` : '';
|
|
|
|
|
if (link) {
|
|
|
|
|
report += `- [${author}] \`${link}\` (${dateStr})${blogUrl}\n`;
|
|
|
|
|
} else {
|
|
|
|
|
report += `- [${author}] (${dateStr})${blogUrl}\n`;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (toShow.length > 10) {
|
|
|
|
|
report += `... 以及其他 ${toShow.length - 10} 篇\n`;
|
|
|
|
|
}
|
|
|
|
|
report += `\n`;
|
2026-03-01 09:42:39 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Error reading friend circle data:", e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 12:24:47 +08:00
|
|
|
report += `### 🧩 环境\n`;
|
|
|
|
|
report += `- Node: ${process.version}\n`;
|
|
|
|
|
if (process.env.RUNNER_OS) report += `- Runner: ${process.env.RUNNER_OS}\n`;
|
|
|
|
|
report += `\n`;
|
|
|
|
|
|
2026-03-01 09:42:39 +08:00
|
|
|
// Output to stdout for debugging
|
|
|
|
|
console.log(report);
|
|
|
|
|
|
|
|
|
|
// Output to GITHUB_ENV if running in Actions
|
|
|
|
|
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();
|