pake-cli 3.15.7 โ 3.16.1
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 +1 -1
- package/dist/cli.js +520 -158
- package/llms.txt +15 -2
- package/package.json +2 -2
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +2 -2
- package/src-tauri/src/app/auth.rs +478 -0
- package/src-tauri/src/app/config.rs +5 -0
- package/src-tauri/src/app/mod.rs +1 -1
- package/src-tauri/src/app/window.rs +69 -14
- package/src-tauri/src/inject/event.js +71 -90
- package/src-tauri/src/inject/find.js +39 -24
- package/src-tauri/src/inject/frame_links.js +66 -0
- package/src-tauri/src/inject/fullscreen.js +9 -4
- package/src-tauri/src/inject/link_policy.js +83 -0
- package/src-tauri/src/inject/style.js +28 -18
- package/src-tauri/src/inject/styles.js +100 -0
- package/src-tauri/tauri.conf.json +1 -1
- package/src-tauri/src/app/cert.rs +0 -187
package/dist/cli.js
CHANGED
|
@@ -4,13 +4,14 @@ import chalk from 'chalk';
|
|
|
4
4
|
import updateNotifier from 'update-notifier';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import fsExtra from 'fs-extra';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
7
|
import prompts from 'prompts';
|
|
9
8
|
import os from 'os';
|
|
10
9
|
import { execa, execaSync } from 'execa';
|
|
11
10
|
import crypto from 'crypto';
|
|
12
11
|
import ora from 'ora';
|
|
13
12
|
import fs from 'fs';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
14
15
|
import fs$1 from 'fs/promises';
|
|
15
16
|
import { dir } from 'tmp-promise';
|
|
16
17
|
import { fileTypeFromBuffer } from 'file-type';
|
|
@@ -18,8 +19,9 @@ import * as psl from 'psl';
|
|
|
18
19
|
import { InvalidArgumentError, program as program$1, Option } from 'commander';
|
|
19
20
|
|
|
20
21
|
var name = "pake-cli";
|
|
21
|
-
var version = "3.
|
|
22
|
+
var version = "3.16.1";
|
|
22
23
|
var description = "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ";
|
|
24
|
+
var homepage = "https://faberon.io/projects/pake";
|
|
23
25
|
var engines = {
|
|
24
26
|
node: ">=20.9.0"
|
|
25
27
|
};
|
|
@@ -97,7 +99,6 @@ var devDependencies = {
|
|
|
97
99
|
"@types/prompts": "^2.4.9",
|
|
98
100
|
"@types/tmp": "^0.2.6",
|
|
99
101
|
"@types/update-notifier": "^6.0.8",
|
|
100
|
-
"app-root-path": "^3.1.0",
|
|
101
102
|
"cross-env": "^10.1.0",
|
|
102
103
|
prettier: "^3.8.1",
|
|
103
104
|
rollup: "^4.59.0",
|
|
@@ -121,6 +122,7 @@ var packageJson = {
|
|
|
121
122
|
name: name,
|
|
122
123
|
version: version,
|
|
123
124
|
description: description,
|
|
125
|
+
homepage: homepage,
|
|
124
126
|
engines: engines,
|
|
125
127
|
packageManager: packageManager,
|
|
126
128
|
bin: bin,
|
|
@@ -137,40 +139,6 @@ var packageJson = {
|
|
|
137
139
|
pnpm: pnpm
|
|
138
140
|
};
|
|
139
141
|
|
|
140
|
-
// Convert the current module URL to a file path
|
|
141
|
-
const currentModulePath = fileURLToPath(import.meta.url);
|
|
142
|
-
// Resolve the parent directory of the current module
|
|
143
|
-
const npmDirectory = path.join(path.dirname(currentModulePath), '..');
|
|
144
|
-
const tauriConfigDirectory = path.join(npmDirectory, 'src-tauri', '.pake');
|
|
145
|
-
|
|
146
|
-
// Load configs from npm package directory, not from project source
|
|
147
|
-
const tauriSrcDir = path.join(npmDirectory, 'src-tauri');
|
|
148
|
-
const pakeConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'pake.json'));
|
|
149
|
-
const CommonConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.conf.json'));
|
|
150
|
-
const WinConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.windows.conf.json'));
|
|
151
|
-
const MacConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.macos.conf.json'));
|
|
152
|
-
const LinuxConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.linux.conf.json'));
|
|
153
|
-
const platformConfigs = {
|
|
154
|
-
win32: WinConf,
|
|
155
|
-
darwin: MacConf,
|
|
156
|
-
linux: LinuxConf,
|
|
157
|
-
};
|
|
158
|
-
const { platform: platform$2 } = process;
|
|
159
|
-
// @ts-ignore
|
|
160
|
-
const platformConfig = platformConfigs[platform$2];
|
|
161
|
-
let tauriConfig = {
|
|
162
|
-
...CommonConf,
|
|
163
|
-
bundle: platformConfig.bundle,
|
|
164
|
-
app: {
|
|
165
|
-
...CommonConf.app,
|
|
166
|
-
trayIcon: {
|
|
167
|
-
...(platformConfig?.app?.trayIcon ?? {}),
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
build: CommonConf.build,
|
|
171
|
-
pake: pakeConf,
|
|
172
|
-
};
|
|
173
|
-
|
|
174
142
|
// Stable exit-code contract: 0 success, 2 invalid input, 3 build/network
|
|
175
143
|
// failure, 4 missing environment, 1 unexpected. Documented in cli-usage docs.
|
|
176
144
|
const ERROR_EXIT_CODES = {
|
|
@@ -279,10 +247,10 @@ function isCnMirrorEnabled(value = process.env[CN_MIRROR_ENV]) {
|
|
|
279
247
|
return TRUE_VALUES.has((value ?? '').trim().toLowerCase());
|
|
280
248
|
}
|
|
281
249
|
|
|
282
|
-
const { platform: platform$
|
|
283
|
-
const IS_MAC = platform$
|
|
284
|
-
const IS_WIN = platform$
|
|
285
|
-
const IS_LINUX = platform$
|
|
250
|
+
const { platform: platform$2 } = process;
|
|
251
|
+
const IS_MAC = platform$2 === 'darwin';
|
|
252
|
+
const IS_WIN = platform$2 === 'win32';
|
|
253
|
+
const IS_LINUX = platform$2 === 'linux';
|
|
286
254
|
// Distro IDs / ID_LIKE families that ship an RPM-based package manager.
|
|
287
255
|
const RPM_FAMILY_IDS = new Set([
|
|
288
256
|
'rhel',
|
|
@@ -371,10 +339,295 @@ function getDefaultLinuxTargets() {
|
|
|
371
339
|
return detectLinuxPackageFamily() === 'rpm' ? 'rpm,appimage' : 'deb,appimage';
|
|
372
340
|
}
|
|
373
341
|
|
|
342
|
+
// Convert the current module URL to a file path
|
|
343
|
+
const currentModulePath = fileURLToPath(import.meta.url);
|
|
344
|
+
// Resolve the parent directory of the current module
|
|
345
|
+
const packageDirectory = path.join(path.dirname(currentModulePath), '..');
|
|
346
|
+
let npmDirectory = packageDirectory;
|
|
347
|
+
let tauriConfigDirectory = path.join(npmDirectory, 'src-tauri', '.pake');
|
|
348
|
+
// A CLI invocation owns one build workspace. Keep template resolution separate
|
|
349
|
+
// from generated paths so packaging never rewrites the installed package.
|
|
350
|
+
function setBuildDirectory(directory) {
|
|
351
|
+
npmDirectory = directory;
|
|
352
|
+
tauriConfigDirectory = path.join(directory, 'src-tauri', '.pake');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const logger = {
|
|
356
|
+
info(...msg) {
|
|
357
|
+
log.info(...msg.map((m) => chalk.white(m)));
|
|
358
|
+
},
|
|
359
|
+
debug(...msg) {
|
|
360
|
+
log.debug(...msg);
|
|
361
|
+
},
|
|
362
|
+
error(...msg) {
|
|
363
|
+
log.error(...msg.map((m) => chalk.red(m)));
|
|
364
|
+
},
|
|
365
|
+
warn(...msg) {
|
|
366
|
+
log.warn(...msg.map((m) => chalk.yellow(m)));
|
|
367
|
+
},
|
|
368
|
+
success(...msg) {
|
|
369
|
+
log.info(...msg.map((m) => chalk.green(m)));
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Error class used for user-facing CLI errors.
|
|
375
|
+
*
|
|
376
|
+
* The top-level catch in `bin/cli.ts` prints `message` directly without a
|
|
377
|
+
* stack trace and exits with the code mapped from `code` (see
|
|
378
|
+
* ERROR_EXIT_CODES in utils/output.ts). Use this for predictable failures
|
|
379
|
+
* (invalid names, missing files, etc.) so users see a clean message instead
|
|
380
|
+
* of a Node.js stack dump. `code` and `hint` also feed the `--json` result.
|
|
381
|
+
*/
|
|
382
|
+
class PakeError extends Error {
|
|
383
|
+
constructor(message, options) {
|
|
384
|
+
super(message);
|
|
385
|
+
this.isUserError = true;
|
|
386
|
+
this.name = 'PakeError';
|
|
387
|
+
this.code = options?.code;
|
|
388
|
+
this.hint = options?.hint;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function isPakeError(error) {
|
|
392
|
+
return (error instanceof PakeError ||
|
|
393
|
+
(typeof error === 'object' &&
|
|
394
|
+
error !== null &&
|
|
395
|
+
error.isUserError === true));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Check the local launcher and native binding, not just an installed manifest. */
|
|
399
|
+
async function hasReadyTauriCli(directory) {
|
|
400
|
+
const modules = path.join(directory, 'node_modules');
|
|
401
|
+
const launcher = path.join(modules, '.bin', process.platform === 'win32' ? 'tauri.cmd' : 'tauri');
|
|
402
|
+
const entry = path.join(modules, '@tauri-apps', 'cli', 'tauri.js');
|
|
403
|
+
try {
|
|
404
|
+
await fsExtra.access(launcher, process.platform === 'win32' ? fsExtra.constants.F_OK : fsExtra.constants.X_OK);
|
|
405
|
+
await execa(process.execPath, [entry, '--version'], {
|
|
406
|
+
cwd: directory,
|
|
407
|
+
stdio: 'ignore',
|
|
408
|
+
timeout: 10000,
|
|
409
|
+
});
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let cancellation;
|
|
418
|
+
function beginBuildCancellation() {
|
|
419
|
+
const scope = { controller: new AbortController() };
|
|
420
|
+
cancellation = scope;
|
|
421
|
+
const cancel = (signal) => scope.controller.abort(new PakeError(`Build cancelled (${signal}).`, { code: 'BUILD_FAILED' }));
|
|
422
|
+
const interrupt = () => cancel('SIGINT');
|
|
423
|
+
const terminate = () => cancel('SIGTERM');
|
|
424
|
+
process.on('SIGINT', interrupt);
|
|
425
|
+
process.on('SIGTERM', terminate);
|
|
426
|
+
return () => {
|
|
427
|
+
process.off('SIGINT', interrupt);
|
|
428
|
+
process.off('SIGTERM', terminate);
|
|
429
|
+
if (cancellation === scope)
|
|
430
|
+
cancellation = undefined;
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function getBuildCancellationSignal() {
|
|
434
|
+
return cancellation?.controller.signal;
|
|
435
|
+
}
|
|
436
|
+
function preventBuildWorkspaceCleanup(reason) {
|
|
437
|
+
if (cancellation)
|
|
438
|
+
cancellation.unsafeCleanup = reason;
|
|
439
|
+
}
|
|
440
|
+
function throwIfBuildCancelled() {
|
|
441
|
+
getBuildCancellationSignal()?.throwIfAborted();
|
|
442
|
+
}
|
|
443
|
+
/** Copy only build inputs; cached or previously generated user content is not a template. */
|
|
444
|
+
async function createBuildWorkspace(sourceDirectory) {
|
|
445
|
+
const directory = await fsExtra.mkdtemp(path.join(os.tmpdir(), 'pake-build-'));
|
|
446
|
+
try {
|
|
447
|
+
for (const file of [
|
|
448
|
+
'package.json',
|
|
449
|
+
'pnpm-lock.yaml',
|
|
450
|
+
'package-lock.json',
|
|
451
|
+
'rust-toolchain.toml',
|
|
452
|
+
'rust-toolchain',
|
|
453
|
+
]) {
|
|
454
|
+
const source = path.join(sourceDirectory, file);
|
|
455
|
+
if (await fsExtra.pathExists(source))
|
|
456
|
+
await fsExtra.copy(source, path.join(directory, file));
|
|
457
|
+
}
|
|
458
|
+
const sourceTauri = path.join(sourceDirectory, 'src-tauri');
|
|
459
|
+
await fsExtra.copy(sourceTauri, path.join(directory, 'src-tauri'), {
|
|
460
|
+
filter: (source) => !['target', '.pake', 'gen'].includes(path.relative(sourceTauri, source).split(path.sep)[0]),
|
|
461
|
+
});
|
|
462
|
+
await fsExtra.ensureDir(path.join(directory, 'dist'));
|
|
463
|
+
// Keep the CLI runnable while local input staging replaces this workspace's dist.
|
|
464
|
+
await fsExtra.copy(path.join(sourceDirectory, 'dist', 'cli.js'), path.join(directory, 'dist', 'cli.js'));
|
|
465
|
+
const modules = path.join(sourceDirectory, 'node_modules');
|
|
466
|
+
if (await hasReadyTauriCli(sourceDirectory)) {
|
|
467
|
+
await fsExtra.symlink(modules, path.join(directory, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir');
|
|
468
|
+
}
|
|
469
|
+
return directory;
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
await fsExtra.remove(directory);
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/** Hold the cache through artifact copying, beyond Cargo's own compile lock. */
|
|
477
|
+
async function acquireBuildCache(targetDirectory) {
|
|
478
|
+
await fsExtra.ensureDir(targetDirectory);
|
|
479
|
+
const lock = path.join(targetDirectory, '.pake-build.lock');
|
|
480
|
+
const started = Date.now();
|
|
481
|
+
let announced = false;
|
|
482
|
+
for (;;) {
|
|
483
|
+
throwIfBuildCancelled();
|
|
484
|
+
try {
|
|
485
|
+
const handle = await fsExtra.open(lock, 'wx');
|
|
486
|
+
try {
|
|
487
|
+
fsExtra.writeFileSync(handle, String(process.pid));
|
|
488
|
+
}
|
|
489
|
+
finally {
|
|
490
|
+
fsExtra.closeSync(handle);
|
|
491
|
+
}
|
|
492
|
+
let released = false;
|
|
493
|
+
return async () => {
|
|
494
|
+
if (!released) {
|
|
495
|
+
await fsExtra.remove(lock);
|
|
496
|
+
released = true;
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
if (error.code !== 'EEXIST')
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const owner = Number(await fsExtra.readFile(lock, 'utf8'));
|
|
506
|
+
if (Number.isInteger(owner) && owner > 0) {
|
|
507
|
+
try {
|
|
508
|
+
process.kill(owner, 0);
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
if (error.code === 'ESRCH') {
|
|
512
|
+
// Read-then-unlink cannot atomically reclaim a dead owner's lock:
|
|
513
|
+
// another waiter may already have acquired a new one at this path.
|
|
514
|
+
throw new PakeError('A previous Pake process left a compilation cache lock.', {
|
|
515
|
+
code: 'BUILD_FAILED',
|
|
516
|
+
hint: `After stopping other Pake builds, remove ${lock} and retry.`,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
if (error.code === 'ENOENT')
|
|
524
|
+
continue;
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
if (Date.now() - started > 900000) {
|
|
528
|
+
throw new PakeError('Another Pake build is still using the compilation cache.', {
|
|
529
|
+
code: 'BUILD_FAILED',
|
|
530
|
+
hint: 'Wait for that build to finish, then retry.',
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
if (!announced) {
|
|
534
|
+
logger.info('Waiting for another Pake build to finish using the compilation cache...');
|
|
535
|
+
announced = true;
|
|
536
|
+
}
|
|
537
|
+
await setTimeout$1(200);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function enterBuildWorkspace() {
|
|
541
|
+
const previousTarget = process.env.CARGO_TARGET_DIR;
|
|
542
|
+
const targetDirectory = path.resolve(packageDirectory, previousTarget || 'src-tauri/target');
|
|
543
|
+
const release = await acquireBuildCache(targetDirectory);
|
|
544
|
+
let directory;
|
|
545
|
+
try {
|
|
546
|
+
directory = await createBuildWorkspace(packageDirectory);
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
await release();
|
|
550
|
+
throw error;
|
|
551
|
+
}
|
|
552
|
+
setBuildDirectory(directory);
|
|
553
|
+
process.env.CARGO_TARGET_DIR = targetDirectory;
|
|
554
|
+
const leave = async () => {
|
|
555
|
+
setBuildDirectory(packageDirectory);
|
|
556
|
+
if (previousTarget === undefined)
|
|
557
|
+
delete process.env.CARGO_TARGET_DIR;
|
|
558
|
+
else
|
|
559
|
+
process.env.CARGO_TARGET_DIR = previousTarget;
|
|
560
|
+
if (cancellation?.unsafeCleanup) {
|
|
561
|
+
logger.warn(`Build processes could not be confirmed stopped; keeping workspace ${directory} and its cache lock: ${cancellation.unsafeCleanup}`);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
await fsExtra.remove(directory);
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
logger.warn(`Could not remove the temporary build workspace ${directory}: ${String(error)}`);
|
|
569
|
+
}
|
|
570
|
+
finally {
|
|
571
|
+
try {
|
|
572
|
+
await release();
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
logger.warn(`Could not release the compilation cache lock: ${String(error)}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
if (getBuildCancellationSignal()?.aborted) {
|
|
580
|
+
await leave();
|
|
581
|
+
throwIfBuildCancelled();
|
|
582
|
+
}
|
|
583
|
+
return leave;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async function terminateBuildTree(pid) {
|
|
587
|
+
if (process.platform === 'win32') {
|
|
588
|
+
await execa('taskkill', ['/pid', String(pid), '/T', '/F'], {
|
|
589
|
+
timeout: 5000,
|
|
590
|
+
windowsHide: true,
|
|
591
|
+
});
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const killGroup = (signal) => {
|
|
595
|
+
try {
|
|
596
|
+
process.kill(-pid, signal);
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
if (error.code !== 'ESRCH')
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
killGroup('SIGTERM');
|
|
604
|
+
const started = Date.now();
|
|
605
|
+
for (;;) {
|
|
606
|
+
// The package manager can exit before its compiler descendants. Only
|
|
607
|
+
// release the cache once no live member of their process group remains.
|
|
608
|
+
// Zombies have already exited and cannot write artifacts.
|
|
609
|
+
const { stdout } = await execa('ps', ['-axo', 'pgid=,stat='], {
|
|
610
|
+
timeout: 1000,
|
|
611
|
+
});
|
|
612
|
+
const alive = stdout.split('\n').some((line) => {
|
|
613
|
+
const [group, state] = line.trim().split(/\s+/);
|
|
614
|
+
return Number(group) === pid && state && !state.startsWith('Z');
|
|
615
|
+
});
|
|
616
|
+
if (!alive)
|
|
617
|
+
return;
|
|
618
|
+
if (Date.now() - started > 5000)
|
|
619
|
+
throw new Error('Build process group did not stop.');
|
|
620
|
+
if (Date.now() - started >= 250)
|
|
621
|
+
killGroup('SIGKILL');
|
|
622
|
+
await setTimeout$1(25);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
374
625
|
async function shellExec(command, timeout = 300000, env) {
|
|
626
|
+
const signal = getBuildCancellationSignal();
|
|
627
|
+
signal?.throwIfAborted();
|
|
375
628
|
try {
|
|
376
|
-
const
|
|
377
|
-
cwd: npmDirectory,
|
|
629
|
+
const subprocess = execa(command.executable, command.args, {
|
|
630
|
+
cwd: command.cwd ?? npmDirectory,
|
|
378
631
|
// Use 'inherit' to show all output directly to user in real-time.
|
|
379
632
|
// This ensures linuxdeploy and other tool outputs are visible during builds.
|
|
380
633
|
// In machine mode (--json) stdout is reserved for the final JSON result,
|
|
@@ -382,22 +635,53 @@ async function shellExec(command, timeout = 300000, env) {
|
|
|
382
635
|
stdin: 'inherit',
|
|
383
636
|
stdout: isMachineMode() ? process.stderr : 'inherit',
|
|
384
637
|
stderr: 'inherit',
|
|
385
|
-
shell:
|
|
638
|
+
shell: false,
|
|
639
|
+
detached: Boolean(signal) && process.platform !== 'win32',
|
|
386
640
|
timeout,
|
|
387
641
|
env: env ? { ...process.env, ...env } : process.env,
|
|
388
642
|
});
|
|
389
|
-
|
|
643
|
+
let termination;
|
|
644
|
+
const cancel = () => {
|
|
645
|
+
if (termination || subprocess.pid === undefined)
|
|
646
|
+
return;
|
|
647
|
+
termination = terminateBuildTree(subprocess.pid).catch((error) => {
|
|
648
|
+
preventBuildWorkspaceCleanup(String(error));
|
|
649
|
+
subprocess.kill('SIGKILL');
|
|
650
|
+
});
|
|
651
|
+
};
|
|
652
|
+
signal?.addEventListener('abort', cancel, { once: true });
|
|
653
|
+
if (signal?.aborted)
|
|
654
|
+
cancel();
|
|
655
|
+
try {
|
|
656
|
+
const { exitCode } = await subprocess;
|
|
657
|
+
return exitCode;
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
// A timed-out or failed package manager can leave compiler descendants.
|
|
661
|
+
// Use the same tree barrier before the caller releases its cache lock.
|
|
662
|
+
if (signal)
|
|
663
|
+
cancel();
|
|
664
|
+
throw error;
|
|
665
|
+
}
|
|
666
|
+
finally {
|
|
667
|
+
signal?.removeEventListener('abort', cancel);
|
|
668
|
+
await termination;
|
|
669
|
+
signal?.throwIfAborted();
|
|
670
|
+
}
|
|
390
671
|
}
|
|
391
672
|
catch (error) {
|
|
673
|
+
if (signal?.aborted)
|
|
674
|
+
throw signal.reason;
|
|
675
|
+
const description = JSON.stringify([command.executable, ...command.args]);
|
|
392
676
|
const exitCode = error.exitCode ?? 'unknown';
|
|
393
677
|
const errorMessage = error.message || 'Unknown error occurred';
|
|
394
678
|
if (error.timedOut) {
|
|
395
|
-
throw new Error(`Command timed out after ${timeout}ms:
|
|
679
|
+
throw new Error(`Command timed out after ${timeout}ms: ${description}. Try increasing timeout or check network connectivity.`);
|
|
396
680
|
}
|
|
397
681
|
// AppImage/linuxdeploy guidance is added by the caller (BaseBuilder), which
|
|
398
682
|
// knows the build target. We only have the command line here (the tool's
|
|
399
683
|
// diagnostics stream to the terminal via stdio:inherit, not into the error).
|
|
400
|
-
throw new Error(`Error occurred while executing command
|
|
684
|
+
throw new Error(`Error occurred while executing command ${description}. Exit code: ${exitCode}. Details: ${errorMessage}`);
|
|
401
685
|
}
|
|
402
686
|
}
|
|
403
687
|
|
|
@@ -448,25 +732,54 @@ function ensureRustEnv() {
|
|
|
448
732
|
ensureCargoBinOnPath();
|
|
449
733
|
}
|
|
450
734
|
async function installRust() {
|
|
451
|
-
const rustInstallScriptForUnix = isCnMirrorEnabled()
|
|
452
|
-
? 'export RUSTUP_DIST_SERVER="https://rsproxy.cn" && export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" && curl --proto "=https" --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh'
|
|
453
|
-
: "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
|
|
454
|
-
const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup';
|
|
455
735
|
const spinner = getSpinner('Downloading Rust...');
|
|
456
736
|
try {
|
|
457
|
-
|
|
737
|
+
if (IS_WIN) {
|
|
738
|
+
await shellExec({
|
|
739
|
+
executable: 'winget',
|
|
740
|
+
args: ['install', '--id', 'Rustlang.Rustup'],
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
const useCnMirror = isCnMirrorEnabled();
|
|
745
|
+
const tempDir = await fsExtra.mkdtemp(path.join(os.tmpdir(), 'pake-rustup-'));
|
|
746
|
+
try {
|
|
747
|
+
const scriptPath = path.join(tempDir, 'rustup-init.sh');
|
|
748
|
+
await shellExec({
|
|
749
|
+
executable: 'curl',
|
|
750
|
+
args: [
|
|
751
|
+
'--proto',
|
|
752
|
+
'=https',
|
|
753
|
+
'--tlsv1.2',
|
|
754
|
+
'-sSf',
|
|
755
|
+
'-o',
|
|
756
|
+
scriptPath,
|
|
757
|
+
useCnMirror
|
|
758
|
+
? 'https://rsproxy.cn/rustup-init.sh'
|
|
759
|
+
: 'https://sh.rustup.rs',
|
|
760
|
+
],
|
|
761
|
+
});
|
|
762
|
+
await shellExec({
|
|
763
|
+
executable: 'sh',
|
|
764
|
+
args: useCnMirror ? [scriptPath] : [scriptPath, '-y'],
|
|
765
|
+
}, 300000, useCnMirror
|
|
766
|
+
? {
|
|
767
|
+
RUSTUP_DIST_SERVER: 'https://rsproxy.cn',
|
|
768
|
+
RUSTUP_UPDATE_ROOT: 'https://rsproxy.cn/rustup',
|
|
769
|
+
}
|
|
770
|
+
: undefined);
|
|
771
|
+
}
|
|
772
|
+
finally {
|
|
773
|
+
await fsExtra.remove(tempDir);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
458
776
|
spinner.succeed(chalk.green('โ Rust installed successfully!'));
|
|
459
777
|
ensureRustEnv();
|
|
460
778
|
}
|
|
461
779
|
catch (error) {
|
|
462
780
|
spinner.fail(chalk.red('โ Rust installation failed!'));
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
console.error(error);
|
|
468
|
-
}
|
|
469
|
-
process.exit(1);
|
|
781
|
+
// The CLI owns error reporting and workspace/cache cleanup.
|
|
782
|
+
throw error;
|
|
470
783
|
}
|
|
471
784
|
}
|
|
472
785
|
function checkRustInstalled() {
|
|
@@ -486,9 +799,13 @@ async function combineFiles(files, output) {
|
|
|
486
799
|
const fileContent = await fs$1.readFile(file, 'utf-8');
|
|
487
800
|
return `window.addEventListener('DOMContentLoaded', (_event) => {
|
|
488
801
|
const css = ${JSON.stringify(fileContent)};
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
802
|
+
if (typeof window.__PAKE_INJECT_STYLE__ === 'function') {
|
|
803
|
+
window.__PAKE_INJECT_STYLE__(css);
|
|
804
|
+
} else {
|
|
805
|
+
const style = document.createElement('style');
|
|
806
|
+
style.textContent = css;
|
|
807
|
+
document.head.appendChild(style);
|
|
808
|
+
}
|
|
492
809
|
});`;
|
|
493
810
|
}
|
|
494
811
|
const fileContent = await fs$1.readFile(file);
|
|
@@ -504,24 +821,6 @@ async function combineFiles(files, output) {
|
|
|
504
821
|
return files;
|
|
505
822
|
}
|
|
506
823
|
|
|
507
|
-
const logger = {
|
|
508
|
-
info(...msg) {
|
|
509
|
-
log.info(...msg.map((m) => chalk.white(m)));
|
|
510
|
-
},
|
|
511
|
-
debug(...msg) {
|
|
512
|
-
log.debug(...msg);
|
|
513
|
-
},
|
|
514
|
-
error(...msg) {
|
|
515
|
-
log.error(...msg.map((m) => chalk.red(m)));
|
|
516
|
-
},
|
|
517
|
-
warn(...msg) {
|
|
518
|
-
log.warn(...msg.map((m) => chalk.yellow(m)));
|
|
519
|
-
},
|
|
520
|
-
success(...msg) {
|
|
521
|
-
log.info(...msg.map((m) => chalk.green(m)));
|
|
522
|
-
},
|
|
523
|
-
};
|
|
524
|
-
|
|
525
824
|
function generateSafeFilename(name) {
|
|
526
825
|
return name
|
|
527
826
|
.replace(/[<>:"/\\|?*]/g, '_')
|
|
@@ -559,31 +858,6 @@ function generateIdentifierSafeName(name) {
|
|
|
559
858
|
return cleaned;
|
|
560
859
|
}
|
|
561
860
|
|
|
562
|
-
/**
|
|
563
|
-
* Error class used for user-facing CLI errors.
|
|
564
|
-
*
|
|
565
|
-
* The top-level catch in `bin/cli.ts` prints `message` directly without a
|
|
566
|
-
* stack trace and exits with the code mapped from `code` (see
|
|
567
|
-
* ERROR_EXIT_CODES in utils/output.ts). Use this for predictable failures
|
|
568
|
-
* (invalid names, missing files, etc.) so users see a clean message instead
|
|
569
|
-
* of a Node.js stack dump. `code` and `hint` also feed the `--json` result.
|
|
570
|
-
*/
|
|
571
|
-
class PakeError extends Error {
|
|
572
|
-
constructor(message, options) {
|
|
573
|
-
super(message);
|
|
574
|
-
this.isUserError = true;
|
|
575
|
-
this.name = 'PakeError';
|
|
576
|
-
this.code = options?.code;
|
|
577
|
-
this.hint = options?.hint;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
function isPakeError(error) {
|
|
581
|
-
return (error instanceof PakeError ||
|
|
582
|
-
(typeof error === 'object' &&
|
|
583
|
-
error !== null &&
|
|
584
|
-
error.isUserError === true));
|
|
585
|
-
}
|
|
586
|
-
|
|
587
861
|
const LINUX_TARGET_TYPES = ['deb', 'appimage', 'rpm', 'zst'];
|
|
588
862
|
// Returns the valid Linux build targets from a comma-separated targets
|
|
589
863
|
// string, preserving LINUX_TARGET_TYPES order. Unknown entries are dropped.
|
|
@@ -686,8 +960,11 @@ async function stageLocalTree(sourceDir) {
|
|
|
686
960
|
const resolvedPackage = await fsExtra
|
|
687
961
|
.realpath(npmDirectory)
|
|
688
962
|
.catch(() => path.resolve(npmDirectory));
|
|
963
|
+
const installedPackage = await fsExtra.realpath(packageDirectory);
|
|
689
964
|
const packageDist = path.join(resolvedPackage, 'dist');
|
|
690
|
-
if (resolvedSource ===
|
|
965
|
+
if (resolvedSource === installedPackage ||
|
|
966
|
+
installedPackage.startsWith(resolvedSource + path.sep) ||
|
|
967
|
+
resolvedSource === resolvedPackage ||
|
|
691
968
|
resolvedPackage.startsWith(resolvedSource + path.sep) ||
|
|
692
969
|
resolvedSource === packageDist ||
|
|
693
970
|
resolvedSource.startsWith(packageDist + path.sep)) {
|
|
@@ -919,7 +1196,7 @@ async function mergeIcons(options, name, tauriConf, platform, safeAppName) {
|
|
|
919
1196
|
delete tauriConf.app.trayIcon;
|
|
920
1197
|
}
|
|
921
1198
|
async function injectCustomCode(options, tauriConf) {
|
|
922
|
-
const { inject, proxyUrl, multiInstance, multiWindow, wasm } = options;
|
|
1199
|
+
const { inject, proxyUrl, basicAuth, multiInstance, multiWindow, wasm } = options;
|
|
923
1200
|
const injectFilePath = path.join(npmDirectory, 'src-tauri/src/inject/custom.js');
|
|
924
1201
|
if (inject?.length > 0) {
|
|
925
1202
|
const injectArray = Array.isArray(inject) ? inject : [inject];
|
|
@@ -936,6 +1213,7 @@ async function injectCustomCode(options, tauriConf) {
|
|
|
936
1213
|
await fsExtra.writeFile(injectFilePath, '');
|
|
937
1214
|
}
|
|
938
1215
|
tauriConf.pake.proxy_url = proxyUrl || '';
|
|
1216
|
+
tauriConf.pake.basic_auth = basicAuth;
|
|
939
1217
|
tauriConf.pake.multi_instance = multiInstance;
|
|
940
1218
|
tauriConf.pake.multi_window = multiWindow;
|
|
941
1219
|
if (wasm) {
|
|
@@ -1038,6 +1316,34 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
1038
1316
|
await writeAllConfigs(tauriConf, platform);
|
|
1039
1317
|
}
|
|
1040
1318
|
|
|
1319
|
+
// Load configs from npm package directory, not from project source
|
|
1320
|
+
const tauriSrcDir = path.join(npmDirectory, 'src-tauri');
|
|
1321
|
+
const pakeConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'pake.json'));
|
|
1322
|
+
const CommonConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.conf.json'));
|
|
1323
|
+
const WinConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.windows.conf.json'));
|
|
1324
|
+
const MacConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.macos.conf.json'));
|
|
1325
|
+
const LinuxConf = fsExtra.readJSONSync(path.join(tauriSrcDir, 'tauri.linux.conf.json'));
|
|
1326
|
+
const platformConfigs = {
|
|
1327
|
+
win32: WinConf,
|
|
1328
|
+
darwin: MacConf,
|
|
1329
|
+
linux: LinuxConf,
|
|
1330
|
+
};
|
|
1331
|
+
const { platform: platform$1 } = process;
|
|
1332
|
+
// @ts-ignore
|
|
1333
|
+
const platformConfig = platformConfigs[platform$1];
|
|
1334
|
+
let tauriConfig = {
|
|
1335
|
+
...CommonConf,
|
|
1336
|
+
bundle: platformConfig.bundle,
|
|
1337
|
+
app: {
|
|
1338
|
+
...CommonConf.app,
|
|
1339
|
+
trayIcon: {
|
|
1340
|
+
...(platformConfig?.app?.trayIcon ?? {}),
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1343
|
+
build: CommonConf.build,
|
|
1344
|
+
pake: pakeConf,
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1041
1347
|
/**
|
|
1042
1348
|
* Returns build environment variables overrides for macOS, where Rust crates
|
|
1043
1349
|
* sometimes need explicit C/C++ flags and a deterministic SDK target. Other
|
|
@@ -1129,11 +1435,12 @@ async function detectPackageManager() {
|
|
|
1129
1435
|
return 'pnpm';
|
|
1130
1436
|
}
|
|
1131
1437
|
function getInstallCommand(packageManager, useCnMirror) {
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1438
|
+
const args = ['install'];
|
|
1439
|
+
if (useCnMirror)
|
|
1440
|
+
args.push('--registry=https://registry.npmmirror.com');
|
|
1441
|
+
if (packageManager === 'npm')
|
|
1442
|
+
args.push('--legacy-peer-deps');
|
|
1443
|
+
return { executable: packageManager, args };
|
|
1137
1444
|
}
|
|
1138
1445
|
async function copyFileWithSamePathGuard(sourcePath, destinationPath) {
|
|
1139
1446
|
if (path.resolve(sourcePath) === path.resolve(destinationPath)) {
|
|
@@ -1265,7 +1572,7 @@ class BaseBuilder {
|
|
|
1265
1572
|
}
|
|
1266
1573
|
async prepare() {
|
|
1267
1574
|
const tauriSrcPath = path.join(npmDirectory, 'src-tauri');
|
|
1268
|
-
const tauriTargetPath =
|
|
1575
|
+
const tauriTargetPath = this.getCargoTargetDir();
|
|
1269
1576
|
const tauriTargetPathExists = await fsExtra.pathExists(tauriTargetPath);
|
|
1270
1577
|
if (!IS_MAC && !tauriTargetPathExists) {
|
|
1271
1578
|
logger.warn('โผ The first use requires installing system dependencies.');
|
|
@@ -1294,9 +1601,21 @@ class BaseBuilder {
|
|
|
1294
1601
|
});
|
|
1295
1602
|
}
|
|
1296
1603
|
}
|
|
1297
|
-
const spinner = getSpinner('Installing package...');
|
|
1298
1604
|
const useCnMirror = isCnMirrorEnabled();
|
|
1299
1605
|
await configureCargoRegistry(tauriSrcPath, useCnMirror);
|
|
1606
|
+
// Workspaces reuse installed dependencies. Reinstalling through their
|
|
1607
|
+
// node_modules link would mutate the shared CLI installation.
|
|
1608
|
+
if (await hasReadyTauriCli(npmDirectory)) {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
// Dependencies may disappear after the workspace linked them. Reinstall
|
|
1612
|
+
// privately even in that case, without writing through to the source tree.
|
|
1613
|
+
const modules = path.join(npmDirectory, 'node_modules');
|
|
1614
|
+
if (npmDirectory !== packageDirectory &&
|
|
1615
|
+
(await fsExtra.lstat(modules).catch(() => null))?.isSymbolicLink()) {
|
|
1616
|
+
await fsExtra.unlink(modules);
|
|
1617
|
+
}
|
|
1618
|
+
const spinner = getSpinner('Installing package...');
|
|
1300
1619
|
const packageManager = await detectPackageManager();
|
|
1301
1620
|
const timeout = getInstallTimeout();
|
|
1302
1621
|
const buildEnv = getBuildEnvironment();
|
|
@@ -1332,23 +1651,24 @@ class BaseBuilder {
|
|
|
1332
1651
|
}
|
|
1333
1652
|
async start(url) {
|
|
1334
1653
|
logger.info('Pake dev server starting...');
|
|
1335
|
-
await mergeConfig(url, this.options, tauriConfig);
|
|
1654
|
+
await mergeConfig(url, this.options, structuredClone(tauriConfig));
|
|
1336
1655
|
const packageManager = await detectPackageManager();
|
|
1337
1656
|
const configPath = path.join(npmDirectory, 'src-tauri', '.pake', 'tauri.conf.json');
|
|
1338
1657
|
const features = this.getBuildFeatures();
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1658
|
+
const args = ['run', 'tauri'];
|
|
1659
|
+
if (packageManager === 'npm')
|
|
1660
|
+
args.push('--');
|
|
1661
|
+
args.push('dev', '--config', configPath);
|
|
1662
|
+
if (features.length > 0)
|
|
1663
|
+
args.push('--features', features.join(','));
|
|
1664
|
+
await shellExec({ executable: packageManager, args });
|
|
1343
1665
|
}
|
|
1344
1666
|
async buildAndCopy(url, target, logSuccess = true) {
|
|
1345
1667
|
const { name = 'pake-app' } = this.options;
|
|
1346
|
-
await mergeConfig(url, this.options, tauriConfig);
|
|
1668
|
+
await mergeConfig(url, this.options, structuredClone(tauriConfig));
|
|
1347
1669
|
const packageManager = await detectPackageManager();
|
|
1348
1670
|
// Build app
|
|
1349
1671
|
const buildSpinner = getSpinner('Building app...');
|
|
1350
|
-
// Let spinner run for a moment so user can see it, then stop before package manager command
|
|
1351
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1352
1672
|
buildSpinner.stop();
|
|
1353
1673
|
// Show static message to keep the status visible. Info, not warn: warn
|
|
1354
1674
|
// entries feed the --json warnings array and this is a status line.
|
|
@@ -1365,7 +1685,7 @@ class BaseBuilder {
|
|
|
1365
1685
|
if (isLinuxAppImage && !buildEnv.NO_STRIP && this.options.debug) {
|
|
1366
1686
|
logger.warn('โ AppImage strip step can fail on glibc 2.38+; Pake will auto-retry with NO_STRIP=1.');
|
|
1367
1687
|
}
|
|
1368
|
-
const buildCommand =
|
|
1688
|
+
const buildCommand = this.getBuildCommand(packageManager);
|
|
1369
1689
|
const buildTimeout = getBuildTimeout();
|
|
1370
1690
|
try {
|
|
1371
1691
|
await shellExec(buildCommand, buildTimeout, resolveExecEnv());
|
|
@@ -1473,24 +1793,23 @@ class BaseBuilder {
|
|
|
1473
1793
|
return BaseBuilder.ARCH_DISPLAY_NAMES[arch] || arch;
|
|
1474
1794
|
}
|
|
1475
1795
|
buildBaseCommand(packageManager, configPath, target) {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
let fullCommand = `${baseCommand}${argSeparator} -c "${configPath}"`;
|
|
1796
|
+
const args = ['run', this.options.debug ? 'build:debug' : 'build'];
|
|
1797
|
+
if (packageManager === 'npm')
|
|
1798
|
+
args.push('--');
|
|
1799
|
+
args.push('-c', configPath);
|
|
1481
1800
|
if (target) {
|
|
1482
|
-
|
|
1801
|
+
args.push('--target', target);
|
|
1483
1802
|
}
|
|
1484
1803
|
// Enable verbose output in debug mode to help diagnose build issues.
|
|
1485
1804
|
// This provides detailed logs from Tauri CLI and bundler tools.
|
|
1486
1805
|
if (this.options.debug) {
|
|
1487
|
-
|
|
1806
|
+
args.push('--verbose');
|
|
1488
1807
|
}
|
|
1489
1808
|
const features = this.getBuildFeatures();
|
|
1490
1809
|
if (features.length > 0) {
|
|
1491
|
-
|
|
1810
|
+
args.push('--features', features.join(','));
|
|
1492
1811
|
}
|
|
1493
|
-
return
|
|
1812
|
+
return { executable: packageManager, args };
|
|
1494
1813
|
}
|
|
1495
1814
|
getBuildFeatures() {
|
|
1496
1815
|
const features = ['cli-build'];
|
|
@@ -1506,10 +1825,10 @@ class BaseBuilder {
|
|
|
1506
1825
|
getBuildCommand(packageManager = 'pnpm') {
|
|
1507
1826
|
// Use temporary config directory to avoid modifying source files
|
|
1508
1827
|
const configPath = path.join(npmDirectory, 'src-tauri', '.pake', 'tauri.conf.json');
|
|
1509
|
-
|
|
1828
|
+
const fullCommand = this.buildBaseCommand(packageManager, configPath);
|
|
1510
1829
|
// For macOS, use app bundles by default unless DMG is explicitly requested
|
|
1511
1830
|
if (IS_MAC && this.options.targets === 'app') {
|
|
1512
|
-
fullCommand
|
|
1831
|
+
fullCommand.args.push('--bundles', 'app');
|
|
1513
1832
|
}
|
|
1514
1833
|
return fullCommand;
|
|
1515
1834
|
}
|
|
@@ -1660,7 +1979,7 @@ class MacBuilder extends BaseBuilder {
|
|
|
1660
1979
|
else {
|
|
1661
1980
|
arch = this.getArchDisplayName(this.resolveTargetArch(this.buildArch));
|
|
1662
1981
|
}
|
|
1663
|
-
return `${name}_${
|
|
1982
|
+
return `${name}_${this.options.appVersion}_${arch}`;
|
|
1664
1983
|
}
|
|
1665
1984
|
getReportArch() {
|
|
1666
1985
|
return this.getActualArch();
|
|
@@ -1723,9 +2042,9 @@ class WinBuilder extends BaseBuilder {
|
|
|
1723
2042
|
}
|
|
1724
2043
|
getFileName() {
|
|
1725
2044
|
const { name } = this.options;
|
|
1726
|
-
const language =
|
|
2045
|
+
const language = this.options.installerLanguage;
|
|
1727
2046
|
const targetArch = this.getArchDisplayName(this.buildArch);
|
|
1728
|
-
return `${name}_${
|
|
2047
|
+
return `${name}_${this.options.appVersion}_${targetArch}_${language}`;
|
|
1729
2048
|
}
|
|
1730
2049
|
getBuildCommand(packageManager = 'pnpm') {
|
|
1731
2050
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
|
@@ -1781,7 +2100,7 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1781
2100
|
}
|
|
1782
2101
|
getFileName() {
|
|
1783
2102
|
const { name = 'pake-app', targets } = this.options;
|
|
1784
|
-
const version =
|
|
2103
|
+
const version = this.options.appVersion;
|
|
1785
2104
|
const buildType = this.currentBuildType || targets.split(',').map((t) => t.trim())[0];
|
|
1786
2105
|
let arch;
|
|
1787
2106
|
if (this.buildArch === 'arm64') {
|
|
@@ -1859,7 +2178,7 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1859
2178
|
];
|
|
1860
2179
|
for (const { tool, pacmanPackage } of requiredTools) {
|
|
1861
2180
|
try {
|
|
1862
|
-
await
|
|
2181
|
+
await execa(tool, ['--version'], { stdio: 'ignore' });
|
|
1863
2182
|
}
|
|
1864
2183
|
catch {
|
|
1865
2184
|
throw new Error(`Building a zst package requires "${tool}". Install it first, e.g. "sudo pacman -S ${pacmanPackage}".`);
|
|
@@ -1869,24 +2188,30 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1869
2188
|
async createArchPackageFromDeb({ removeSourceDeb, }) {
|
|
1870
2189
|
const { name = 'pake-app' } = this.options;
|
|
1871
2190
|
const packageName = generateLinuxPackageName(name);
|
|
1872
|
-
const version =
|
|
2191
|
+
const version = this.options.appVersion;
|
|
1873
2192
|
const arch = this.buildArch === 'arm64' ? 'aarch64' : 'x86_64';
|
|
1874
2193
|
const debPath = path.resolve(`${name}.deb`);
|
|
1875
2194
|
const packagePath = path.resolve(`${name}-${version}-1-${arch}.pkg.tar.zst`);
|
|
1876
|
-
|
|
2195
|
+
await this.ensureArchPackagingTools();
|
|
2196
|
+
const workDir = await fsExtra.mkdtemp(path.join(os.tmpdir(), 'pake-arch-'));
|
|
1877
2197
|
const dataDir = path.join(workDir, 'data');
|
|
1878
2198
|
const controlDir = path.join(workDir, 'control');
|
|
1879
|
-
await this.ensureArchPackagingTools();
|
|
1880
|
-
await fsExtra.remove(workDir);
|
|
1881
|
-
await fsExtra.ensureDir(dataDir);
|
|
1882
|
-
await fsExtra.ensureDir(controlDir);
|
|
1883
2199
|
try {
|
|
1884
|
-
await
|
|
2200
|
+
await fsExtra.ensureDir(dataDir);
|
|
2201
|
+
await fsExtra.ensureDir(controlDir);
|
|
2202
|
+
await shellExec({
|
|
2203
|
+
executable: 'ar',
|
|
2204
|
+
args: ['x', debPath],
|
|
2205
|
+
cwd: controlDir,
|
|
2206
|
+
});
|
|
1885
2207
|
const dataArchive = (await fsExtra.readdir(controlDir)).find((file) => file.startsWith('data.tar'));
|
|
1886
2208
|
if (!dataArchive) {
|
|
1887
2209
|
throw new Error(`Could not find data.tar payload in ${debPath}`);
|
|
1888
2210
|
}
|
|
1889
|
-
await shellExec(
|
|
2211
|
+
await shellExec({
|
|
2212
|
+
executable: 'tar',
|
|
2213
|
+
args: ['-xf', path.join(controlDir, dataArchive), '-C', dataDir],
|
|
2214
|
+
});
|
|
1890
2215
|
// Drop the desktop entry auto-generated by the Tauri deb bundler;
|
|
1891
2216
|
// the payload already ships Pake's own com.pake.<name>.desktop.
|
|
1892
2217
|
await fsExtra.remove(path.join(dataDir, 'usr', 'share', 'applications', `${packageName}.desktop`));
|
|
@@ -1926,7 +2251,19 @@ post_remove() {
|
|
|
1926
2251
|
update-desktop-database -q usr/share/applications
|
|
1927
2252
|
}
|
|
1928
2253
|
`);
|
|
1929
|
-
await shellExec(
|
|
2254
|
+
await shellExec({
|
|
2255
|
+
executable: 'bsdtar',
|
|
2256
|
+
args: [
|
|
2257
|
+
'--zstd',
|
|
2258
|
+
'-cf',
|
|
2259
|
+
packagePath,
|
|
2260
|
+
'-C',
|
|
2261
|
+
dataDir,
|
|
2262
|
+
'.PKGINFO',
|
|
2263
|
+
'.INSTALL',
|
|
2264
|
+
'usr',
|
|
2265
|
+
],
|
|
2266
|
+
});
|
|
1930
2267
|
await this.recordArtifact(packagePath, 'zst');
|
|
1931
2268
|
logger.success('โ Build success!');
|
|
1932
2269
|
logger.success('โ App installer located in', packagePath);
|
|
@@ -1964,14 +2301,15 @@ post_remove() {
|
|
|
1964
2301
|
const buildTarget = this.buildArch === 'arm64'
|
|
1965
2302
|
? (this.getTauriTarget(this.buildArch, 'linux') ?? undefined)
|
|
1966
2303
|
: undefined;
|
|
1967
|
-
|
|
2304
|
+
const fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
|
|
1968
2305
|
// --no-bundle: build the executable only, skipping .deb/.rpm/.appimage
|
|
1969
2306
|
// packaging entirely (e.g. RPM-based distros where the bundler aborts).
|
|
1970
2307
|
if (this.options.bundle === false) {
|
|
1971
|
-
|
|
2308
|
+
fullCommand.args.push('--no-bundle');
|
|
2309
|
+
return fullCommand;
|
|
1972
2310
|
}
|
|
1973
2311
|
if (this.currentBuildType) {
|
|
1974
|
-
fullCommand
|
|
2312
|
+
fullCommand.args.push('--bundles', this.currentBuildType);
|
|
1975
2313
|
}
|
|
1976
2314
|
// Enable verbose output for AppImage builds when debugging or PAKE_VERBOSE is set.
|
|
1977
2315
|
// AppImage builds often fail with minimal error messages from linuxdeploy,
|
|
@@ -1980,7 +2318,7 @@ post_remove() {
|
|
|
1980
2318
|
(this.options.targets.includes('appimage') ||
|
|
1981
2319
|
this.options.debug ||
|
|
1982
2320
|
process.env.PAKE_VERBOSE)) {
|
|
1983
|
-
fullCommand
|
|
2321
|
+
fullCommand.args.push('--verbose');
|
|
1984
2322
|
}
|
|
1985
2323
|
return fullCommand;
|
|
1986
2324
|
}
|
|
@@ -2828,7 +3166,6 @@ async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
|
|
|
2828
3166
|
const response = await fetch(iconUrl, {
|
|
2829
3167
|
signal: controller.signal,
|
|
2830
3168
|
});
|
|
2831
|
-
clearTimeout(timeoutId);
|
|
2832
3169
|
if (!response.ok) {
|
|
2833
3170
|
if (response.status === 404 && !showSpinner) {
|
|
2834
3171
|
return null;
|
|
@@ -2845,7 +3182,6 @@ async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
|
|
|
2845
3182
|
return await saveIconFile(arrayBuffer, extension);
|
|
2846
3183
|
}
|
|
2847
3184
|
catch (error) {
|
|
2848
|
-
clearTimeout(timeoutId);
|
|
2849
3185
|
if (showSpinner) {
|
|
2850
3186
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
2851
3187
|
logger.error('Icon download timed out!');
|
|
@@ -2856,6 +3192,9 @@ async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
|
|
|
2856
3192
|
}
|
|
2857
3193
|
return null;
|
|
2858
3194
|
}
|
|
3195
|
+
finally {
|
|
3196
|
+
clearTimeout(timeoutId);
|
|
3197
|
+
}
|
|
2859
3198
|
}
|
|
2860
3199
|
/**
|
|
2861
3200
|
* Saves icon file to temporary location
|
|
@@ -3031,6 +3370,7 @@ const DEFAULT_PAKE_OPTIONS = {
|
|
|
3031
3370
|
useLocalFile: false,
|
|
3032
3371
|
systemTrayIcon: '',
|
|
3033
3372
|
proxyUrl: '',
|
|
3373
|
+
basicAuth: false,
|
|
3034
3374
|
debug: false,
|
|
3035
3375
|
json: false,
|
|
3036
3376
|
inject: [],
|
|
@@ -3133,6 +3473,9 @@ ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with
|
|
|
3133
3473
|
.option('--debug', 'Debug build and more output', DEFAULT_PAKE_OPTIONS.debug)
|
|
3134
3474
|
.option('--json', 'Machine-readable output: logs to stderr, one JSON result on stdout', DEFAULT_PAKE_OPTIONS.json)
|
|
3135
3475
|
.option('--config <path>', 'Load options from a JSON config file (fields mirror CLI options, see schema/pake.schema.json)')
|
|
3476
|
+
.addOption(new Option('--basic-auth', 'Prompt for HTTP Basic credentials at runtime (macOS only)')
|
|
3477
|
+
.default(DEFAULT_PAKE_OPTIONS.basicAuth)
|
|
3478
|
+
.hideHelp())
|
|
3136
3479
|
.addOption(new Option('--proxy-url <url>', 'Proxy URL for all network requests (http://, https://, socks5://)')
|
|
3137
3480
|
.default(DEFAULT_PAKE_OPTIONS.proxyUrl)
|
|
3138
3481
|
.hideHelp())
|
|
@@ -3430,6 +3773,8 @@ program.action(async (urlArg, options) => {
|
|
|
3430
3773
|
let phase = 'input';
|
|
3431
3774
|
let appName = null;
|
|
3432
3775
|
let url = urlArg;
|
|
3776
|
+
let leaveWorkspace;
|
|
3777
|
+
let endCancellation;
|
|
3433
3778
|
try {
|
|
3434
3779
|
// Heal a dist_bak stranded by an earlier crashed local-input run before
|
|
3435
3780
|
// building, or this build would embed that run's staged files.
|
|
@@ -3476,13 +3821,22 @@ program.action(async (urlArg, options) => {
|
|
|
3476
3821
|
if (options.debug) {
|
|
3477
3822
|
log.setLevel('debug');
|
|
3478
3823
|
}
|
|
3824
|
+
endCancellation = beginBuildCancellation();
|
|
3825
|
+
phase = 'prepare';
|
|
3826
|
+
leaveWorkspace = await enterBuildWorkspace();
|
|
3827
|
+
phase = 'input';
|
|
3479
3828
|
const appOptions = await handleOptions(options, url);
|
|
3829
|
+
throwIfBuildCancelled();
|
|
3480
3830
|
appName = appOptions.name ?? null;
|
|
3481
3831
|
const builder = BuilderProvider.create(appOptions);
|
|
3482
3832
|
phase = 'prepare';
|
|
3483
3833
|
await builder.prepare();
|
|
3834
|
+
throwIfBuildCancelled();
|
|
3484
3835
|
phase = 'build';
|
|
3485
3836
|
await builder.build(url);
|
|
3837
|
+
throwIfBuildCancelled();
|
|
3838
|
+
await leaveWorkspace();
|
|
3839
|
+
leaveWorkspace = undefined;
|
|
3486
3840
|
if (jsonMode) {
|
|
3487
3841
|
printJsonResult({
|
|
3488
3842
|
ok: true,
|
|
@@ -3501,6 +3855,10 @@ program.action(async (urlArg, options) => {
|
|
|
3501
3855
|
if (isCommanderExit(error) && error.exitCode === 0) {
|
|
3502
3856
|
return;
|
|
3503
3857
|
}
|
|
3858
|
+
if (leaveWorkspace) {
|
|
3859
|
+
await leaveWorkspace();
|
|
3860
|
+
leaveWorkspace = undefined;
|
|
3861
|
+
}
|
|
3504
3862
|
const classified = classifyError(error, phase);
|
|
3505
3863
|
if (jsonMode) {
|
|
3506
3864
|
printJsonResult({
|
|
@@ -3533,10 +3891,14 @@ program.action(async (urlArg, options) => {
|
|
|
3533
3891
|
process.exitCode = ERROR_EXIT_CODES[classified.code];
|
|
3534
3892
|
}
|
|
3535
3893
|
finally {
|
|
3536
|
-
//
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3894
|
+
// Failed builds discard their private inputs without touching the package.
|
|
3895
|
+
try {
|
|
3896
|
+
if (leaveWorkspace)
|
|
3897
|
+
await leaveWorkspace();
|
|
3898
|
+
}
|
|
3899
|
+
finally {
|
|
3900
|
+
endCancellation?.();
|
|
3901
|
+
}
|
|
3540
3902
|
}
|
|
3541
3903
|
});
|
|
3542
3904
|
program.parseAsync().catch((error) => {
|