📢 添加光湖系统介绍 + 系统公告区 + 自动更新工作流
- 重写 README.md:系统简介、架构图、人格体介绍、技术栈、团队信息、模块目录 - 添加系统公告区(BULLETIN_START/END 标记),自动显示模块上传和系统事件 - 新增 scripts/update-readme-bulletin.js:从 memory.json + GitHub API + git 日志聚合事件 - 新增 .github/workflows/update-readme-bulletin.yml:推送/工作流完成/定时触发自动更新公告 Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
40caf593c5
commit
7bd40b3e9a
|
|
@ -0,0 +1,75 @@
|
|||
# 光湖系统公告区自动更新工作流
|
||||
#
|
||||
# 触发条件:
|
||||
# 1. 推送到 main 分支 (模块代码变更)
|
||||
# 2. 其他工作流完成后 (CI/部署/巡检结果)
|
||||
# 3. 每日定时更新 (北京时间 09:00 / 21:00)
|
||||
# 4. 手动触发
|
||||
#
|
||||
# 功能:
|
||||
# 读取 memory.json + GitHub Actions API + git 日志,
|
||||
# 自动更新 README.md 的系统公告区
|
||||
|
||||
name: 📢 更新系统公告区
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'README.md' # 避免自身更新触发循环
|
||||
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "铸渊每日自检"
|
||||
- "📦 Module Doc + Notify"
|
||||
- "🚀 CD: Deploy to guanghulab.com"
|
||||
types: [completed]
|
||||
|
||||
schedule:
|
||||
# 北京时间 09:00 (UTC 01:00)
|
||||
- cron: '0 1 * * *'
|
||||
# 北京时间 21:00 (UTC 13:00)
|
||||
- cron: '0 13 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: update-bulletin
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-bulletin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 检出代码
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 50
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 🟢 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: 📢 更新公告区
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: node scripts/update-readme-bulletin.js
|
||||
|
||||
- name: 📝 提交更新
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add README.md
|
||||
if git diff --cached --quiet; then
|
||||
echo "📢 公告区无变化,跳过提交"
|
||||
else
|
||||
git commit -m "📢 自动更新系统公告区 [skip ci]"
|
||||
git push
|
||||
echo "✅ 公告区已更新并推送"
|
||||
fi
|
||||
226
README.md
226
README.md
|
|
@ -1,47 +1,183 @@
|
|||
# HoloLake 光湖系统
|
||||
<div align="center">
|
||||
|
||||
## 模块目录
|
||||
# 🌊 HoloLake 光湖系统
|
||||
|
||||
- app/
|
||||
- backend-integration/
|
||||
- backend/
|
||||
- broadcasts-outbox/
|
||||
- chat-bubble/
|
||||
- cloud-drive/
|
||||
- coldstart/
|
||||
- cost-control/
|
||||
- dashboard/
|
||||
- dev-nodes/
|
||||
- dingtalk-bot/
|
||||
- docs/
|
||||
- frontend/
|
||||
- guanghulab-main/
|
||||
- help-center/
|
||||
- m01-login/
|
||||
- m03-personality/
|
||||
- m05-user-center/
|
||||
- m06-ticket/
|
||||
- m07-dialogue-ui/
|
||||
- m10-cloud/
|
||||
- m11-module/
|
||||
- m12-kanban/
|
||||
- m15-cloud-drive/
|
||||
- m18-health-check/
|
||||
- multi-persona/
|
||||
- notification/
|
||||
- notion-push/
|
||||
- persona-selector/
|
||||
- portal/
|
||||
- public/
|
||||
- reports/
|
||||
- scripts/
|
||||
- settings/
|
||||
- signal-log/
|
||||
- src/
|
||||
- status-board/
|
||||
- style-system/
|
||||
- syslog-inbox/
|
||||
- syslog-processed/
|
||||
- tests/
|
||||
- ticket-system/
|
||||
- user-center/
|
||||
**人格语言操作系统(AGE OS) · 壳-核分离架构**
|
||||
|
||||
[](https://github.com/qinfendebingshuo/guanghulab/actions/workflows/zhuyuan-daily-selfcheck.yml)
|
||||
[](https://github.com/qinfendebingshuo/guanghulab/actions/workflows/deploy-to-server.yml)
|
||||
|
||||
`guanghulab.com` · Node.js 20 + Express + PM2 + Nginx
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📖 系统简介
|
||||
|
||||
**光湖(HoloLake)** 是一个基于 **人格语言操作系统(AGE OS)** 的智能协作平台,采用 **壳-核分离** 设计理念:
|
||||
|
||||
- **壳(Shell)**:前端交互层,包括对话界面、用户中心、工单系统、云盘等模块
|
||||
- **核(Core)**:后端智能层,包括人格引擎、广播分发、信号处理、Notion 桥接等
|
||||
|
||||
### 🏛️ 核心架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 光湖 HoloLake │
|
||||
├──────────────┬──────────────────────────────────┤
|
||||
│ 壳 Shell │ 核 Core │
|
||||
│ │ │
|
||||
│ 🖥️ 对话 UI │ 🧠 铸渊 (Zhùyuān) 代码守护人格 │
|
||||
│ 👤 用户中心 │ 📡 广播分发系统 │
|
||||
│ 🎫 工单系统 │ 🔔 信号处理 + Notion 桥接 │
|
||||
│ ☁️ 云盘 │ 🔄 CI/CD 自动化流水线 │
|
||||
│ 📊 状态看板 │ 📋 模块自检 + 文档生成 │
|
||||
└──────────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 🤖 智能人格体
|
||||
|
||||
| 人格体 | 角色 | 职责 |
|
||||
|--------|------|------|
|
||||
| **铸渊 Zhùyuān** | 代码守护者 | 代码审查、CI 巡检、模块协议执行、Issue 回复 |
|
||||
| **冰朔 Bīng Shuò** | 系统创建者 | 系统架构设计、核心决策、广播发布 |
|
||||
| **霜砚 Shuāng Yàn** | 人格导师 | 人格调校、风格管理、联觉语言系统 |
|
||||
|
||||
### 🔧 技术栈
|
||||
|
||||
- **运行时**:Node.js 20 + Next.js 15 + React 19
|
||||
- **后端**:Express + PM2 进程管理
|
||||
- **数据库**:SQLite (better-sqlite3) + Notion 数据桥接
|
||||
- **部署**:Nginx 反向代理 + GitHub Actions CI/CD
|
||||
- **自动化**:23+ GitHub Actions 工作流
|
||||
|
||||
---
|
||||
|
||||
## 📢 系统公告区
|
||||
|
||||
> 🔄 此区域由 GitHub Actions 自动更新,显示最近的模块上传、系统事件和版本变更。
|
||||
>
|
||||
> 合作者每次进入仓库首页即可查看最新动态。
|
||||
|
||||
<!-- BULLETIN_START -->
|
||||
| 时间 | 事件 | 详情 |
|
||||
|------|------|------|
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `backend-integration/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `backend/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `cloud-drive/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `dingtalk-bot/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `docs/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `frontend/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m01-login/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m03-personality/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m05-user-center/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m06-ticket/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m07-dialogue-ui/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m10-cloud/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m11-module/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m12-kanban/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m15-cloud-drive/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `m18-health-check/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `notification/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `scripts/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `status-board/` |
|
||||
| 03-09 17:01 | 📦 铸渊 (ZhùYuān) | 模块更新: `ticket-system/` |
|
||||
<!-- BULLETIN_END -->
|
||||
|
||||
---
|
||||
|
||||
## 👥 开发团队
|
||||
|
||||
| 编号 | 成员 | 角色 | 负责模块 |
|
||||
|------|------|------|----------|
|
||||
| DEV-001 | 🛠️ 页页 | 后端中间件 | backend-integration |
|
||||
| DEV-002 | 🐱 肥猫 | 前端 + 副操 | m01-login, m03-personality |
|
||||
| DEV-003 | 🎨 燕樊 | 对话 UI | m07-dialogue-ui, m15-cloud-drive |
|
||||
| DEV-004 | 🤖 之之 | 钉钉机器人 | dingtalk-bot |
|
||||
| DEV-005 | 🍓 小草莓 | 状态看板 | status-board, m12-kanban |
|
||||
| DEV-009 | 🌸 花尔 | 用户中心 | m05-user-center |
|
||||
| DEV-010 | 🍊 桔子 | 前端主力 | m06-ticket, ticket-system |
|
||||
| DEV-011 | ✍️ 匆匆那年 | 写作工坊 | — |
|
||||
| DEV-012 | 🌟 Awen | 通知中心 | notification |
|
||||
|
||||
---
|
||||
|
||||
## 📦 模块目录
|
||||
|
||||
<details>
|
||||
<summary>点击展开完整模块列表(47+ 模块)</summary>
|
||||
|
||||
### 核心功能模块
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| `m01-login/` | 登录系统 |
|
||||
| `m03-personality/` | 人格系统 |
|
||||
| `m05-user-center/` | 用户中心 |
|
||||
| `m06-ticket/` | 工单系统 |
|
||||
| `m07-dialogue-ui/` | 对话界面 |
|
||||
| `m10-cloud/` | 云服务 |
|
||||
| `m11-module/` | 模块管理 |
|
||||
| `m12-kanban/` | 看板系统 |
|
||||
| `m15-cloud-drive/` | 云盘 |
|
||||
| `m18-health-check/` | 健康检查 |
|
||||
|
||||
### 基础设施
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| `backend/` | 后端服务 |
|
||||
| `backend-integration/` | 后端集成中间件 |
|
||||
| `frontend/` | 前端主体 |
|
||||
| `status-board/` | 状态看板 |
|
||||
| `dingtalk-bot/` | 钉钉机器人 |
|
||||
| `notification/` | 通知系统 |
|
||||
|
||||
### 系统支撑
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| `scripts/` | 自动化脚本 |
|
||||
| `tests/` | 契约测试 + 冒烟测试 |
|
||||
| `docs/` | 文档中心 |
|
||||
| `broadcasts-outbox/` | 广播发件箱 |
|
||||
| `syslog-inbox/` | 系统日志收件箱 |
|
||||
| `persona-selector/` | 人格选择器 |
|
||||
| `signal-log/` | 信号日志 |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 本地开发
|
||||
npm run dev
|
||||
|
||||
# 运行契约测试
|
||||
npm run test:contract
|
||||
|
||||
# 运行冒烟测试
|
||||
npm run test:smoke
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 系统状态
|
||||
|
||||
- **每日巡检**:铸渊 PSP 自动巡检(每日 08:30 UTC)
|
||||
- **模块自检**:推送时自动检测模块完整性(README.md + package.json + src/)
|
||||
- **部署流水线**:推送到 main → 验证 → 同步 → 重启 → Notion 通知
|
||||
- **广播系统**:冰朔广播 → 分发至各开发者 outbox → 邮件/钉钉通知
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**光湖 HoloLake** · 由冰朔创建 · 铸渊守护
|
||||
|
||||
*壳-核分离 · 人格共生 · 协作共建*
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
/**
|
||||
* 光湖系统公告区自动更新脚本
|
||||
*
|
||||
* 读取 memory.json 事件 + GitHub Actions 最近工作流运行记录,
|
||||
* 自动更新 README.md 中 <!-- BULLETIN_START --> 和 <!-- BULLETIN_END --> 之间的公告区域。
|
||||
*
|
||||
* 环境变量:
|
||||
* GITHUB_TOKEN - GitHub API token (Actions 自动提供)
|
||||
* GITHUB_REPOSITORY - owner/repo (Actions 自动提供)
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/update-readme-bulletin.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const README_PATH = path.join(__dirname, '..', 'README.md');
|
||||
const MEMORY_PATH = path.join(__dirname, '..', '.github', 'brain', 'memory.json');
|
||||
const BULLETIN_START = '<!-- BULLETIN_START -->';
|
||||
const BULLETIN_END = '<!-- BULLETIN_END -->';
|
||||
const MAX_ENTRIES = 20;
|
||||
|
||||
/* ── 开发者名册 ─────────────────────────── */
|
||||
const DEV_MAP = {
|
||||
'DEV-001': '🛠️ 页页',
|
||||
'DEV-002': '🐱 肥猫',
|
||||
'DEV-003': '🎨 燕樊',
|
||||
'DEV-004': '🤖 之之',
|
||||
'DEV-005': '🍓 小草莓',
|
||||
'DEV-009': '🌸 花尔',
|
||||
'DEV-010': '🍊 桔子',
|
||||
'DEV-011': '✍️ 匆匆那年',
|
||||
'DEV-012': '🌟 Awen',
|
||||
};
|
||||
|
||||
const ACTOR_MAP = {
|
||||
'qinfendebingshuo': '冰朔',
|
||||
'copilot-swe-agent[bot]': '铸渊 (Copilot)',
|
||||
};
|
||||
|
||||
/* ── 模块路径映射 ─────────────────────────── */
|
||||
const MODULE_PREFIXES = [
|
||||
'm01-login', 'm03-personality', 'm05-user-center', 'm06-ticket',
|
||||
'm07-dialogue-ui', 'm10-cloud', 'm11-module', 'm12-kanban',
|
||||
'm15-cloud-drive', 'm18-health-check', 'dingtalk-bot',
|
||||
'backend-integration', 'status-board', 'backend', 'frontend',
|
||||
'notification', 'docs', 'scripts', 'ticket-system', 'cloud-drive',
|
||||
];
|
||||
|
||||
/* ── 工具函数 ─────────────────────────── */
|
||||
|
||||
function formatTime(ts) {
|
||||
if (!ts) return '—';
|
||||
const d = new Date(ts);
|
||||
if (isNaN(d.getTime())) return ts;
|
||||
const bj = new Date(d.getTime() + 8 * 3600000);
|
||||
const mm = String(bj.getUTCMonth() + 1).padStart(2, '0');
|
||||
const dd = String(bj.getUTCDate()).padStart(2, '0');
|
||||
const hh = String(bj.getUTCHours()).padStart(2, '0');
|
||||
const mi = String(bj.getUTCMinutes()).padStart(2, '0');
|
||||
return `${mm}-${dd} ${hh}:${mi}`;
|
||||
}
|
||||
|
||||
function statusIcon(result) {
|
||||
if (!result) return '🔵';
|
||||
const r = result.toLowerCase();
|
||||
if (r === 'passed' || r === 'success' || r === 'completed') return '✅';
|
||||
if (r === 'failed' || r === 'failure') return '❌';
|
||||
if (r === 'cancelled') return '⏹️';
|
||||
return '🔵';
|
||||
}
|
||||
|
||||
function resolveActor(actor) {
|
||||
if (!actor) return '系统';
|
||||
return ACTOR_MAP[actor] || actor;
|
||||
}
|
||||
|
||||
/* ── 从 memory.json 读取事件 ─────────────────────────── */
|
||||
|
||||
function loadMemoryEvents() {
|
||||
if (!fs.existsSync(MEMORY_PATH)) return [];
|
||||
const memory = JSON.parse(fs.readFileSync(MEMORY_PATH, 'utf8'));
|
||||
const events = memory.events || [];
|
||||
return events.map(ev => {
|
||||
const ts = ev.timestamp || ev.date || '';
|
||||
const actor = resolveActor(ev.actor || ev.by);
|
||||
const type = ev.type || 'event';
|
||||
|
||||
let icon, detail;
|
||||
switch (type) {
|
||||
case 'daily_check':
|
||||
icon = statusIcon(ev.result);
|
||||
detail = `铸渊每日巡检 ${ev.result === 'passed' ? '通过' : '异常'}`;
|
||||
break;
|
||||
case 'ci_run':
|
||||
icon = statusIcon(ev.result);
|
||||
detail = `CI 构建 ${ev.result === 'passed' ? '通过' : ev.result === 'unknown' ? '状态未知' : '失败'}`;
|
||||
break;
|
||||
case 'psp_inspection':
|
||||
icon = ev.description?.includes('通过') ? '✅' : '⚠️';
|
||||
detail = ev.description || 'PSP 巡检';
|
||||
break;
|
||||
case 'system_build':
|
||||
icon = '🚀';
|
||||
detail = ev.title || '系统构建';
|
||||
break;
|
||||
case 'brain_upgrade':
|
||||
icon = '🧠';
|
||||
detail = ev.title || ev.description || '大脑升级';
|
||||
break;
|
||||
case 'module_upload':
|
||||
icon = statusIcon(ev.result);
|
||||
detail = `模块上传: ${ev.module || '未知模块'}`;
|
||||
break;
|
||||
default:
|
||||
icon = '📋';
|
||||
detail = ev.title || ev.description || type;
|
||||
}
|
||||
|
||||
return { ts, icon, actor, detail, sortKey: new Date(ts || '2000-01-01').getTime() };
|
||||
});
|
||||
}
|
||||
|
||||
/* ── 从 GitHub Actions API 获取最近工作流运行 ─────────────────────────── */
|
||||
|
||||
async function fetchRecentWorkflowRuns() {
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repo = process.env.GITHUB_REPOSITORY;
|
||||
if (!token || !repo) {
|
||||
console.log('⚠️ GITHUB_TOKEN 或 GITHUB_REPOSITORY 未设置,跳过 API 查询');
|
||||
return [];
|
||||
}
|
||||
|
||||
const url = `https://api.github.com/repos/${repo}/actions/runs?per_page=15&status=completed`;
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.log(`⚠️ GitHub API 响应 ${res.status},跳过工作流数据`);
|
||||
return [];
|
||||
}
|
||||
const data = await res.json();
|
||||
const runs = data.workflow_runs || [];
|
||||
|
||||
return runs.map(run => {
|
||||
const actor = resolveActor(run.actor?.login);
|
||||
const conclusion = run.conclusion || 'unknown';
|
||||
const icon = statusIcon(conclusion);
|
||||
const wfName = run.name || '工作流';
|
||||
const branch = run.head_branch || 'main';
|
||||
const detail = `${wfName} · ${branch} · ${conclusion === 'success' ? '成功' : conclusion === 'failure' ? '失败' : conclusion}`;
|
||||
|
||||
return {
|
||||
ts: run.updated_at || run.created_at,
|
||||
icon,
|
||||
actor,
|
||||
detail,
|
||||
sortKey: new Date(run.updated_at || run.created_at).getTime(),
|
||||
};
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(`⚠️ 获取工作流数据失败: ${err.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 从最近的 git 记录检测模块推送 ─────────────────────────── */
|
||||
|
||||
function detectRecentModulePushes() {
|
||||
const { execSync } = require('child_process');
|
||||
const entries = [];
|
||||
|
||||
try {
|
||||
const log = execSync(
|
||||
'git log --oneline --name-only --since="7 days ago" -30 2>/dev/null || true',
|
||||
{ encoding: 'utf8', cwd: path.join(__dirname, '..') }
|
||||
);
|
||||
|
||||
const lines = log.split('\n');
|
||||
let currentCommit = null;
|
||||
let currentActor = null;
|
||||
const moduleChanges = new Map();
|
||||
|
||||
for (const line of lines) {
|
||||
const commitMatch = line.match(/^([a-f0-9]+)\s+(.*)$/);
|
||||
if (commitMatch) {
|
||||
currentCommit = commitMatch[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line.trim()) continue;
|
||||
|
||||
for (const prefix of MODULE_PREFIXES) {
|
||||
if (line.startsWith(prefix + '/') || line === prefix) {
|
||||
if (!moduleChanges.has(prefix)) {
|
||||
moduleChanges.set(prefix, currentCommit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [mod, commit] of moduleChanges) {
|
||||
try {
|
||||
const info = execSync(
|
||||
`git log -1 --format="%aI|%an" ${commit} 2>/dev/null || true`,
|
||||
{ encoding: 'utf8', cwd: path.join(__dirname, '..') }
|
||||
).trim();
|
||||
const [ts, author] = info.split('|');
|
||||
entries.push({
|
||||
ts,
|
||||
icon: '📦',
|
||||
actor: resolveActor(author) || author,
|
||||
detail: `模块更新: \`${mod}/\``,
|
||||
sortKey: new Date(ts).getTime(),
|
||||
});
|
||||
} catch (_) { /* skip */ }
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`⚠️ Git 日志读取失败: ${err.message}`);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/* ── 生成公告表格 ─────────────────────────── */
|
||||
|
||||
function buildBulletinTable(entries) {
|
||||
entries.sort((a, b) => b.sortKey - a.sortKey);
|
||||
|
||||
const seen = new Set();
|
||||
const unique = [];
|
||||
for (const e of entries) {
|
||||
const key = `${e.detail}|${formatTime(e.ts)}`;
|
||||
if (!seen.has(key)) {
|
||||
seen.add(key);
|
||||
unique.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
const display = unique.slice(0, MAX_ENTRIES);
|
||||
|
||||
if (display.length === 0) {
|
||||
return '| 时间 | 事件 | 详情 |\n|------|------|------|\n| 🕐 暂无记录 | — | 公告系统已就绪 |';
|
||||
}
|
||||
|
||||
const rows = display.map(e =>
|
||||
`| ${formatTime(e.ts)} | ${e.icon} ${e.actor} | ${e.detail} |`
|
||||
);
|
||||
|
||||
return `| 时间 | 事件 | 详情 |\n|------|------|------|\n${rows.join('\n')}`;
|
||||
}
|
||||
|
||||
/* ── 更新 README.md ─────────────────────────── */
|
||||
|
||||
function updateReadme(bulletinContent) {
|
||||
if (!fs.existsSync(README_PATH)) {
|
||||
console.error('❌ README.md 不存在');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const readme = fs.readFileSync(README_PATH, 'utf8');
|
||||
const startIdx = readme.indexOf(BULLETIN_START);
|
||||
const endIdx = readme.indexOf(BULLETIN_END);
|
||||
|
||||
if (startIdx === -1 || endIdx === -1) {
|
||||
console.error('❌ README.md 中未找到公告区标记 (BULLETIN_START / BULLETIN_END)');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const before = readme.substring(0, startIdx + BULLETIN_START.length);
|
||||
const after = readme.substring(endIdx);
|
||||
const updated = `${before}\n${bulletinContent}\n${after}`;
|
||||
|
||||
if (updated === readme) {
|
||||
console.log('ℹ️ 公告区内容无变化,跳过写入');
|
||||
return false;
|
||||
}
|
||||
|
||||
fs.writeFileSync(README_PATH, updated, 'utf8');
|
||||
console.log('✅ README.md 公告区已更新');
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ── 主流程 ─────────────────────────── */
|
||||
|
||||
async function main() {
|
||||
console.log('🌊 光湖系统公告区更新脚本启动...\n');
|
||||
|
||||
const memoryEvents = loadMemoryEvents();
|
||||
console.log(`📋 memory.json 事件: ${memoryEvents.length} 条`);
|
||||
|
||||
const workflowEvents = await fetchRecentWorkflowRuns();
|
||||
console.log(`🔄 工作流运行记录: ${workflowEvents.length} 条`);
|
||||
|
||||
const moduleEvents = detectRecentModulePushes();
|
||||
console.log(`📦 模块推送记录: ${moduleEvents.length} 条`);
|
||||
|
||||
const allEvents = [...memoryEvents, ...workflowEvents, ...moduleEvents];
|
||||
console.log(`📊 合计事件: ${allEvents.length} 条\n`);
|
||||
|
||||
const table = buildBulletinTable(allEvents);
|
||||
const changed = updateReadme(table);
|
||||
|
||||
if (changed) {
|
||||
console.log('\n📢 公告区更新完成!');
|
||||
} else {
|
||||
console.log('\n📢 公告区无需更新。');
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('❌ 脚本执行失败:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Reference in New Issue