- 前端: React+Vite 首页/AI专区, 算法库(MARD色板/量化/背景去除), API对接 - 后端: Express+SQLite, 兑换码/订单/支付/chibi生成, 风控+云端同步 - CI/CD: GitHub Actions + Docker + nginx 配置 - 文档: PRD/DESIGN/LAUNCH/ROADMAP/E2E/竞品对照等 15 份 - 工程: 任务看板(taskboard) + 健康巡检(agent-health) + Git流程(gitflow)
124 lines
5.1 KiB
JavaScript
124 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* PERLER LAB · Git 集成脚本
|
|
* 状态机 → Git 提交工作流:
|
|
* node gitflow.js daily "提交说明" # 日常提交所有改动
|
|
* node gitflow.js approve T-001 # 任务验收通过 → 自动 commit + 建议 push
|
|
* node gitflow.js push # 推送当前分支到远端
|
|
* node gitflow.js status # 查看任务/代码状态
|
|
*/
|
|
'use strict';
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { execSync } = require('child_process');
|
|
|
|
const ROOT = path.resolve(__dirname);
|
|
const TASKS_FILE = path.join(__dirname, 'taskboard', 'tasks.json');
|
|
|
|
function run(cmd, silent = false) {
|
|
try {
|
|
const out = execSync(cmd, { cwd: ROOT, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
if (!silent && out.trim()) console.log(out.trim().split('\n').map(l => ' ' + l).join('\n'));
|
|
return { ok: true, out: out.trim() };
|
|
} catch (e) {
|
|
const msg = (e.stderr || e.stdout || e.message || '').trim();
|
|
if (!silent && msg) console.log(' ' + msg.split('\n').map(l => ' ' + l).join('\n'));
|
|
return { ok: false, out: msg };
|
|
}
|
|
}
|
|
|
|
function loadTasks() {
|
|
return JSON.parse(fs.readFileSync(TASKS_FILE, 'utf8'));
|
|
}
|
|
|
|
function ensureGitUser() {
|
|
const name = run('git config user.name', true).out;
|
|
const email = run('git config user.email', true).out;
|
|
if (!name || !email) {
|
|
console.log('[Git] 未配置提交身份,使用默认(可用 git config --global 覆盖)');
|
|
if (!name) run('git config user.name "Sisyphus"', true);
|
|
if (!email) run('git config user.email "sisyphus@perler-lab.local"', true);
|
|
}
|
|
}
|
|
|
|
function gitStatus() {
|
|
const { out } = run('git status --porcelain', true);
|
|
const files = out ? out.split('\n').filter(Boolean) : [];
|
|
const staged = files.filter(f => /^[MARD]/.test(f));
|
|
const unstaged = files.filter(f => /^.[MAD?]/.test(f));
|
|
return { all: files, staged, unstaged };
|
|
}
|
|
|
|
/* ---------- 命令 ---------- */
|
|
|
|
function cmdDaily(args) {
|
|
ensureGitUser();
|
|
const msg = args._.join(' ') || '日常提交';
|
|
const st = gitStatus();
|
|
if (!st.all.length) { console.log('[Git] 无任何改动,无需提交'); return; }
|
|
run('git add -A');
|
|
run(`git commit -m "${msg}"`);
|
|
console.log(`[Git] 日常提交完成: "${msg}" (${st.all.length} 个文件)`);
|
|
console.log('[Git] 提示: 运行 node gitflow.js push 推送到远端');
|
|
}
|
|
|
|
function cmdApprove(args) {
|
|
ensureGitUser();
|
|
const taskId = args._[0];
|
|
if (!taskId) { console.error('[错误] 用法: node gitflow.js approve <任务ID>'); process.exit(1); }
|
|
const db = loadTasks();
|
|
const task = db.tasks.find(t => t.id.toUpperCase() === String(taskId).toUpperCase());
|
|
if (!task) { console.error(`[错误] 任务 ${taskId} 不存在`); process.exit(1); }
|
|
if (task.status !== 'approved') {
|
|
console.error(`[错误] 任务 ${taskId} 当前状态为「${task.status}」,只有「验收通过」的任务才可上传`);
|
|
process.exit(1);
|
|
}
|
|
const st = gitStatus();
|
|
if (!st.all.length) { console.log(`[Git] 任务 ${taskId} 已验收通过,且代码无未提交改动`); return; }
|
|
run('git add -A');
|
|
const msg = `[验收通过] ${task.id} ${task.title}`;
|
|
const r = run(`git commit -m "${msg}"`);
|
|
if (!r.ok) { console.error('[Git] 提交失败'); process.exit(1); }
|
|
console.log(`[Git] 任务 ${taskId} 验收通过 → 已自动提交: "${msg}"`);
|
|
const remote = run('git remote get-url origin', true).out;
|
|
if (remote) {
|
|
const push = run('git push');
|
|
console.log(push.ok ? '[Git] 已推送到远端' : '[Git] 推送失败,请检查远端配置');
|
|
} else {
|
|
console.log('[Git] 未配置远端 (origin),跳过推送。配置: git remote add origin <仓库URL>');
|
|
}
|
|
}
|
|
|
|
function cmdPush() {
|
|
const remote = run('git remote get-url origin', true).out;
|
|
if (!remote) { console.error('[Git] 未配置远端,先执行: git remote add origin https://github.com/<用户>/<仓库>.git'); process.exit(1); }
|
|
const branch = run('git branch --show-current', true).out || 'main';
|
|
const r = run(`git push -u origin ${branch}`);
|
|
console.log(r.ok ? `[Git] 已推送 ${branch} 到 ${remote}` : '[Git] 推送失败');
|
|
}
|
|
|
|
function cmdStatus() {
|
|
const st = gitStatus();
|
|
console.log('=== Git 状态 ===');
|
|
if (!st.all.length) { console.log(' (工作区干净)'); }
|
|
st.all.forEach(f => console.log(' ' + f));
|
|
const remote = run('git remote get-url origin', true).out || '(未配置)';
|
|
const branch = run('git branch --show-current', true).out || '(无提交)';
|
|
console.log(`分支: ${branch} | 远端: ${remote}`);
|
|
const db = loadTasks();
|
|
const approved = db.tasks.filter(t => t.status === 'approved');
|
|
const pending = db.tasks.filter(t => ['in_progress', 'review'].includes(t.status));
|
|
console.log(`任务: 验收通过 ${approved.length} | 开发/待验收 ${pending.length}`);
|
|
}
|
|
|
|
/* ---------- 入口 ---------- */
|
|
const argv = process.argv.slice(2);
|
|
const cmd = argv[0];
|
|
const args = { _: argv.slice(1) };
|
|
const CMDS = { daily: cmdDaily, approve: cmdApprove, push: cmdPush, status: cmdStatus };
|
|
if (!CMDS[cmd]) {
|
|
console.log('用法: node gitflow.js <daily|approve|push|status> [参数]');
|
|
process.exit(1);
|
|
}
|
|
CMDS[cmd](args);
|