/** * 一次性脚本:把所有项目的 launchedAt 改成「该项目最早一次 commit 时间」。 * 没有绑定 repo / repo 里无 commit 的项目,默认 2026-01-01。 * * 用法:bun run scripts/backfill-launched-at.ts */ import { asc, eq, inArray } from 'drizzle-orm'; import { db } from '../src/db/index'; import { projects, projectRepos, gitCommits } from '../src/db/schema'; const DEFAULT_DATE = new Date('2026-01-01T00:00:00+08:00'); /** 抹除 .git 后缀和 URL 前缀,只保留仓库名 */ function normalizeRepoName(raw: string): string { let cleaned = raw.trim().replace(/\.git$/, ''); if (cleaned.includes('://')) { try { const parts = new URL(cleaned).pathname.split('/').filter(Boolean); return parts[parts.length - 1] || cleaned; } catch { /* fallthrough */ } } if (cleaned.includes('/')) return cleaned.split('/').pop() || cleaned; return cleaned; } const all = await db.select().from(projects); console.log(`Total projects: ${all.length}`); let withCommitsCount = 0, fallbackCount = 0; for (const p of all) { const repos = await db.select().from(projectRepos).where(eq(projectRepos.projectId, p.id)); const repoNames = repos.map(r => normalizeRepoName(r.repoName)); let launchedAt = DEFAULT_DATE; let source = 'default-2026-01-01'; if (repoNames.length > 0) { const earliest = await db.select({ committedAt: gitCommits.committedAt, repoName: gitCommits.repoName, sha: gitCommits.sha }) .from(gitCommits) .where(inArray(gitCommits.repoName, repoNames)) .orderBy(asc(gitCommits.committedAt)) .limit(1); if (earliest.length > 0 && earliest[0].committedAt) { launchedAt = earliest[0].committedAt instanceof Date ? earliest[0].committedAt : new Date(earliest[0].committedAt); source = `first commit ${earliest[0].repoName}/${earliest[0].sha?.slice(0, 7)}`; withCommitsCount += 1; } else { fallbackCount += 1; } } else { fallbackCount += 1; } await db.update(projects).set({ launchedAt, updatedAt: new Date(), }).where(eq(projects.id, p.id)); const label = `${p.identifier || p.id} (${p.name})`; console.log(` ${label.padEnd(50)} → ${launchedAt.toISOString().slice(0, 10)} [${source}]`); } console.log(''); console.log(`Done. with-commits=${withCommitsCount} fallback=${fallbackCount}`); process.exit(0);