feat(release): add incremental gate and auto CI status for 103 publish
When the runtime artifact is unchanged, carry forward prior gate results and re-run only REL scenarios; resolve REL-02 from Gitea commit status and allow stable promotion when canary matches the same artifact bundle. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
|
||||
const ROOT = path.resolve(new URL('..', import.meta.url).pathname);
|
||||
|
||||
function git(args) {
|
||||
const result = spawnSync('git', args, { cwd: ROOT, encoding: 'utf8' });
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`git ${args.join(' ')} failed`);
|
||||
}
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
async function resolveRemote() {
|
||||
let remote = '';
|
||||
try {
|
||||
remote = git(['remote', 'get-url', 'origin']);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const slugMatch = remote.match(/(?:https?:\/\/[^/]+\/|git@[^:]+:)(.+?)(?:\.git)?$/);
|
||||
if (!slugMatch) return null;
|
||||
const slug = slugMatch[1].replace(/\.git$/, '');
|
||||
const [owner, repo] = slug.split('/');
|
||||
if (!owner || !repo) return null;
|
||||
const hostMatch = remote.match(/^git@([^:]+):/) || remote.match(/^https?:\/\/([^/]+)\//);
|
||||
const host = hostMatch?.[1] ?? 'git.tkmind.cn';
|
||||
const protocol = remote.startsWith('git@') ? 'https' : remote.split('://')[0];
|
||||
return `${protocol}://${host}/api/v1/repos/${owner}/${repo}`;
|
||||
}
|
||||
|
||||
async function queryGiteaCommitStatus(apiBase, commitSha) {
|
||||
const response = await fetch(`${apiBase}/commits/${commitSha}/status`, {
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Gitea commit status lookup failed: HTTP ${response.status}`);
|
||||
}
|
||||
const payload = await response.json();
|
||||
const state = String(payload?.state ?? '').trim().toLowerCase();
|
||||
if (state === 'success') return 'success';
|
||||
if (state === 'pending') return 'pending';
|
||||
return state || 'missing';
|
||||
}
|
||||
|
||||
export async function resolveReleaseCiStatus(commitSha) {
|
||||
const explicit = String(process.env.MEMIND_RELEASE_CI_STATUS ?? '').trim().toLowerCase();
|
||||
if (explicit) return explicit;
|
||||
|
||||
const apiBase = await resolveRemote();
|
||||
if (!apiBase) return 'missing';
|
||||
try {
|
||||
return await queryGiteaCommitStatus(apiBase, commitSha);
|
||||
} catch {
|
||||
return 'missing';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user