Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/ultra/dynamic-artifacts.ts

Raw
import { createHash } from "node:crypto";
import { lstatSync, readdirSync, readFileSync } from "node:fs";
import { basename, join, relative } from "node:path";

export function artifactFiles(root: string): string[] {
	const files: string[] = [];
	const visit = (dir: string): void => {
		for (const entry of readdirSync(dir, { withFileTypes: true })) {
			const path = join(dir, entry.name);
			if (entry.isSymbolicLink()) files.push(path);
			else if (entry.isDirectory()) visit(path);
			else files.push(path);
		}
	};
	visit(root);
	return files;
}

export function hashArtifacts(root: string): string {
	const hash = createHash("sha256");
	const files = artifactFiles(root)
		.filter((path) => basename(path) !== ".ultra-validation.json")
		.sort((left, right) =>
			relative(root, left)
				.replaceAll("\\", "/")
				.localeCompare(relative(root, right).replaceAll("\\", "/")),
		);
	for (const file of files) {
		if (lstatSync(file).isSymbolicLink())
			throw new Error(`symlink rejected: ${file}`);
		const relativePath = relative(root, file).replaceAll("\\", "/");
		const bytes = readFileSync(file);
		hash.update(String(Buffer.byteLength(relativePath)));
		hash.update(":");
		hash.update(relativePath);
		hash.update(":");
		hash.update(String(bytes.length));
		hash.update(":");
		hash.update(bytes);
	}
	return hash.digest("hex");
}