@lakutata/builder 3.1.5 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/README.zh-CN.md +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/lib/Installer.js +3 -3
- package/dist/esm/lib/NativeRuntime.js +25 -3
- package/dist/esm/lib/Processes.js +3 -2
- package/dist/esm/lib/Tools.js +98 -19
- package/dist/esm/lib/linux/Linux.js +3 -2
- package/dist/esm/lib/mobile/Addons.js +675 -0
- package/dist/esm/lib/mobile/Node.js +190 -0
- package/dist/esm/lib/mobile/Stage.js +180 -0
- package/dist/esm/lib/nwjs/Addons.js +7 -4
- package/dist/esm/lib/nwjs/Package.js +1 -1
- package/dist/esm/lib/nwjs/Runtime.js +5 -31
- package/dist/esm/lib/win/Windows.js +5 -4
- package/dist/types/index.d.ts +3 -0
- package/dist/types/lib/Installer.d.ts +4 -0
- package/dist/types/lib/Processes.d.ts +2 -1
- package/dist/types/lib/Tools.d.ts +47 -1
- package/dist/types/lib/linux/Linux.d.ts +2 -1
- package/dist/types/lib/mobile/Addons.d.ts +154 -0
- package/dist/types/lib/mobile/Node.d.ts +89 -0
- package/dist/types/lib/mobile/Stage.d.ts +111 -0
- package/dist/types/lib/nwjs/Addons.d.ts +18 -0
- package/dist/types/lib/nwjs/Package.d.ts +4 -0
- package/dist/types/lib/nwjs/Runtime.d.ts +4 -0
- package/dist/types/lib/win/Windows.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,19 @@ Node.js of NW.js), its resources and the manifest of NW.js. `buildNwjsApp()` put
|
|
|
39
39
|
named after the application, and `buildInstaller()` makes its installers with `"runtime": "nwjs"` (the installers of a
|
|
40
40
|
desktop application: dmg on macOS, nsis on Windows, deb on Linux by default).
|
|
41
41
|
|
|
42
|
+
The mobile applications of React Native (Android, iOS, HarmonyOS) run in a libnode built from the sources of Node.js by
|
|
43
|
+
lakutata/libnode. `downloadMobileNode()` downloads the libnode of a system from its releases once
|
|
44
|
+
(`LAKUTATA_LIBNODE_MIRROR`: a mirror of them), checks it with their checksums and keeps it in the cache
|
|
45
|
+
(`LAKUTATA_MOBILE_NODE_DIR`: a libnode of your own instead). `stageMobileApp()` makes the Node.js project of the
|
|
46
|
+
application: the bundle for the Node.js of the libnode (Node.js 24.20.0, `MOBILE_NODE_VERSION`, without a libnode), the
|
|
47
|
+
resources, the manifest and an entry that puts away `fetch()` without WebAssembly (HarmonyOS). It builds the native
|
|
48
|
+
addons for the system from their sources, against the headers and the libraries of the libnode: a native tree per
|
|
49
|
+
architecture of Android (node-gyp and the NDK), for iOS a framework per addon for each SDK (Xcode) in the directory the
|
|
50
|
+
`frameworks` option gives, and for HarmonyOS a library per addon (the native SDK of DevEco Studio) in the libraries of
|
|
51
|
+
the HAP the `libraries` option gives. The HAP of HarmonyOS leaves out of its resources the files whose names start with
|
|
52
|
+
a dot: on HarmonyOS the native tree is packed into the project (`native.gz`, `native.json`) and extracted at its first
|
|
53
|
+
run into the directory `LAKUTATA_NATIVE_DIR` gives.
|
|
54
|
+
|
|
42
55
|
The Linux installers are tested in containers (Docker): `npm run test:installer` builds them on Debian and Alpine, then
|
|
43
56
|
installs, upgrades and removes them on Debian and Fedora (systemd) and on Alpine (OpenRC). The macOS installers are
|
|
44
57
|
installed in a macOS virtual machine (`npm run test:installer:mac`, with tart), and the Windows installers on Windows,
|
package/README.zh-CN.md
CHANGED
|
@@ -23,6 +23,8 @@ await buildNwjsApp({project: process.cwd()})
|
|
|
23
23
|
|
|
24
24
|
NW.js 应用运行在它 `lakutata.nwjs` 中指定的 NW.js 版本里。`nwjsReleases()` 列出 NW.js 的发布版本,`downloadNwjs()` 把一个运行时下载到缓存目录(按其发布版本的校验和核对),`stageNwjsApp()` 为 NW.js 准备应用:为这个 NW.js 的 Node.js 打包的代码、处理成能在其中加载的原生插件(为它的架构重新构建,使用 V8 API 的插件用它的头文件重新构建;在 Windows 上让插件加载 NW.js 的 Node.js)、资源和 NW.js 的清单。`buildNwjsApp()` 把应用放进以应用命名的 NW.js normal 版本中,`buildInstaller()` 在 `"runtime": "nwjs"` 时制作它的安装包(桌面应用的安装包:默认 macOS 上是 dmg,Windows 上是 nsis,Linux 上是 deb)。
|
|
25
25
|
|
|
26
|
+
React Native 手机应用(Android、iOS、鸿蒙)运行在一个 libnode 中,它由 lakutata/libnode 从 Node.js 源码编译而成。`downloadMobileNode()` 从它的 Release 下载一个系统的 libnode,只下载一次(`LAKUTATA_LIBNODE_MIRROR` 指定镜像),用 Release 的校验和验证后保存在缓存中(设置 `LAKUTATA_MOBILE_NODE_DIR` 时改用自己编译的 libnode)。`stageMobileApp()` 生成应用的 Node.js 项目:面向 libnode 中的 Node.js 的单文件包(没有 libnode 时面向 Node.js 24.20.0,`MOBILE_NODE_VERSION`)、资源、清单,以及一个入口:没有 WebAssembly 时(鸿蒙)收起 `fetch()`。它还会用 libnode 的头文件和库,从源码为目标系统编译原生插件:Android 每种架构一棵原生树(node-gyp 和 NDK);iOS 每个插件为每个 SDK 编成一个 framework(Xcode),放在 `frameworks` 指定的目录中;鸿蒙每个插件编成一个库(DevEco Studio 的原生 SDK),放在 `libraries` 指定的 HAP 库目录中。鸿蒙的 HAP 会把以点开头的文件从资源中去掉,所以鸿蒙上的原生树打包进项目(`native.gz`、`native.json`),第一次运行时解压到 `LAKUTATA_NATIVE_DIR` 指定的目录。
|
|
27
|
+
|
|
26
28
|
Linux 安装包在容器(Docker)中测试:`npm run test:installer` 先在 Debian 和 Alpine 上制作安装包,再在 Debian、Fedora(systemd)和 Alpine(OpenRC)上安装、升级和卸载它们。macOS 安装包在 macOS 虚拟机中测试安装(`npm run test:installer:mac`,使用 tart),Windows 安装包则在 Windows 上以管理员身份测试安装(`LAKUTATA_WINDOWS_INSTALL=1 npm run test:installer:windows`)。
|
|
27
29
|
|
|
28
30
|
## 另见
|
package/dist/esm/index.js
CHANGED
|
@@ -14,3 +14,5 @@ export { MAC_TARGETS } from './lib/mac/Mac.js';
|
|
|
14
14
|
export { downloadNwjs, nwjsPlatform, nwjsRelease, nwjsReleases } from './lib/nwjs/Runtime.js';
|
|
15
15
|
export { stageNwjsApp } from './lib/nwjs/Stage.js';
|
|
16
16
|
export { buildNwjsApp } from './lib/nwjs/Package.js';
|
|
17
|
+
export { stageMobileApp } from './lib/mobile/Stage.js';
|
|
18
|
+
export { downloadMobileNode, MOBILE_NODE_RELEASE, MOBILE_NODE_VERSION } from './lib/mobile/Node.js';
|
|
@@ -114,7 +114,7 @@ export async function buildInstaller(options) {
|
|
|
114
114
|
await writeFile(icon, icoFile(application.image));
|
|
115
115
|
}
|
|
116
116
|
//The executable and what it loads; or the application of NW.js (its runtime, its executable named after it)
|
|
117
|
-
const nwjs = desktop ? await buildNwjsApp({ project: project, entry: options.entry, minify: options.minify, external: options.external, arch: options.arch, outdir: path.join(work, 'stage') }) : undefined;
|
|
117
|
+
const nwjs = desktop ? await buildNwjsApp({ project: project, entry: options.entry, minify: options.minify, external: options.external, arch: options.arch, outdir: path.join(work, 'stage'), progress: options.progress }) : undefined;
|
|
118
118
|
const executable = nwjs ? undefined : await buildExecutable({
|
|
119
119
|
project: project,
|
|
120
120
|
entry: options.entry,
|
|
@@ -166,8 +166,8 @@ export async function buildInstaller(options) {
|
|
|
166
166
|
const outdir = path.resolve(project, options.outdir ?? path.join('dist', 'installer'));
|
|
167
167
|
await mkdir(outdir, { recursive: true });
|
|
168
168
|
const files = mac ? await buildMacPackages(plan, targets, path.join(work, 'mac'), outdir)
|
|
169
|
-
: windows ? await buildWindowsPackages(plan, targets, path.join(work, 'win'), outdir)
|
|
170
|
-
: await buildLinuxPackages(plan, targets, path.join(work, 'linux'), outdir);
|
|
169
|
+
: windows ? await buildWindowsPackages(plan, targets, path.join(work, 'win'), outdir, options.progress)
|
|
170
|
+
: await buildLinuxPackages(plan, targets, path.join(work, 'linux'), outdir, options.progress);
|
|
171
171
|
return { name: name, version: version, files: files, ...executable ? { executable: executable } : {}, ...nwjs ? { nwjs: nwjs } : {}, warnings: warnings };
|
|
172
172
|
}
|
|
173
173
|
finally {
|
|
@@ -82,13 +82,33 @@ export function lakutataNativeRequire(nodeRequire, config) {
|
|
|
82
82
|
fs.symlinkSync(path.relative(path.dirname(at), local(temporary, link.target)), at, 'dir');
|
|
83
83
|
}
|
|
84
84
|
fs.writeFileSync(path.join(temporary, '.complete'), '');
|
|
85
|
+
const complete = () => fs.existsSync(path.join(directory, '.complete'));
|
|
85
86
|
try {
|
|
86
87
|
fs.renameSync(temporary, directory);
|
|
87
88
|
}
|
|
88
89
|
catch (error) {
|
|
89
90
|
//Another process extracted it
|
|
90
|
-
if (
|
|
91
|
+
if (complete())
|
|
92
|
+
return directory;
|
|
93
|
+
if (!fs.existsSync(directory))
|
|
91
94
|
throw error;
|
|
95
|
+
//A tree left incomplete (a process stopped, a cleaning of the temporary files that keeps the directories):
|
|
96
|
+
//moved aside, removed, replaced
|
|
97
|
+
const stale = `${directory}.stale.${process.pid}.${Date.now()}`;
|
|
98
|
+
try {
|
|
99
|
+
fs.renameSync(directory, stale);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
//Moved by another process
|
|
103
|
+
}
|
|
104
|
+
fs.rmSync(stale, { recursive: true, force: true });
|
|
105
|
+
try {
|
|
106
|
+
fs.renameSync(temporary, directory);
|
|
107
|
+
}
|
|
108
|
+
catch (retry) {
|
|
109
|
+
if (!complete())
|
|
110
|
+
throw retry;
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
113
|
}
|
|
94
114
|
finally {
|
|
@@ -114,10 +134,12 @@ export function lakutataNativeRequire(nodeRequire, config) {
|
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
else {
|
|
117
|
-
//Next to the bundle,
|
|
137
|
+
//Next to the bundle, the tree of the architecture first (<directory>-arm64: a mobile application of Android holds
|
|
138
|
+
//one tree per architecture), or in the resources of a packaged Electron application (outside of its archive)
|
|
118
139
|
const resources = process.resourcesPath;
|
|
119
140
|
const local = path.join(__dirname, config.directory);
|
|
120
|
-
|
|
141
|
+
const own = `${local}-${process.arch}`;
|
|
142
|
+
root = fs.existsSync(own) ? own : fs.existsSync(local) || !resources ? local : path.join(resources, config.directory);
|
|
121
143
|
}
|
|
122
144
|
return root;
|
|
123
145
|
};
|
|
@@ -27,11 +27,12 @@ export function spawnCommand(command, args, options) {
|
|
|
27
27
|
* @param command
|
|
28
28
|
* @param args
|
|
29
29
|
* @param cwd
|
|
30
|
+
* @param env its environment, the one of the process by default
|
|
30
31
|
* @return the exit code and the output
|
|
31
32
|
*/
|
|
32
|
-
export function runCommand(command, args, cwd) {
|
|
33
|
+
export function runCommand(command, args, cwd, env) {
|
|
33
34
|
return new Promise((resolve, reject) => {
|
|
34
|
-
const child = spawnCommand(command, args, { cwd: cwd });
|
|
35
|
+
const child = spawnCommand(command, args, { cwd: cwd, env: env });
|
|
35
36
|
let output = '';
|
|
36
37
|
//Piped (the default stdio)
|
|
37
38
|
child.stdout.on('data', (chunk) => output += chunk.toString());
|
package/dist/esm/lib/Tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
-
import { chmod, mkdir, mkdtemp, rename, rm
|
|
3
|
+
import { chmod, mkdir, mkdtemp, open, rename, rm } from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { runCommand } from './Processes.js';
|
|
@@ -54,13 +54,100 @@ const TOOLS = {
|
|
|
54
54
|
export function cacheDirectory() {
|
|
55
55
|
return process.env.LAKUTATA_CACHE_DIR ?? path.join(process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), '.cache'), 'lakutata');
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Download a file, its sha256 computed while it is written. A connection receiving too little (one of the releases of
|
|
59
|
+
* GitHub can stay at some kilobytes a second while another one downloads the file at once) or broken is left for
|
|
60
|
+
* another one, which downloads the file again from its start
|
|
61
|
+
* @param url
|
|
62
|
+
* @param file
|
|
63
|
+
* @param options
|
|
64
|
+
*/
|
|
65
|
+
export async function downloadFile(url, file, options = {}) {
|
|
66
|
+
const interval = options.interval ?? 30000;
|
|
67
|
+
const minimum = options.minimum ?? 100 * 1024;
|
|
68
|
+
const attempts = options.attempts ?? 3;
|
|
69
|
+
const slow = `under ${minimum % 1024 === 0 ? `${minimum / 1024} KB` : `${minimum} bytes`} received in ${interval / 1000} s`;
|
|
70
|
+
for (let attempt = 1;; attempt++) {
|
|
71
|
+
const controller = new AbortController();
|
|
72
|
+
let received = 0;
|
|
73
|
+
let checked = 0;
|
|
74
|
+
//From the request: a connection waiting for its response receives nothing too
|
|
75
|
+
const timer = setInterval(() => {
|
|
76
|
+
if (received - checked < minimum)
|
|
77
|
+
controller.abort();
|
|
78
|
+
checked = received;
|
|
79
|
+
}, interval);
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(url, { headers: options.headers, signal: controller.signal });
|
|
82
|
+
if (!response.ok || !response.body)
|
|
83
|
+
throw new Error(`${url} cannot be downloaded (${response.status})`);
|
|
84
|
+
const hash = createHash('sha256');
|
|
85
|
+
const handle = await open(file, 'w');
|
|
86
|
+
try {
|
|
87
|
+
for await (const chunk of response.body) {
|
|
88
|
+
received += chunk.length;
|
|
89
|
+
hash.update(chunk);
|
|
90
|
+
await handle.write(chunk);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await handle.close();
|
|
95
|
+
}
|
|
96
|
+
return hash.digest('hex');
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
//The connections broken fail with a TypeError (fetch failed, terminated); a file refused by the server, or
|
|
100
|
+
//which cannot be written, is not downloaded again
|
|
101
|
+
const cause = error.cause?.code;
|
|
102
|
+
const reason = controller.signal.aborted ? slow : error instanceof TypeError ? `${error.message}${cause ? ` (${cause})` : ''}` : undefined;
|
|
103
|
+
if (reason === undefined)
|
|
104
|
+
throw error;
|
|
105
|
+
if (attempt === attempts)
|
|
106
|
+
throw new Error(`${url} cannot be downloaded, ${attempts} connections tried: ${reason}`, { cause: error });
|
|
107
|
+
options.progress?.(`${path.basename(file)} again (${attempt + 1}/${attempts}): ${reason}`);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
clearInterval(timer);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Move a directory written aside into its place in the cache. The one already there is kept when it is complete (put
|
|
116
|
+
* there by another build running at the same time), and replaced when it is not: left by a build that was stopped, or
|
|
117
|
+
* emptied by a cleaning of the temporary files that removes the files and keeps the directories (macOS cleans its
|
|
118
|
+
* temporary directory so)
|
|
119
|
+
* @param temporary The directory written, removed by the caller.
|
|
120
|
+
* @param target Its place in the cache.
|
|
121
|
+
* @param complete Whether the directory in its place is complete.
|
|
122
|
+
*/
|
|
123
|
+
export async function moveIntoCache(temporary, target, complete) {
|
|
124
|
+
try {
|
|
125
|
+
await rename(temporary, target);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (complete())
|
|
129
|
+
return;
|
|
130
|
+
if (!existsSync(target))
|
|
131
|
+
throw error;
|
|
132
|
+
//Moved aside before being removed: its place is free at once for this build and the others
|
|
133
|
+
const stale = `${target}.stale-${process.pid}-${Date.now()}`;
|
|
134
|
+
await rename(target, stale).catch(() => undefined);
|
|
135
|
+
await rm(stale, { recursive: true, force: true });
|
|
136
|
+
await rename(temporary, target).catch((retry) => {
|
|
137
|
+
//Put there by another build meanwhile
|
|
138
|
+
if (!complete())
|
|
139
|
+
throw retry;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
57
143
|
/**
|
|
58
144
|
* A tool of the builder: the one given by its environment variable (LAKUTATA_NFPM, LAKUTATA_WINSW, LAKUTATA_NSIS:
|
|
59
145
|
* makensis.exe, LAKUTATA_WIX: candle.exe, next to light.exe), else the version of the builder, downloaded once into the
|
|
60
146
|
* cache and checked with its sha256
|
|
61
147
|
* @param name
|
|
148
|
+
* @param progress Called with what is being downloaded
|
|
62
149
|
*/
|
|
63
|
-
export async function ensureTool(name) {
|
|
150
|
+
export async function ensureTool(name, progress) {
|
|
64
151
|
const given = process.env[`LAKUTATA_${name.toUpperCase()}`];
|
|
65
152
|
if (given)
|
|
66
153
|
return given;
|
|
@@ -73,21 +160,17 @@ export async function ensureTool(name) {
|
|
|
73
160
|
const executable = path.join(directory, ...tool.executable.split('/'));
|
|
74
161
|
if (existsSync(executable))
|
|
75
162
|
return executable;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
throw new Error(`${name} cannot be downloaded (${error.message}): give its executable (LAKUTATA_${name.toUpperCase()})`);
|
|
79
|
-
});
|
|
80
|
-
if (!response.ok)
|
|
81
|
-
throw new Error(`${name} cannot be downloaded (${tool.url(release.archive)}: ${response.status}): give its executable (LAKUTATA_${name.toUpperCase()})`);
|
|
82
|
-
const archive = Buffer.from(await response.arrayBuffer());
|
|
83
|
-
const sha256 = createHash('sha256').update(archive).digest('hex');
|
|
84
|
-
if (sha256 !== release.sha256)
|
|
85
|
-
throw new Error(`The archive of ${name} ${tool.version} downloaded is not the one of its release (sha256 ${sha256})`);
|
|
86
|
-
//Extracted aside, then renamed: the builds running together do not see it partly written
|
|
163
|
+
progress?.(`${release.archive} (${name} ${tool.version})`);
|
|
164
|
+
//Downloaded and extracted aside, then renamed: the builds running together do not see it partly written
|
|
87
165
|
await mkdir(path.dirname(directory), { recursive: true });
|
|
88
166
|
const temporary = await mkdtemp(`${directory}.`);
|
|
89
167
|
try {
|
|
90
|
-
|
|
168
|
+
//SourceForge answers the browsers with its page, the tools with the file
|
|
169
|
+
const sha256 = await downloadFile(tool.url(release.archive), path.join(temporary, release.archive), { headers: { 'user-agent': 'Wget' }, progress: progress }).catch((error) => {
|
|
170
|
+
throw new Error(`${error.message}: give the executable of ${name} (LAKUTATA_${name.toUpperCase()})`);
|
|
171
|
+
});
|
|
172
|
+
if (sha256 !== release.sha256)
|
|
173
|
+
throw new Error(`The archive of ${name} ${tool.version} downloaded is not the one of its release (sha256 ${sha256})`);
|
|
91
174
|
//An archive (tar.gz, zip: the tar of Windows reads them), or the executable itself
|
|
92
175
|
if (/\.(tar\.gz|zip)$/.test(release.archive)) {
|
|
93
176
|
const extracted = await runCommand('tar', ['-xf', release.archive], temporary);
|
|
@@ -96,11 +179,7 @@ export async function ensureTool(name) {
|
|
|
96
179
|
await rm(path.join(temporary, release.archive));
|
|
97
180
|
}
|
|
98
181
|
await chmod(path.join(temporary, ...tool.executable.split('/')), 0o755);
|
|
99
|
-
await
|
|
100
|
-
//Extracted by another build
|
|
101
|
-
if (!existsSync(executable))
|
|
102
|
-
throw error;
|
|
103
|
-
});
|
|
182
|
+
await moveIntoCache(temporary, directory, () => existsSync(executable));
|
|
104
183
|
}
|
|
105
184
|
finally {
|
|
106
185
|
await rm(temporary, { recursive: true, force: true });
|
|
@@ -328,8 +328,9 @@ function packageScripts(plan, target) {
|
|
|
328
328
|
* @param targets
|
|
329
329
|
* @param work
|
|
330
330
|
* @param outdir
|
|
331
|
+
* @param progress Called with what is being downloaded (nFPM)
|
|
331
332
|
*/
|
|
332
|
-
export async function buildLinuxPackages(plan, targets, work, outdir) {
|
|
333
|
+
export async function buildLinuxPackages(plan, targets, work, outdir, progress) {
|
|
333
334
|
await mkdir(work, { recursive: true });
|
|
334
335
|
const files = [];
|
|
335
336
|
const service = plan.service;
|
|
@@ -398,7 +399,7 @@ export async function buildLinuxPackages(plan, targets, work, outdir) {
|
|
|
398
399
|
};
|
|
399
400
|
//JSON is YAML
|
|
400
401
|
await writeFile(path.join(directory, 'nfpm.yaml'), JSON.stringify(config, null, 2));
|
|
401
|
-
const nfpm = await ensureTool('nfpm');
|
|
402
|
+
const nfpm = await ensureTool('nfpm', progress);
|
|
402
403
|
const file = path.join(outdir, packageFile(plan, target));
|
|
403
404
|
await rm(file, { force: true });
|
|
404
405
|
const packaged = await runCommand(nfpm, ['package', '--config', path.join(directory, 'nfpm.yaml'), '--packager', target, '--target', file], directory);
|