-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfilesystem.js
More file actions
164 lines (140 loc) · 4.07 KB
/
Copy pathfilesystem.js
File metadata and controls
164 lines (140 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
const fs = require("node:fs");
const fsp = require("node:fs/promises");
const path = require("node:path");
const { ZipLib } = require("./dependencies");
const { runProcess } = require("./process");
async function pathExists(absolutePath) {
try {
await fsp.access(absolutePath, fs.constants.F_OK);
return true;
} catch {
return false;
}
}
async function ensureDir(absolutePath) {
await fsp.mkdir(absolutePath, { recursive: true });
}
async function emptyDir(absolutePath) {
await fsp.rm(absolutePath, { recursive: true, force: true });
await ensureDir(absolutePath);
}
async function copyDirContents(sourceDir, destinationDir) {
await ensureDir(destinationDir);
if (!(await pathExists(sourceDir))) {
return;
}
const entries = await fsp.readdir(sourceDir);
for (const entry of entries) {
const sourcePath = path.join(sourceDir, entry);
const destinationPath = path.join(destinationDir, entry);
await fsp.cp(sourcePath, destinationPath, { recursive: true, force: true });
}
}
async function copyFileIfExists(sourceFile, destinationFile) {
if (!(await pathExists(sourceFile))) {
return false;
}
await ensureDir(path.dirname(destinationFile));
await fsp.copyFile(sourceFile, destinationFile);
return true;
}
async function copyDirectoryIfExists(sourceDir, destinationDir) {
if (!(await pathExists(sourceDir))) {
return false;
}
await fsp.cp(sourceDir, destinationDir, { recursive: true, force: true });
return true;
}
async function collectFilesRecursively(rootDir) {
const files = [];
if (!(await pathExists(rootDir))) {
return files;
}
const stack = [rootDir];
while (stack.length) {
const currentDir = stack.pop();
const entries = await fsp.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const absolutePath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
stack.push(absolutePath);
} else if (entry.isFile()) {
files.push(absolutePath);
}
}
}
files.sort();
return files;
}
async function findFirstFileByExtension(rootDir, extension) {
const files = await collectFilesRecursively(rootDir);
const normalizedExt = String(extension || "").toLowerCase();
return (
files.find((filePath) => filePath.toLowerCase().endsWith(normalizedExt)) ||
""
);
}
async function unzipToDirectory(sourceZipFile, destinationDir) {
await ensureDir(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) {
await ensureDir(path.dirname(destinationZipFile));
const zip = new ZipLib.Zip();
if (await pathExists(sourceDir)) {
zip.addFolder(sourceDir);
}
await zip.archive(destinationZipFile);
}
async function statMtimeMs(filePath) {
const stats = await fsp.stat(filePath);
return Number(stats.mtimeMs || 0);
}
function sanitizeCacheName(filePath) {
return String(filePath || "")
.replace(/[^a-zA-Z0-9._-]+/g, "_")
.replace(/_+/g, "_");
}
module.exports = {
fs,
fsp,
pathExists,
ensureDir,
emptyDir,
copyDirContents,
copyFileIfExists,
copyDirectoryIfExists,
collectFilesRecursively,
findFirstFileByExtension,
unzipToDirectory,
zipDirectory,
statMtimeMs,
sanitizeCacheName,
};