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';
|
||||
|
||||
import { createMetaGateRole, type GateMeta } from './roles/meta-gate.js';
|
||||
import { type MetaPromoterMeta } from './roles/meta-promoter.js';
|
||||
|
||||
// ── Meta Types ─────────────────────────────────────────────────
|
||||
|
||||
@ -49,9 +50,6 @@ export interface MetaTesterMeta {
|
||||
[key: string]: unknown;
|
||||
pass: boolean;
|
||||
reason: string;
|
||||
/** Only present when pass=true */
|
||||
commitHash?: string;
|
||||
pushed?: boolean;
|
||||
}
|
||||
|
||||
export type MetaWorkflowRoles = {
|
||||
@ -59,6 +57,7 @@ export type MetaWorkflowRoles = {
|
||||
coder: Role<MetaCoderMeta>;
|
||||
checker: Role<MetaCheckerMeta>;
|
||||
tester: Role<MetaTesterMeta>;
|
||||
promoter: Role<MetaPromoterMeta>;
|
||||
};
|
||||
|
||||
// ── Moderator ──────────────────────────────────────────────────
|
||||
@ -82,8 +81,10 @@ function metaModerator(
|
||||
}
|
||||
case 'tester': {
|
||||
const meta = input.meta as MetaTesterMeta | null;
|
||||
return meta?.pass ? END : 'coder';
|
||||
return meta?.pass ? 'promoter' : 'coder';
|
||||
}
|
||||
case 'promoter':
|
||||
return END;
|
||||
default:
|
||||
return END;
|
||||
}
|
||||
@ -103,12 +104,17 @@ function createDefaultMetaRoles(engineDir: string): MetaWorkflowRoles {
|
||||
content: 'e2e stub',
|
||||
meta: { pass: true, reason: 'e2e stub' },
|
||||
});
|
||||
const stubPromoter: Role<MetaPromoterMeta> = async () => ({
|
||||
content: 'promote stub',
|
||||
meta: { commitHash: 'stub', pushed: false, codeRev: 'stub' },
|
||||
});
|
||||
|
||||
return {
|
||||
gate: createMetaGateRole({ engineDir }),
|
||||
coder: stubCoder,
|
||||
checker: stubChecker,
|
||||
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)
|
||||
*/
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { existsSync, mkdtempSync, readdirSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
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 {
|
||||
content: `e2e 验证通过\n\n${summary}\n\nCommit: ${commitHash ?? 'none'}\nPushed: ${pushed ? 'yes' : 'no'}`,
|
||||
meta: { pass: true, reason: 'e2e verification passed', commitHash, pushed },
|
||||
content: `e2e 验证通过\n\n${summary}`,
|
||||
meta: { pass: true, reason: 'e2e verification passed' },
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user