biodsbench-adapter / src /harness /evaluation /sourcePublicIntegrity.ts
starpacker52's picture
Add files using upload-large-folder tool
57c9ddf verified
Raw
History Blame Contribute Delete
6.09 kB
import { lstat, mkdir, readFile, readdir, rm, stat, writeFile } from 'fs/promises'
import { dirname, join, relative, resolve } from 'path'
export type PublicSnapshotEntry = {
entry_type: 'file' | 'dir'
size: number
mtimeMs: number
content?: Buffer
}
export type PublicSnapshot = Map<string, PublicSnapshotEntry>
export type PublicSnapshotOptions = {
includeFileContents?: boolean
contentPathFilter?: (relativePath: string) => boolean
}
export type PublicMutation = {
type: 'created' | 'modified' | 'deleted'
path: string
entry_type: 'file' | 'dir'
}
export type PublicRestoreResult = {
removedCreatedPaths: string[]
restoredPaths: string[]
unrestoredPaths: string[]
}
function toPublicRelative(root: string, path: string): string {
return relative(root, path).replace(/\\/g, '/')
}
async function walkPublic(
root: string,
current: string,
snapshot: PublicSnapshot,
options: PublicSnapshotOptions,
): Promise<void> {
const entries = await readdir(current, { withFileTypes: true })
for (const entry of entries) {
const fullPath = join(current, entry.name)
const linkMetadata = await lstat(fullPath)
const isSymlink = linkMetadata.isSymbolicLink()
let metadata = linkMetadata
if (isSymlink) {
try {
metadata = await stat(fullPath)
} catch {
metadata = linkMetadata
}
}
const isDirectory = metadata.isDirectory()
const relativePath = toPublicRelative(root, fullPath)
const snapshotEntry: PublicSnapshotEntry = {
entry_type: isDirectory ? 'dir' : 'file',
size: linkMetadata.size,
mtimeMs: linkMetadata.mtimeMs,
}
if (
!isDirectory &&
!isSymlink &&
options.includeFileContents &&
(!options.contentPathFilter || options.contentPathFilter(relativePath))
) {
snapshotEntry.content = await readFile(fullPath)
}
snapshot.set(relativePath, snapshotEntry)
if (isDirectory && !isSymlink) {
await walkPublic(root, fullPath, snapshot, options)
}
}
}
export async function takePublicSnapshot(
publicDir: string,
options: PublicSnapshotOptions = {},
): Promise<PublicSnapshot> {
const snapshot: PublicSnapshot = new Map()
await walkPublic(publicDir, publicDir, snapshot, options)
return snapshot
}
export function diffPublicSnapshots(
before: PublicSnapshot,
after: PublicSnapshot,
limit = 50,
): PublicMutation[] {
const mutations: PublicMutation[] = []
for (const [path, afterEntry] of after.entries()) {
const beforeEntry = before.get(path)
if (!beforeEntry) {
mutations.push({
type: 'created',
path,
entry_type: afterEntry.entry_type,
})
} else if (
beforeEntry.entry_type !== afterEntry.entry_type ||
beforeEntry.size !== afterEntry.size ||
beforeEntry.mtimeMs !== afterEntry.mtimeMs ||
(beforeEntry.content !== undefined &&
afterEntry.content !== undefined &&
!beforeEntry.content.equals(afterEntry.content))
) {
mutations.push({
type: 'modified',
path,
entry_type: afterEntry.entry_type,
})
}
if (mutations.length >= limit) return mutations
}
for (const [path, beforeEntry] of before.entries()) {
if (!after.has(path)) {
mutations.push({
type: 'deleted',
path,
entry_type: beforeEntry.entry_type,
})
}
if (mutations.length >= limit) return mutations
}
return mutations
}
function isInside(path: string, parent: string): boolean {
const resolvedChild = resolve(path)
const resolvedBase = resolve(parent)
const child = process.platform === 'win32' ? resolvedChild.toLowerCase() : resolvedChild
const base = process.platform === 'win32' ? resolvedBase.toLowerCase() : resolvedBase
return child === base || child.startsWith(`${base}\\`) || child.startsWith(`${base}/`)
}
export async function cleanupCreatedPublicEntries(
publicDir: string,
mutations: PublicMutation[],
): Promise<string[]> {
const created = mutations
.filter(mutation => mutation.type === 'created')
.map(mutation => mutation.path)
.sort((a, b) => b.length - a.length)
const removed: string[] = []
for (const relativePath of created) {
const target = resolve(publicDir, relativePath)
if (!isInside(target, publicDir) || target === resolve(publicDir)) continue
await rm(target, { recursive: true, force: true })
removed.push(relativePath)
}
return removed
}
export async function restorePublicSnapshotMutations(
publicDir: string,
before: PublicSnapshot,
mutations: PublicMutation[],
): Promise<PublicRestoreResult> {
const result: PublicRestoreResult = {
removedCreatedPaths: [],
restoredPaths: [],
unrestoredPaths: [],
}
result.removedCreatedPaths = await cleanupCreatedPublicEntries(publicDir, mutations)
const deletedDirs = mutations
.filter(mutation => mutation.type === 'deleted')
.filter(mutation => before.get(mutation.path)?.entry_type === 'dir')
.map(mutation => mutation.path)
.sort((a, b) => a.length - b.length)
for (const relativePath of deletedDirs) {
const target = resolve(publicDir, relativePath)
if (!isInside(target, publicDir) || target === resolve(publicDir)) continue
await mkdir(target, { recursive: true })
result.restoredPaths.push(relativePath)
}
const filesToRestore = mutations
.filter(mutation => mutation.type === 'modified' || mutation.type === 'deleted')
.filter(mutation => before.get(mutation.path)?.entry_type === 'file')
.map(mutation => mutation.path)
.sort((a, b) => a.length - b.length)
for (const relativePath of filesToRestore) {
const beforeEntry = before.get(relativePath)
const target = resolve(publicDir, relativePath)
if (!beforeEntry?.content || !isInside(target, publicDir)) {
result.unrestoredPaths.push(relativePath)
continue
}
await mkdir(dirname(target), { recursive: true })
await writeFile(target, beforeEntry.content)
result.restoredPaths.push(relativePath)
}
return result
}