import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; function normalizeSlashes(value) { return value.replace(/\\/g, '/'); } function walkFiles(dir) { if (!fs.existsSync(dir)) return []; const files = []; for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const entryPath = path.join(dir, entry.name); if (entry.isDirectory()) files.push(...walkFiles(entryPath)); else files.push(entryPath); } return files; } export function hashArtifactDirectory(dir) { const files = walkFiles(dir) .map(filePath => ({ filePath, relativePath: normalizeSlashes(path.relative(dir, filePath)), })) .sort((left, right) => left.relativePath.localeCompare(right.relativePath, 'en')); const hash = crypto.createHash('sha256'); let totalBytes = 0; for (const file of files) { const content = fs.readFileSync(file.filePath); totalBytes += content.length; hash.update(file.relativePath, 'utf8'); hash.update('\0'); hash.update(String(content.length), 'utf8'); hash.update('\0'); hash.update(content); hash.update('\0'); } return { sha256: hash.digest('hex'), files: files.length, totalBytes, }; }