feat: promoter role — code_rev from engine git commit (#3)
This commit is contained in:
parent
04a8683d0a
commit
be380a53ca
@ -29,6 +29,7 @@ import {
|
|||||||
} from '@uncaged/pulse';
|
} from '@uncaged/pulse';
|
||||||
|
|
||||||
import { createMetaGateRole, type GateMeta } from './roles/meta-gate.js';
|
import { createMetaGateRole, type GateMeta } from './roles/meta-gate.js';
|
||||||
|
import { type MetaPromoterMeta } from './roles/meta-promoter.js';
|
||||||
|
|
||||||
// ── Meta Types ─────────────────────────────────────────────────
|
// ── Meta Types ─────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -49,9 +50,6 @@ export interface MetaTesterMeta {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
pass: boolean;
|
pass: boolean;
|
||||||
reason: string;
|
reason: string;
|
||||||
/** Only present when pass=true */
|
|
||||||
commitHash?: string;
|
|
||||||
pushed?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MetaWorkflowRoles = {
|
export type MetaWorkflowRoles = {
|
||||||
@ -59,6 +57,7 @@ export type MetaWorkflowRoles = {
|
|||||||
coder: Role<MetaCoderMeta>;
|
coder: Role<MetaCoderMeta>;
|
||||||
checker: Role<MetaCheckerMeta>;
|
checker: Role<MetaCheckerMeta>;
|
||||||
tester: Role<MetaTesterMeta>;
|
tester: Role<MetaTesterMeta>;
|
||||||
|
promoter: Role<MetaPromoterMeta>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Moderator ──────────────────────────────────────────────────
|
// ── Moderator ──────────────────────────────────────────────────
|
||||||
@ -82,8 +81,10 @@ function metaModerator(
|
|||||||
}
|
}
|
||||||
case 'tester': {
|
case 'tester': {
|
||||||
const meta = input.meta as MetaTesterMeta | null;
|
const meta = input.meta as MetaTesterMeta | null;
|
||||||
return meta?.pass ? END : 'coder';
|
return meta?.pass ? 'promoter' : 'coder';
|
||||||
}
|
}
|
||||||
|
case 'promoter':
|
||||||
|
return END;
|
||||||
default:
|
default:
|
||||||
return END;
|
return END;
|
||||||
}
|
}
|
||||||
@ -103,12 +104,17 @@ function createDefaultMetaRoles(engineDir: string): MetaWorkflowRoles {
|
|||||||
content: 'e2e stub',
|
content: 'e2e stub',
|
||||||
meta: { pass: true, reason: 'e2e stub' },
|
meta: { pass: true, reason: 'e2e stub' },
|
||||||
});
|
});
|
||||||
|
const stubPromoter: Role<MetaPromoterMeta> = async () => ({
|
||||||
|
content: 'promote stub',
|
||||||
|
meta: { commitHash: 'stub', pushed: false, codeRev: 'stub' },
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gate: createMetaGateRole({ engineDir }),
|
gate: createMetaGateRole({ engineDir }),
|
||||||
coder: stubCoder,
|
coder: stubCoder,
|
||||||
checker: stubChecker,
|
checker: stubChecker,
|
||||||
tester: stubTester,
|
tester: stubTester,
|
||||||
|
promoter: stubPromoter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
80
src/workflows/roles/meta-promoter.ts
Normal file
80
src/workflows/roles/meta-promoter.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Meta Promoter role — commit, push, and emit promote event.
|
||||||
|
* code_rev = engine repo git commit hash.
|
||||||
|
*
|
||||||
|
* 小橘 🍊 (NEKO Team)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
import type { Role, RoleResult, WorkflowMessage } from '@uncaged/pulse';
|
||||||
|
|
||||||
|
export interface MetaPromoterMeta {
|
||||||
|
[key: string]: unknown;
|
||||||
|
commitHash: string;
|
||||||
|
pushed: boolean;
|
||||||
|
codeRev: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMetaPromoterRole(opts: {
|
||||||
|
repoDir: string;
|
||||||
|
remote?: string;
|
||||||
|
branch?: string;
|
||||||
|
}): Role<MetaPromoterMeta> {
|
||||||
|
const branch = opts.branch ?? 'main';
|
||||||
|
|
||||||
|
return async (chain: WorkflowMessage[]): Promise<RoleResult<MetaPromoterMeta>> => {
|
||||||
|
const cwd = opts.repoDir;
|
||||||
|
const exec = (cmd: string) =>
|
||||||
|
execSync(cmd, { cwd, encoding: 'utf-8', timeout: 30_000 }).trim();
|
||||||
|
|
||||||
|
// Get commit message from __start__
|
||||||
|
const startMsg = chain.find((m) => m.role === '__start__');
|
||||||
|
const firstLine = (startMsg?.content ?? '').split('\n')[0].slice(0, 60);
|
||||||
|
const commitMsg = firstLine || 'meta workflow auto-commit';
|
||||||
|
|
||||||
|
// Stage all changes
|
||||||
|
exec('git add -A');
|
||||||
|
|
||||||
|
let commitHash: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec('git diff --cached --quiet');
|
||||||
|
// No changes to commit
|
||||||
|
commitHash = exec('git rev-parse --short HEAD');
|
||||||
|
} catch {
|
||||||
|
// Has staged changes — commit
|
||||||
|
exec(`git commit -m "${commitMsg}" --author="小橘 <xiaoju@shazhou.work>"`);
|
||||||
|
commitHash = exec('git rev-parse --short HEAD');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full hash for code_rev
|
||||||
|
const fullHash = exec('git rev-parse HEAD');
|
||||||
|
|
||||||
|
// Push
|
||||||
|
let pushed = false;
|
||||||
|
const remote = opts.remote ?? (() => {
|
||||||
|
try {
|
||||||
|
const remotes = exec('git remote').split('\n').filter(Boolean);
|
||||||
|
return remotes[0] || null;
|
||||||
|
} catch { return null; }
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (remote) {
|
||||||
|
try {
|
||||||
|
exec(`git push ${remote} ${branch} --no-verify`);
|
||||||
|
pushed = true;
|
||||||
|
} catch {
|
||||||
|
pushed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: `promote: ${commitHash} (pushed: ${pushed})`,
|
||||||
|
meta: {
|
||||||
|
commitHash,
|
||||||
|
pushed,
|
||||||
|
codeRev: fullHash,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@
|
|||||||
* 小橘 🍊 (NEKO Team)
|
* 小橘 🍊 (NEKO Team)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { execSync } from 'node:child_process';
|
|
||||||
import { existsSync, mkdtempSync, readdirSync } from 'node:fs';
|
import { existsSync, mkdtempSync, readdirSync } from 'node:fs';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
@ -186,60 +185,9 @@ export function createMetaTesterRole(opts: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: All pass — commit + push
|
|
||||||
let commitHash: string | undefined;
|
|
||||||
let pushed: boolean | undefined;
|
|
||||||
|
|
||||||
const exec = (cmd: string) =>
|
|
||||||
execSync(cmd, { cwd, encoding: 'utf-8', timeout: 30_000 }).trim();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const startMsg = chain.find((m) => m.role === '__start__');
|
|
||||||
const firstLine = (startMsg?.content ?? '').split('\n')[0].slice(0, 60);
|
|
||||||
const commitMsg = firstLine || 'meta workflow auto-commit';
|
|
||||||
|
|
||||||
exec('git add -A');
|
|
||||||
|
|
||||||
// Check if there's anything to commit
|
|
||||||
try {
|
|
||||||
exec('git diff --cached --quiet');
|
|
||||||
// No changes — still pass, just no commit needed
|
|
||||||
commitHash = exec('git rev-parse --short HEAD');
|
|
||||||
pushed = false;
|
|
||||||
} catch {
|
|
||||||
// There are staged changes
|
|
||||||
exec(
|
|
||||||
`git commit -m "${commitMsg}" --author="小橘 <xiaoju@shazhou.work>"`,
|
|
||||||
);
|
|
||||||
commitHash = exec('git rev-parse --short HEAD');
|
|
||||||
|
|
||||||
// Auto-detect remote
|
|
||||||
const remote = opts.remote ?? (() => {
|
|
||||||
try {
|
|
||||||
const remotes = exec('git remote').split('\n').filter(Boolean);
|
|
||||||
return remotes[0] || null;
|
|
||||||
} catch { return null; }
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (remote) {
|
|
||||||
try {
|
|
||||||
exec(`git push ${remote} ${branch} --no-verify`);
|
|
||||||
pushed = true;
|
|
||||||
} catch {
|
|
||||||
pushed = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pushed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
commitHash = undefined;
|
|
||||||
pushed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: `e2e 验证通过\n\n${summary}\n\nCommit: ${commitHash ?? 'none'}\nPushed: ${pushed ? 'yes' : 'no'}`,
|
content: `e2e 验证通过\n\n${summary}`,
|
||||||
meta: { pass: true, reason: 'e2e verification passed', commitHash, pushed },
|
meta: { pass: true, reason: 'e2e verification passed' },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user