Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/build-helpers-ifexists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@geonovum/workflow-build-runner": patch
---

Voeg pathExists en emptyDir toe aan publieke build-helpers; copyFile en copyDir ondersteunen nu optie ifExists
5 changes: 5 additions & 0 deletions .changeset/unzip-shell-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@geonovum/workflow-build-runner": patch
---

`unzipToDirectory` gebruikt nu bij voorkeur de shell `unzip`-binary; valt terug op `zip-lib` als die niet beschikbaar is. Lost een hang op die optreedt bij yauzl-extracts onder Docker overlay met Node 26+, waarbij de extract-Promise nooit settled en het proces stilletjes exit-eert met code 0 na een gedeeltelijke extractie.
43 changes: 33 additions & 10 deletions src/support/build-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const { SaxonJS } = require("./dependencies");
const {
fsp,
ensureDir,
pathExists,
pathExists: _pathExists,
emptyDir: _emptyDir,
unzipToDirectory,
zipDirectory,
} = require("./filesystem");
Expand All @@ -23,6 +24,14 @@ const {
const { xmlProperty } = require("./xml");
const { compileStylesheetToSef, createCollectionFinder } = require("./xslt");

async function pathExists(filePath) {
return _pathExists(path.resolve(String(filePath || "")));
}

async function emptyDir(dirName) {
await _emptyDir(path.resolve(String(dirName || "")));
}

async function makeDir(dirName) {
await ensureDir(path.resolve(String(dirName || "")));
}
Expand All @@ -34,19 +43,31 @@ async function deleteDir(dirName) {
});
}

async function copyDir(sourceDirName, destinationDirName) {
await fsp.cp(
path.resolve(String(sourceDirName || "")),
path.resolve(String(destinationDirName || "")),
{ recursive: true, force: true },
);
async function copyDir(
sourceDirName,
destinationDirName,
{ ifExists = false } = {},
) {
const sourceDir = path.resolve(String(sourceDirName || ""));
if (ifExists && !(await _pathExists(sourceDir))) return false;
await fsp.cp(sourceDir, path.resolve(String(destinationDirName || "")), {
recursive: true,
force: true,
});
return true;
}

async function copyFile(sourceFileName, destinationFileName) {
async function copyFile(
sourceFileName,
destinationFileName,
{ ifExists = false } = {},
) {
const sourceFile = path.resolve(String(sourceFileName || ""));
if (ifExists && !(await _pathExists(sourceFile))) return false;
const destinationFile = path.resolve(String(destinationFileName || ""));
await ensureDir(path.dirname(destinationFile));
await fsp.copyFile(sourceFile, destinationFile);
return true;
}

async function deleteFile(fileName) {
Expand Down Expand Up @@ -106,7 +127,7 @@ async function xslt({
sefCacheDir,
} = {}) {
const resolvedSourceFile = path.resolve(String(sourceFile || ""));
if (!(await pathExists(resolvedSourceFile))) {
if (!(await _pathExists(resolvedSourceFile))) {
throw new Error(
`Bronbestand voor transformatie niet gevonden: ${resolvedSourceFile}`,
);
Expand All @@ -117,7 +138,7 @@ async function xslt({
resolvedSefFile = path.resolve(resolvedSefFile);
} else {
const resolvedStylesheetFile = path.resolve(String(stylesheetFile || ""));
if (!(await pathExists(resolvedStylesheetFile))) {
if (!(await _pathExists(resolvedStylesheetFile))) {
throw new Error(`Stylesheet niet gevonden: ${resolvedStylesheetFile}`);
}
resolvedSefFile = await compileStylesheetToSef(
Expand Down Expand Up @@ -180,6 +201,8 @@ async function getResource(options) {
module.exports = {
fileset,
normalizeFileset,
pathExists,
emptyDir,
makeDir,
deleteDir,
copyDir,
Expand Down
32 changes: 31 additions & 1 deletion src/support/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fsp = require("node:fs/promises");
const path = require("node:path");

const { ZipLib } = require("./dependencies");
const { runProcess } = require("./process");

async function pathExists(absolutePath) {
try {
Expand Down Expand Up @@ -91,7 +92,36 @@ async function findFirstFileByExtension(rootDir, extension) {

async function unzipToDirectory(sourceZipFile, destinationDir) {
await ensureDir(destinationDir);
await ZipLib.extract(sourceZipFile, await fsp.realpath(destinationDir));
const realDestination = await fsp.realpath(destinationDir);

// Prefer the shell `unzip` binary (yauzl-based extractors such as zip-lib
// can hang silently on certain Docker overlay + Node combinations, leaving
// a partial extraction without throwing). Fall back to ZipLib when `unzip`
// is not available on the host.
try {
const result = await runProcess("unzip", [
"-q",
"-o",
sourceZipFile,
"-d",
realDestination,
]);
if (result.code === 0) {
return;
}
// unzip warnings (code 1) are non-fatal; treat anything higher as failure
// and fall through to ZipLib.
if (result.code === 1) {
return;
}
} catch (error) {
if (error && error.code !== "ENOENT") {
throw error;
}
// `unzip` binary missing — fall through to the JS implementation.
}

await ZipLib.extract(sourceZipFile, realDestination);
}

async function zipDirectory(sourceDir, destinationZipFile) {
Expand Down
2 changes: 2 additions & 0 deletions tests/package.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ test("package exports the expected public api", () => {
assert.equal(typeof packageApi.runBuild, "function");
assert.equal(typeof packageApi.runCli, "function");
assert.equal(typeof packageApi.build.fileset, "function");
assert.equal(typeof packageApi.build.pathExists, "function");
assert.equal(typeof packageApi.build.emptyDir, "function");
assert.equal(typeof packageApi.build.checksum, "function");
assert.equal(typeof packageApi.build.basename, "function");
assert.equal(typeof packageApi.build.xslt, "function");
Expand Down