@oniroproject/core 0.14.1 → 0.15.0
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 -0
- package/dist/index.cjs +374 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -12
- package/dist/index.d.ts +97 -12
- package/dist/index.js +366 -156
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -180,6 +180,7 @@ var ALL_SDKS = [
|
|
|
180
180
|
{ version: "7.0", api: "26", tarballStrip: { linuxWindows: 1, darwin: 3 }, archiveSuffix: "_20260829" }
|
|
181
181
|
];
|
|
182
182
|
var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
|
|
183
|
+
var OHOS_DAILY_BUILDS_URL = "https://dcp.openharmony.cn/workbench/cicd/dailybuild/dailylist";
|
|
183
184
|
|
|
184
185
|
// src/sdk/platform.ts
|
|
185
186
|
import * as os2 from "os";
|
|
@@ -762,8 +763,17 @@ async function extractZipWithProgress(opts) {
|
|
|
762
763
|
if (inc > 0) progress.report({ message: "Extracting: 100%", increment: inc });
|
|
763
764
|
}
|
|
764
765
|
}
|
|
765
|
-
async function extractTarball(tarPath, dest, strip = 0) {
|
|
766
|
-
await tar.x({ file: tarPath, cwd: dest, strip });
|
|
766
|
+
async function extractTarball(tarPath, dest, strip = 0, filter) {
|
|
767
|
+
await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
|
|
768
|
+
}
|
|
769
|
+
async function readZipEntry(zipPath, match) {
|
|
770
|
+
const zip = new StreamZip.async({ file: zipPath });
|
|
771
|
+
try {
|
|
772
|
+
const entry = Object.values(await zip.entries()).find((e) => !e.isDirectory && match(e.name));
|
|
773
|
+
return entry ? await zip.entryData(entry) : void 0;
|
|
774
|
+
} finally {
|
|
775
|
+
await zip.close();
|
|
776
|
+
}
|
|
767
777
|
}
|
|
768
778
|
|
|
769
779
|
// src/sdk/list.ts
|
|
@@ -869,34 +879,7 @@ async function downloadAndInstallSdk(opts) {
|
|
|
869
879
|
`Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
|
|
870
880
|
);
|
|
871
881
|
}
|
|
872
|
-
|
|
873
|
-
const componentsStart = 60;
|
|
874
|
-
const componentsBudget = 35;
|
|
875
|
-
const n = zipFiles.length;
|
|
876
|
-
if (n === 0) {
|
|
877
|
-
progress?.report({ message: "No SDK component ZIPs found.", increment: componentsBudget });
|
|
878
|
-
} else {
|
|
879
|
-
progress?.report({ message: "Extracting SDK components...", increment: 0 });
|
|
880
|
-
}
|
|
881
|
-
const base = n > 0 ? Math.floor(componentsBudget / n) : 0;
|
|
882
|
-
let rem = n > 0 ? componentsBudget % n : 0;
|
|
883
|
-
let cursor = componentsStart;
|
|
884
|
-
for (const zipFile of zipFiles) {
|
|
885
|
-
checkCancelled();
|
|
886
|
-
logger.info(`Extracting component ${zipFile}`);
|
|
887
|
-
const zipPath = path10.join(osContentPath, zipFile);
|
|
888
|
-
const thisBudget = base + (rem > 0 ? 1 : 0);
|
|
889
|
-
if (rem > 0) rem--;
|
|
890
|
-
await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
|
|
891
|
-
cursor += thisBudget;
|
|
892
|
-
fs10.unlinkSync(zipPath);
|
|
893
|
-
}
|
|
894
|
-
progress?.report({ message: "Finalizing installation...", increment: 0 });
|
|
895
|
-
if (fs10.existsSync(sdkInstallDir)) {
|
|
896
|
-
fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
|
|
897
|
-
}
|
|
898
|
-
movePath(osContentPath, sdkInstallDir);
|
|
899
|
-
progress?.report({ message: "Finalizing installation...", increment: 3 });
|
|
882
|
+
await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 60, range: 35, checkCancelled });
|
|
900
883
|
progress?.report({ message: "Cleaning up...", increment: 0 });
|
|
901
884
|
removeTempWorkDir(tmpDir, tmpRoot);
|
|
902
885
|
progress?.report({ message: "Cleaning up...", increment: 2 });
|
|
@@ -907,6 +890,115 @@ async function downloadAndInstallSdk(opts) {
|
|
|
907
890
|
throw wrapped;
|
|
908
891
|
}
|
|
909
892
|
}
|
|
893
|
+
async function installComponents(opts) {
|
|
894
|
+
const { osContentPath, sdkInstallDir, progress, logger, checkCancelled } = opts;
|
|
895
|
+
const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
|
|
896
|
+
const n = zipFiles.length;
|
|
897
|
+
if (n === 0) {
|
|
898
|
+
progress?.report({ message: "No SDK component ZIPs found.", increment: opts.range });
|
|
899
|
+
} else {
|
|
900
|
+
progress?.report({ message: "Extracting SDK components...", increment: 0 });
|
|
901
|
+
}
|
|
902
|
+
const base = n > 0 ? Math.floor(opts.range / n) : 0;
|
|
903
|
+
let rem = n > 0 ? opts.range % n : 0;
|
|
904
|
+
let cursor = opts.start;
|
|
905
|
+
for (const zipFile of zipFiles) {
|
|
906
|
+
checkCancelled();
|
|
907
|
+
logger.info(`Extracting component ${zipFile}`);
|
|
908
|
+
const zipPath = path10.join(osContentPath, zipFile);
|
|
909
|
+
const thisBudget = base + (rem > 0 ? 1 : 0);
|
|
910
|
+
if (rem > 0) rem--;
|
|
911
|
+
await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
|
|
912
|
+
cursor += thisBudget;
|
|
913
|
+
fs10.unlinkSync(zipPath);
|
|
914
|
+
}
|
|
915
|
+
progress?.report({ message: "Finalizing installation...", increment: 0 });
|
|
916
|
+
if (fs10.existsSync(sdkInstallDir)) {
|
|
917
|
+
fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
|
|
918
|
+
}
|
|
919
|
+
movePath(osContentPath, sdkInstallDir);
|
|
920
|
+
progress?.report({ message: "Finalizing installation...", increment: 3 });
|
|
921
|
+
}
|
|
922
|
+
async function installSdkFromArchive(opts) {
|
|
923
|
+
const { config, archivePath, progress, abortSignal } = opts;
|
|
924
|
+
const logger = opts.logger ?? noopLogger;
|
|
925
|
+
if (!fs10.statSync(archivePath, { throwIfNoEntry: false })?.isFile()) {
|
|
926
|
+
throw new OniroError(`SDK archive not found: ${archivePath}`);
|
|
927
|
+
}
|
|
928
|
+
const osFolder = getOsFolder();
|
|
929
|
+
const what = `the SDK install from '${path10.basename(archivePath)}'`;
|
|
930
|
+
const sdkRoot = getSdkRootDir(config);
|
|
931
|
+
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
932
|
+
config,
|
|
933
|
+
override: opts.tmpDir,
|
|
934
|
+
installRoot: sdkRoot,
|
|
935
|
+
prefix: "oniro-sdk-",
|
|
936
|
+
logger
|
|
937
|
+
});
|
|
938
|
+
const extractDir = path10.join(tmpDir, "extract");
|
|
939
|
+
fs10.mkdirSync(extractDir);
|
|
940
|
+
const checkCancelled = () => {
|
|
941
|
+
if (abortSignal?.aborted) throw new CancelledError();
|
|
942
|
+
};
|
|
943
|
+
try {
|
|
944
|
+
progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 0 });
|
|
945
|
+
await extractTarball(archivePath, extractDir, 0, (p) => p.split("/").includes(osFolder));
|
|
946
|
+
progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 40 });
|
|
947
|
+
checkCancelled();
|
|
948
|
+
const osContentPath = findSdkOsDir(extractDir, osFolder);
|
|
949
|
+
if (!osContentPath) {
|
|
950
|
+
throw new OniroError(
|
|
951
|
+
`No '${osFolder}' SDK components (a '${osFolder}' folder of ZIPs) found in ${archivePath}. Is it an OpenHarmony SDK archive for this OS?`
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
const { api, version } = await readSdkComponentsMeta(osContentPath);
|
|
955
|
+
const sdkInstallDir = path10.join(sdkRoot, osFolder, api);
|
|
956
|
+
if (fs10.existsSync(sdkInstallDir) && !opts.force) {
|
|
957
|
+
throw new OniroError(
|
|
958
|
+
`An SDK for api ${api} is already installed at ${sdkInstallDir}; pass --force to replace it with this archive, or set ONIRO_SDK_ROOT_DIR to install it in a separate SDK root.`
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
fs10.mkdirSync(path10.dirname(sdkInstallDir), { recursive: true });
|
|
962
|
+
await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 40, range: 55, checkCancelled });
|
|
963
|
+
progress?.report({ message: "Cleaning up...", increment: 0 });
|
|
964
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
965
|
+
progress?.report({ message: "Cleaning up...", increment: 2 });
|
|
966
|
+
return { api, version, dir: sdkInstallDir };
|
|
967
|
+
} catch (err) {
|
|
968
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
969
|
+
throw toSpaceError(err, tmpRoot, what);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function findSdkOsDir(root, osFolder, maxDepth = 4) {
|
|
973
|
+
let level = [root];
|
|
974
|
+
for (let depth = 0; depth < maxDepth && level.length > 0; depth++) {
|
|
975
|
+
const next = [];
|
|
976
|
+
for (const dir of level) {
|
|
977
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
978
|
+
if (!entry.isDirectory()) continue;
|
|
979
|
+
const p = path10.join(dir, entry.name);
|
|
980
|
+
if (entry.name === osFolder && fs10.readdirSync(p).some((n) => n.endsWith(".zip"))) return p;
|
|
981
|
+
next.push(p);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
level = next;
|
|
985
|
+
}
|
|
986
|
+
return void 0;
|
|
987
|
+
}
|
|
988
|
+
async function readSdkComponentsMeta(dir) {
|
|
989
|
+
const zips = fs10.readdirSync(dir).filter((n) => n.endsWith(".zip")).sort((a, b) => Number(b.startsWith("ets-")) - Number(a.startsWith("ets-")));
|
|
990
|
+
for (const name of zips) {
|
|
991
|
+
const data = await readZipEntry(path10.join(dir, name), (e) => /^[^/]+\/oh-uni-package\.json$/.test(e));
|
|
992
|
+
if (!data) continue;
|
|
993
|
+
try {
|
|
994
|
+
const pkg = JSON.parse(data.toString("utf8"));
|
|
995
|
+
const api = String(pkg.apiVersion ?? "");
|
|
996
|
+
if (/^\d+$/.test(api)) return { api, version: String(pkg.version ?? "") };
|
|
997
|
+
} catch {
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
throw new OniroError("No SDK component in the archive declares a numeric apiVersion (oh-uni-package.json).");
|
|
1001
|
+
}
|
|
910
1002
|
|
|
911
1003
|
// src/cmdTools/install.ts
|
|
912
1004
|
import * as fs11 from "fs";
|
|
@@ -2194,9 +2286,13 @@ function moduleOfHap(hapFileName) {
|
|
|
2194
2286
|
const dash = hapFileName.indexOf("-");
|
|
2195
2287
|
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
2196
2288
|
}
|
|
2289
|
+
function isAppProjectRoot(dir) {
|
|
2290
|
+
return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
|
|
2291
|
+
}
|
|
2197
2292
|
async function discoverHaps(opts) {
|
|
2198
2293
|
const { projectDir } = opts;
|
|
2199
2294
|
const maxDepth = opts.maxDepth ?? 8;
|
|
2295
|
+
const skipNestedProjects = isAppProjectRoot(projectDir);
|
|
2200
2296
|
const result = {};
|
|
2201
2297
|
const walk = async (dir, depth) => {
|
|
2202
2298
|
if (depth > maxDepth) return;
|
|
@@ -2205,6 +2301,7 @@ async function discoverHaps(opts) {
|
|
|
2205
2301
|
const full = path18.join(dir, e.name);
|
|
2206
2302
|
if (e.isDirectory()) {
|
|
2207
2303
|
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2304
|
+
if (skipNestedProjects && isAppProjectRoot(full)) continue;
|
|
2208
2305
|
await walk(full, depth + 1);
|
|
2209
2306
|
} else if (e.isFile() && e.name.endsWith(".hap")) {
|
|
2210
2307
|
const moduleName = moduleOfHap(e.name);
|
|
@@ -3396,6 +3493,54 @@ async function resolveProcessId(config, bundle, deviceSerial) {
|
|
|
3396
3493
|
const proc = await findRunningProcess({ config, bundle, deviceSerial });
|
|
3397
3494
|
return proc?.pid;
|
|
3398
3495
|
}
|
|
3496
|
+
async function readDeviceClock(config, deviceSerial) {
|
|
3497
|
+
try {
|
|
3498
|
+
const res = await shell({ config, command: "date +%Y-%m-%dT%H:%M:%S", deviceSerial, timeoutMs: 1e4 });
|
|
3499
|
+
const m = res.code === 0 ? /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/.exec(res.stdout) : null;
|
|
3500
|
+
if (!m) return null;
|
|
3501
|
+
const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
|
|
3502
|
+
return Date.UTC(year, month - 1, day, hour, minute, second);
|
|
3503
|
+
} catch {
|
|
3504
|
+
return null;
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
function hilogTimeMs(time, referenceMs) {
|
|
3508
|
+
const m = /^(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})$/.exec(time);
|
|
3509
|
+
if (!m) return null;
|
|
3510
|
+
const [month, day, hour, minute, second, ms] = m.slice(1).map(Number);
|
|
3511
|
+
const year = new Date(referenceMs).getUTCFullYear();
|
|
3512
|
+
let best = null;
|
|
3513
|
+
for (const y of [year - 1, year, year + 1]) {
|
|
3514
|
+
const t = Date.UTC(y, month - 1, day, hour, minute, second, ms);
|
|
3515
|
+
if (best === null || Math.abs(t - referenceMs) < Math.abs(best - referenceMs)) best = t;
|
|
3516
|
+
}
|
|
3517
|
+
return best;
|
|
3518
|
+
}
|
|
3519
|
+
async function recentEntryFilter(opts) {
|
|
3520
|
+
const passAll = { test: () => true, afterReconnect: async () => {
|
|
3521
|
+
} };
|
|
3522
|
+
const lookbackMs = opts.lookbackMs ?? 0;
|
|
3523
|
+
if (lookbackMs === Infinity) return passAll;
|
|
3524
|
+
const startedAt = Date.now();
|
|
3525
|
+
const now = await readDeviceClock(opts.config, opts.deviceSerial);
|
|
3526
|
+
if (now === null) {
|
|
3527
|
+
opts.logger.warn("Could not read the device clock, so lines already in the log buffer can match too.");
|
|
3528
|
+
return passAll;
|
|
3529
|
+
}
|
|
3530
|
+
let since = now - (Date.now() - startedAt) - lookbackMs;
|
|
3531
|
+
return {
|
|
3532
|
+
test: (entry) => {
|
|
3533
|
+
if (since === null) return true;
|
|
3534
|
+
const t = hilogTimeMs(entry.time, now);
|
|
3535
|
+
return t === null || t >= since;
|
|
3536
|
+
},
|
|
3537
|
+
afterReconnect: async () => {
|
|
3538
|
+
if (since === null) return;
|
|
3539
|
+
const later = await readDeviceClock(opts.config, opts.deviceSerial);
|
|
3540
|
+
if (later !== null && later < since) since = null;
|
|
3541
|
+
}
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3399
3544
|
function waitForLog(opts) {
|
|
3400
3545
|
return new Promise((resolve7, reject) => {
|
|
3401
3546
|
const deadline = Date.now() + opts.timeoutMs;
|
|
@@ -3403,6 +3548,8 @@ function waitForLog(opts) {
|
|
|
3403
3548
|
let child = null;
|
|
3404
3549
|
let reconnectTimer;
|
|
3405
3550
|
let buf = "";
|
|
3551
|
+
let recent = { test: () => true, afterReconnect: async () => {
|
|
3552
|
+
} };
|
|
3406
3553
|
const cleanup = () => {
|
|
3407
3554
|
clearTimeout(timer);
|
|
3408
3555
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
@@ -3443,7 +3590,7 @@ function waitForLog(opts) {
|
|
|
3443
3590
|
buf = lines.pop() ?? "";
|
|
3444
3591
|
for (const line of lines) {
|
|
3445
3592
|
const entry = parseHilogLine(line);
|
|
3446
|
-
if (entry && opts.pattern.test(entryText(entry))) {
|
|
3593
|
+
if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) {
|
|
3447
3594
|
finish(entry);
|
|
3448
3595
|
return;
|
|
3449
3596
|
}
|
|
@@ -3453,13 +3600,21 @@ function waitForLog(opts) {
|
|
|
3453
3600
|
if (settled || child !== stream) return;
|
|
3454
3601
|
child = null;
|
|
3455
3602
|
if (Date.now() < deadline) {
|
|
3456
|
-
reconnectTimer = setTimeout(() => void
|
|
3603
|
+
reconnectTimer = setTimeout(() => void recent.afterReconnect().then(spawnStream).catch(failWith), 1e3);
|
|
3457
3604
|
}
|
|
3458
3605
|
};
|
|
3459
3606
|
stream.once("close", onEnd);
|
|
3460
3607
|
stream.once("error", onEnd);
|
|
3461
3608
|
};
|
|
3462
|
-
void
|
|
3609
|
+
void (async () => {
|
|
3610
|
+
recent = await recentEntryFilter({
|
|
3611
|
+
config: opts.config,
|
|
3612
|
+
deviceSerial: opts.deviceSerial,
|
|
3613
|
+
lookbackMs: opts.lookbackMs,
|
|
3614
|
+
logger: opts.logger ?? noopLogger
|
|
3615
|
+
});
|
|
3616
|
+
await spawnStream();
|
|
3617
|
+
})().catch(failWith);
|
|
3463
3618
|
});
|
|
3464
3619
|
}
|
|
3465
3620
|
function watchLog(opts) {
|
|
@@ -3468,6 +3623,8 @@ function watchLog(opts) {
|
|
|
3468
3623
|
let settled = false;
|
|
3469
3624
|
let child = null;
|
|
3470
3625
|
let buf = "";
|
|
3626
|
+
let recent = { test: () => true, afterReconnect: async () => {
|
|
3627
|
+
} };
|
|
3471
3628
|
const cleanup = () => {
|
|
3472
3629
|
clearTimeout(timer);
|
|
3473
3630
|
opts.abortSignal?.removeEventListener("abort", onAbort);
|
|
@@ -3491,6 +3648,13 @@ function watchLog(opts) {
|
|
|
3491
3648
|
const timer = setTimeout(done, opts.durationMs);
|
|
3492
3649
|
opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
3493
3650
|
void (async () => {
|
|
3651
|
+
recent = await recentEntryFilter({
|
|
3652
|
+
config: opts.config,
|
|
3653
|
+
deviceSerial: opts.deviceSerial,
|
|
3654
|
+
lookbackMs: opts.lookbackMs,
|
|
3655
|
+
logger: opts.logger ?? noopLogger
|
|
3656
|
+
});
|
|
3657
|
+
if (settled) return;
|
|
3494
3658
|
const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
|
|
3495
3659
|
if (settled) return;
|
|
3496
3660
|
const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
|
|
@@ -3501,7 +3665,7 @@ function watchLog(opts) {
|
|
|
3501
3665
|
buf = lines.pop() ?? "";
|
|
3502
3666
|
for (const line of lines) {
|
|
3503
3667
|
const entry = parseHilogLine(line);
|
|
3504
|
-
if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
|
|
3668
|
+
if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) matches.push(entry);
|
|
3505
3669
|
}
|
|
3506
3670
|
});
|
|
3507
3671
|
})().catch((err) => {
|
|
@@ -3786,6 +3950,8 @@ async function ensureOhModules(opts) {
|
|
|
3786
3950
|
}
|
|
3787
3951
|
|
|
3788
3952
|
// src/build/codelinter.ts
|
|
3953
|
+
import * as fs26 from "fs";
|
|
3954
|
+
import * as path26 from "path";
|
|
3789
3955
|
var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
|
|
3790
3956
|
var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
|
|
3791
3957
|
var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
|
|
@@ -3872,6 +4038,16 @@ function parseCodelinterReport(output) {
|
|
|
3872
4038
|
function parseCodelinterFindings(output) {
|
|
3873
4039
|
return parseCodelinterReport(output).findings;
|
|
3874
4040
|
}
|
|
4041
|
+
var PROJECT_ROOT_FILES = [
|
|
4042
|
+
["build-profile.json5"],
|
|
4043
|
+
["hvigorfile.ts", "hvigorfile.js"],
|
|
4044
|
+
[path26.join("hvigor", "hvigor-config.json5")]
|
|
4045
|
+
];
|
|
4046
|
+
function missingProjectRootFiles(projectDir) {
|
|
4047
|
+
return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs26.existsSync(path26.join(projectDir, f)))).map(
|
|
4048
|
+
(names) => names[0]
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
3875
4051
|
async function runCodelinter(opts) {
|
|
3876
4052
|
const res = await runProcess({
|
|
3877
4053
|
command: getCmdToolsBin(opts.config, "codelinter"),
|
|
@@ -3889,7 +4065,7 @@ async function runCodelinter(opts) {
|
|
|
3889
4065
|
var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
|
|
3890
4066
|
function diagnoseBuildFailure(output) {
|
|
3891
4067
|
if (/is not exported from Kit|has no exported member .*@systemapi/i.test(output)) {
|
|
3892
|
-
return
|
|
4068
|
+
return `The project calls system APIs (\`@systemapi\`), which the public SDK leaves out. Build against the full OpenHarmony SDK: download an \`ohos-sdk-full\` archive from ${OHOS_DAILY_BUILDS_URL} and install it with \`oniro-app sdk install --from-archive <archive> --force\` (this replaces the public SDK of that API level; set ONIRO_SDK_ROOT_DIR to keep the two apart). Installing such an app also needs privileged signing (\`oniro-app sign --apl system_core --acls \u2026 --app-privilege-capabilities \u2026\`).`;
|
|
3893
4069
|
}
|
|
3894
4070
|
if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
|
|
3895
4071
|
return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
|
|
@@ -3961,21 +4137,21 @@ function isValidBundleName(bundleName) {
|
|
|
3961
4137
|
}
|
|
3962
4138
|
|
|
3963
4139
|
// src/project/templates.ts
|
|
3964
|
-
import * as
|
|
3965
|
-
import * as
|
|
4140
|
+
import * as fs27 from "fs";
|
|
4141
|
+
import * as path27 from "path";
|
|
3966
4142
|
function toHumanTemplateName(folderName) {
|
|
3967
4143
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
3968
4144
|
}
|
|
3969
4145
|
function listTemplates(templateRoot) {
|
|
3970
|
-
if (!
|
|
3971
|
-
const entries =
|
|
4146
|
+
if (!fs27.existsSync(templateRoot)) return [];
|
|
4147
|
+
const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
|
|
3972
4148
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
3973
|
-
const dir =
|
|
3974
|
-
const metaPath =
|
|
4149
|
+
const dir = path27.join(templateRoot, e.name);
|
|
4150
|
+
const metaPath = path27.join(dir, "template.json");
|
|
3975
4151
|
let meta = {};
|
|
3976
|
-
if (
|
|
4152
|
+
if (fs27.existsSync(metaPath)) {
|
|
3977
4153
|
try {
|
|
3978
|
-
meta = JSON.parse(
|
|
4154
|
+
meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
|
|
3979
4155
|
} catch {
|
|
3980
4156
|
}
|
|
3981
4157
|
}
|
|
@@ -3990,57 +4166,57 @@ function listTemplates(templateRoot) {
|
|
|
3990
4166
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
3991
4167
|
const required = [
|
|
3992
4168
|
"build-profile.json5",
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
4169
|
+
path27.join("AppScope", "app.json5"),
|
|
4170
|
+
path27.join(defaultModuleName, "src", "main", "module.json5"),
|
|
4171
|
+
path27.join(defaultModuleName, "oh-package.json5"),
|
|
3996
4172
|
"hvigorfile.ts"
|
|
3997
4173
|
];
|
|
3998
|
-
return required.filter((rel) => !
|
|
4174
|
+
return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
|
|
3999
4175
|
}
|
|
4000
4176
|
|
|
4001
4177
|
// src/project/createScaffold.ts
|
|
4002
|
-
import * as
|
|
4003
|
-
import * as
|
|
4178
|
+
import * as fs29 from "fs";
|
|
4179
|
+
import * as path28 from "path";
|
|
4004
4180
|
|
|
4005
4181
|
// src/project/jsonHelpers.ts
|
|
4006
|
-
import * as
|
|
4182
|
+
import * as fs28 from "fs";
|
|
4007
4183
|
import JSON55 from "json5";
|
|
4008
4184
|
function readJson5File(filePath) {
|
|
4009
|
-
return JSON55.parse(
|
|
4185
|
+
return JSON55.parse(fs28.readFileSync(filePath, "utf8"));
|
|
4010
4186
|
}
|
|
4011
4187
|
function writeJson5File(filePath, value) {
|
|
4012
|
-
|
|
4188
|
+
fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4013
4189
|
}
|
|
4014
4190
|
function readJsonFile(filePath) {
|
|
4015
|
-
return JSON.parse(
|
|
4191
|
+
return JSON.parse(fs28.readFileSync(filePath, "utf8"));
|
|
4016
4192
|
}
|
|
4017
4193
|
function writeJsonFile(filePath, value) {
|
|
4018
|
-
|
|
4194
|
+
fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4019
4195
|
}
|
|
4020
4196
|
|
|
4021
4197
|
// src/project/createScaffold.ts
|
|
4022
4198
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
4023
4199
|
async function pathExists(p) {
|
|
4024
4200
|
try {
|
|
4025
|
-
await
|
|
4201
|
+
await fs29.promises.access(p);
|
|
4026
4202
|
return true;
|
|
4027
4203
|
} catch {
|
|
4028
4204
|
return false;
|
|
4029
4205
|
}
|
|
4030
4206
|
}
|
|
4031
4207
|
async function copyDirRecursive(srcDir, destDir) {
|
|
4032
|
-
await
|
|
4033
|
-
const entries = await
|
|
4208
|
+
await fs29.promises.mkdir(destDir, { recursive: true });
|
|
4209
|
+
const entries = await fs29.promises.readdir(srcDir, { withFileTypes: true });
|
|
4034
4210
|
for (const entry of entries) {
|
|
4035
|
-
const src =
|
|
4036
|
-
const dest =
|
|
4211
|
+
const src = path28.join(srcDir, entry.name);
|
|
4212
|
+
const dest = path28.join(destDir, entry.name);
|
|
4037
4213
|
if (entry.isDirectory()) {
|
|
4038
4214
|
await copyDirRecursive(src, dest);
|
|
4039
4215
|
} else if (entry.isSymbolicLink()) {
|
|
4040
4216
|
continue;
|
|
4041
4217
|
} else if (entry.isFile()) {
|
|
4042
4218
|
if (entry.name === "template.json") continue;
|
|
4043
|
-
await
|
|
4219
|
+
await fs29.promises.copyFile(src, dest);
|
|
4044
4220
|
}
|
|
4045
4221
|
}
|
|
4046
4222
|
}
|
|
@@ -4048,9 +4224,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
4048
4224
|
const stack = [projectDir];
|
|
4049
4225
|
while (stack.length > 0) {
|
|
4050
4226
|
const currentDir = stack.pop();
|
|
4051
|
-
const entries = await
|
|
4227
|
+
const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
|
|
4052
4228
|
for (const entry of entries) {
|
|
4053
|
-
const fullPath =
|
|
4229
|
+
const fullPath = path28.join(currentDir, entry.name);
|
|
4054
4230
|
if (entry.isDirectory()) {
|
|
4055
4231
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4056
4232
|
stack.push(fullPath);
|
|
@@ -4072,23 +4248,23 @@ function toJavaPropertiesPath(p) {
|
|
|
4072
4248
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
4073
4249
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
4074
4250
|
`;
|
|
4075
|
-
|
|
4251
|
+
fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
|
|
4076
4252
|
}
|
|
4077
4253
|
function renameIfExists(fromPath, toPath) {
|
|
4078
|
-
if (
|
|
4079
|
-
|
|
4254
|
+
if (fs29.existsSync(fromPath) && fromPath !== toPath) {
|
|
4255
|
+
fs29.renameSync(fromPath, toPath);
|
|
4080
4256
|
}
|
|
4081
4257
|
}
|
|
4082
4258
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
4083
|
-
const appJsonPath =
|
|
4084
|
-
if (
|
|
4259
|
+
const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
|
|
4260
|
+
if (fs29.existsSync(appJsonPath)) {
|
|
4085
4261
|
const appJson = readJson5File(appJsonPath);
|
|
4086
4262
|
appJson.app = appJson.app ?? {};
|
|
4087
4263
|
appJson.app.bundleName = args.bundleName;
|
|
4088
4264
|
writeJson5File(appJsonPath, appJson);
|
|
4089
4265
|
}
|
|
4090
|
-
const stringsPath =
|
|
4091
|
-
if (
|
|
4266
|
+
const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
4267
|
+
if (fs29.existsSync(stringsPath)) {
|
|
4092
4268
|
const strings = readJsonFile(stringsPath);
|
|
4093
4269
|
if (Array.isArray(strings.string)) {
|
|
4094
4270
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -4096,8 +4272,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4096
4272
|
}
|
|
4097
4273
|
writeJsonFile(stringsPath, strings);
|
|
4098
4274
|
}
|
|
4099
|
-
const buildProfilePath =
|
|
4100
|
-
if (
|
|
4275
|
+
const buildProfilePath = path28.join(projectDir, "build-profile.json5");
|
|
4276
|
+
if (fs29.existsSync(buildProfilePath)) {
|
|
4101
4277
|
const buildProfile = readJson5File(buildProfilePath);
|
|
4102
4278
|
buildProfile.app = buildProfile.app ?? {};
|
|
4103
4279
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -4110,23 +4286,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4110
4286
|
}
|
|
4111
4287
|
writeJson5File(buildProfilePath, buildProfile);
|
|
4112
4288
|
}
|
|
4113
|
-
const moduleJsonPath =
|
|
4114
|
-
if (
|
|
4289
|
+
const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
4290
|
+
if (fs29.existsSync(moduleJsonPath)) {
|
|
4115
4291
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
4116
4292
|
moduleJson.module = moduleJson.module ?? {};
|
|
4117
4293
|
moduleJson.module.name = args.moduleName;
|
|
4118
4294
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
4119
4295
|
}
|
|
4120
|
-
const moduleOhPackagePath =
|
|
4121
|
-
if (
|
|
4296
|
+
const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
|
|
4297
|
+
if (fs29.existsSync(moduleOhPackagePath)) {
|
|
4122
4298
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
4123
4299
|
pkg.name = args.moduleName;
|
|
4124
4300
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
4125
4301
|
}
|
|
4126
|
-
const vscodeDir =
|
|
4127
|
-
|
|
4302
|
+
const vscodeDir = path28.join(projectDir, ".vscode");
|
|
4303
|
+
fs29.mkdirSync(vscodeDir, { recursive: true });
|
|
4128
4304
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
4129
|
-
writeJsonFile(
|
|
4305
|
+
writeJsonFile(path28.join(vscodeDir, "settings.json"), {
|
|
4130
4306
|
"oniro.hapPath": hapPath,
|
|
4131
4307
|
"files.associations": { "*.json5": "jsonc" }
|
|
4132
4308
|
});
|
|
@@ -4143,7 +4319,7 @@ async function createScaffold(opts) {
|
|
|
4143
4319
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
4144
4320
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
4145
4321
|
}
|
|
4146
|
-
const templateDir =
|
|
4322
|
+
const templateDir = path28.join(opts.templateRoot, opts.templateId);
|
|
4147
4323
|
if (!await pathExists(templateDir)) {
|
|
4148
4324
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
4149
4325
|
}
|
|
@@ -4178,17 +4354,17 @@ async function createScaffold(opts) {
|
|
|
4178
4354
|
}
|
|
4179
4355
|
}
|
|
4180
4356
|
}
|
|
4181
|
-
const projectDir =
|
|
4357
|
+
const projectDir = path28.join(opts.location, opts.projectName);
|
|
4182
4358
|
if (await pathExists(projectDir)) {
|
|
4183
4359
|
if (!opts.overwrite) {
|
|
4184
4360
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
4185
4361
|
}
|
|
4186
|
-
await
|
|
4362
|
+
await fs29.promises.rm(projectDir, { recursive: true, force: true });
|
|
4187
4363
|
}
|
|
4188
4364
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
4189
4365
|
await copyDirRecursive(templateDir, projectDir);
|
|
4190
4366
|
if (moduleName !== defaultModuleName) {
|
|
4191
|
-
renameIfExists(
|
|
4367
|
+
renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
|
|
4192
4368
|
}
|
|
4193
4369
|
updateTemplateConfigs(
|
|
4194
4370
|
projectDir,
|
|
@@ -4205,11 +4381,11 @@ async function createScaffold(opts) {
|
|
|
4205
4381
|
}
|
|
4206
4382
|
|
|
4207
4383
|
// src/project/listModules.ts
|
|
4208
|
-
import * as
|
|
4209
|
-
import * as
|
|
4384
|
+
import * as fs30 from "fs";
|
|
4385
|
+
import * as path29 from "path";
|
|
4210
4386
|
function listModules(opts) {
|
|
4211
|
-
const buildProfile =
|
|
4212
|
-
if (!
|
|
4387
|
+
const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
|
|
4388
|
+
if (!fs30.existsSync(buildProfile)) {
|
|
4213
4389
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
4214
4390
|
}
|
|
4215
4391
|
const parsed = readJson5File(buildProfile);
|
|
@@ -4221,12 +4397,12 @@ function listModules(opts) {
|
|
|
4221
4397
|
}
|
|
4222
4398
|
|
|
4223
4399
|
// src/project/abilities.ts
|
|
4224
|
-
import * as
|
|
4225
|
-
import * as
|
|
4400
|
+
import * as fs31 from "fs";
|
|
4401
|
+
import * as path30 from "path";
|
|
4226
4402
|
function listAbilities(opts) {
|
|
4227
4403
|
const moduleName = opts.moduleName ?? "entry";
|
|
4228
|
-
const moduleJsonPath =
|
|
4229
|
-
if (!
|
|
4404
|
+
const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
4405
|
+
if (!fs31.existsSync(moduleJsonPath)) {
|
|
4230
4406
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
4231
4407
|
}
|
|
4232
4408
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -4234,12 +4410,12 @@ function listAbilities(opts) {
|
|
|
4234
4410
|
}
|
|
4235
4411
|
|
|
4236
4412
|
// src/project/prepareSigning.ts
|
|
4237
|
-
import * as
|
|
4238
|
-
import * as
|
|
4413
|
+
import * as fs32 from "fs";
|
|
4414
|
+
import * as path31 from "path";
|
|
4239
4415
|
function hasSigningArtifacts(dir) {
|
|
4240
|
-
if (!
|
|
4241
|
-
const buildProfile =
|
|
4242
|
-
if (!
|
|
4416
|
+
if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
|
|
4417
|
+
const buildProfile = path31.join(dir, "build-profile.json5");
|
|
4418
|
+
if (!fs32.existsSync(buildProfile)) return false;
|
|
4243
4419
|
try {
|
|
4244
4420
|
const parsed = readJson5File(buildProfile);
|
|
4245
4421
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -4342,8 +4518,25 @@ async function diffHapAssets(opts) {
|
|
|
4342
4518
|
}
|
|
4343
4519
|
|
|
4344
4520
|
// src/install/apply.ts
|
|
4345
|
-
import * as
|
|
4346
|
-
import * as
|
|
4521
|
+
import * as fs33 from "fs";
|
|
4522
|
+
import * as path32 from "path";
|
|
4523
|
+
|
|
4524
|
+
// src/install/hapBundleName.ts
|
|
4525
|
+
function bundleNameFromHapConfig(text) {
|
|
4526
|
+
try {
|
|
4527
|
+
const parsed = JSON.parse(text.replace(/^/, ""));
|
|
4528
|
+
const name = parsed.app?.bundleName;
|
|
4529
|
+
return typeof name === "string" && name ? name : null;
|
|
4530
|
+
} catch {
|
|
4531
|
+
return null;
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
async function readHapBundleName(hapPath) {
|
|
4535
|
+
const config = await readZipEntry(hapPath, (n) => n === "module.json") ?? await readZipEntry(hapPath, (n) => n === "config.json");
|
|
4536
|
+
return config ? bundleNameFromHapConfig(config.toString("utf8")) : null;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
// src/install/apply.ts
|
|
4347
4540
|
function decideInstallMethod(s) {
|
|
4348
4541
|
if (!s.signInfoInconsistent) return "replace";
|
|
4349
4542
|
if (s.isSystemBundle && !s.allowUninstall) return "refuse";
|
|
@@ -4364,7 +4557,7 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
4364
4557
|
async function resolveHap(opts, logger) {
|
|
4365
4558
|
let hap;
|
|
4366
4559
|
if (opts.hapPath) {
|
|
4367
|
-
const hapPath = !
|
|
4560
|
+
const hapPath = !path32.isAbsolute(opts.hapPath) && opts.projectDir ? path32.join(opts.projectDir, opts.hapPath) : opts.hapPath;
|
|
4368
4561
|
hap = existingHapOrUnsigned(hapPath);
|
|
4369
4562
|
if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
4370
4563
|
} else {
|
|
@@ -4373,7 +4566,7 @@ async function resolveHap(opts, logger) {
|
|
|
4373
4566
|
if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
|
|
4374
4567
|
hap = { path: picked.path, fallback: !picked.signed };
|
|
4375
4568
|
}
|
|
4376
|
-
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${
|
|
4569
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path32.basename(hap.path)}.`);
|
|
4377
4570
|
return hap;
|
|
4378
4571
|
}
|
|
4379
4572
|
async function applyChanges(opts) {
|
|
@@ -4383,9 +4576,18 @@ async function applyChanges(opts) {
|
|
|
4383
4576
|
const allowUninstall = opts.allowUninstall ?? false;
|
|
4384
4577
|
const hap = await resolveHap(opts, logger);
|
|
4385
4578
|
const hapPath = hap.path;
|
|
4579
|
+
const hapBundle = await readHapBundleName(hapPath).catch((err) => {
|
|
4580
|
+
logger.debug(`bundle name check skipped: ${err.message}`);
|
|
4581
|
+
return null;
|
|
4582
|
+
});
|
|
4583
|
+
if (hapBundle && hapBundle !== bundle) {
|
|
4584
|
+
throw new OniroError(
|
|
4585
|
+
`${path32.basename(hapPath)} (${hapPath}) is bundle ${hapBundle}, not ${bundle}, so installing it would not update ${bundle}. Choose the HAP explicitly, or name ${hapBundle} as the bundle if it is the app to update.`
|
|
4586
|
+
);
|
|
4587
|
+
}
|
|
4386
4588
|
logger.info(`installing ${hapPath}`);
|
|
4387
4589
|
let assetsChanged = false;
|
|
4388
|
-
if (opts.installedHapPath &&
|
|
4590
|
+
if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
|
|
4389
4591
|
try {
|
|
4390
4592
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
4391
4593
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -4418,7 +4620,7 @@ async function applyChanges(opts) {
|
|
|
4418
4620
|
}
|
|
4419
4621
|
if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
|
|
4420
4622
|
throw new OniroError(
|
|
4421
|
-
`hdc reported ${
|
|
4623
|
+
`hdc reported ${path32.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
|
|
4422
4624
|
);
|
|
4423
4625
|
}
|
|
4424
4626
|
let postInstallPid = await pidOf(config, bundle, deviceSerial);
|
|
@@ -4495,9 +4697,9 @@ var AUTH_PATHS = {
|
|
|
4495
4697
|
};
|
|
4496
4698
|
|
|
4497
4699
|
// src/harmonyos/auth/tokenStore.ts
|
|
4498
|
-
import * as
|
|
4700
|
+
import * as fs34 from "fs";
|
|
4499
4701
|
import * as os11 from "os";
|
|
4500
|
-
import * as
|
|
4702
|
+
import * as path33 from "path";
|
|
4501
4703
|
import * as crypto3 from "crypto";
|
|
4502
4704
|
var ALGORITHM = "aes-256-gcm";
|
|
4503
4705
|
var KEY_LENGTH = 32;
|
|
@@ -4533,7 +4735,7 @@ function permissionHint(dir) {
|
|
|
4533
4735
|
}
|
|
4534
4736
|
function ensureDir(dir) {
|
|
4535
4737
|
try {
|
|
4536
|
-
|
|
4738
|
+
fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
4537
4739
|
} catch (err) {
|
|
4538
4740
|
if (err.code === "EACCES") {
|
|
4539
4741
|
throw new OniroError(permissionHint(dir), err);
|
|
@@ -4544,17 +4746,17 @@ function ensureDir(dir) {
|
|
|
4544
4746
|
function createTokenStore(opts) {
|
|
4545
4747
|
const logger = opts.logger ?? noopLogger;
|
|
4546
4748
|
const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
|
|
4547
|
-
const tokenPath =
|
|
4548
|
-
const keyPath =
|
|
4749
|
+
const tokenPath = path33.join(authDir, TOKEN_FILE);
|
|
4750
|
+
const keyPath = path33.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
|
|
4549
4751
|
function loadOrCreateKey() {
|
|
4550
|
-
if (
|
|
4551
|
-
const existing =
|
|
4752
|
+
if (fs34.existsSync(keyPath)) {
|
|
4753
|
+
const existing = fs34.readFileSync(keyPath);
|
|
4552
4754
|
if (existing.length === KEY_LENGTH) return existing;
|
|
4553
4755
|
logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
|
|
4554
4756
|
}
|
|
4555
|
-
ensureDir(
|
|
4757
|
+
ensureDir(path33.dirname(keyPath));
|
|
4556
4758
|
const key = crypto3.randomBytes(KEY_LENGTH);
|
|
4557
|
-
|
|
4759
|
+
fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
|
|
4558
4760
|
return key;
|
|
4559
4761
|
}
|
|
4560
4762
|
return {
|
|
@@ -4564,12 +4766,12 @@ function createTokenStore(opts) {
|
|
|
4564
4766
|
const payload = Buffer.from(JSON.stringify(session), "utf8");
|
|
4565
4767
|
const blob = encrypt2(loadOrCreateKey(), payload);
|
|
4566
4768
|
ensureDir(authDir);
|
|
4567
|
-
|
|
4769
|
+
fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
|
|
4568
4770
|
},
|
|
4569
4771
|
load() {
|
|
4570
|
-
if (!
|
|
4772
|
+
if (!fs34.existsSync(tokenPath)) return null;
|
|
4571
4773
|
try {
|
|
4572
|
-
const blob = JSON.parse(
|
|
4774
|
+
const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
|
|
4573
4775
|
if (!isEncryptedBlob(blob)) return null;
|
|
4574
4776
|
const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
|
|
4575
4777
|
return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
|
|
@@ -4581,7 +4783,7 @@ function createTokenStore(opts) {
|
|
|
4581
4783
|
}
|
|
4582
4784
|
logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
|
|
4583
4785
|
try {
|
|
4584
|
-
|
|
4786
|
+
fs34.rmSync(tokenPath, { force: true });
|
|
4585
4787
|
} catch {
|
|
4586
4788
|
}
|
|
4587
4789
|
return null;
|
|
@@ -4589,7 +4791,7 @@ function createTokenStore(opts) {
|
|
|
4589
4791
|
},
|
|
4590
4792
|
clear() {
|
|
4591
4793
|
try {
|
|
4592
|
-
|
|
4794
|
+
fs34.rmSync(tokenPath, { force: true });
|
|
4593
4795
|
} catch (err) {
|
|
4594
4796
|
throw new OniroError(`Failed to remove ${tokenPath}.`, err);
|
|
4595
4797
|
}
|
|
@@ -5070,7 +5272,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
|
|
|
5070
5272
|
}
|
|
5071
5273
|
|
|
5072
5274
|
// src/harmonyos/signing/profile.ts
|
|
5073
|
-
import * as
|
|
5275
|
+
import * as fs35 from "fs";
|
|
5074
5276
|
import { X509Certificate } from "crypto";
|
|
5075
5277
|
import forge from "node-forge";
|
|
5076
5278
|
function extractProfileJson(buffer) {
|
|
@@ -5102,7 +5304,7 @@ function extractProfileJson(buffer) {
|
|
|
5102
5304
|
function readProvisionProfile(profilePath) {
|
|
5103
5305
|
let raw;
|
|
5104
5306
|
try {
|
|
5105
|
-
const json = extractProfileJson(
|
|
5307
|
+
const json = extractProfileJson(fs35.readFileSync(profilePath));
|
|
5106
5308
|
if (!json) return null;
|
|
5107
5309
|
raw = JSON.parse(json);
|
|
5108
5310
|
} catch {
|
|
@@ -5131,7 +5333,7 @@ function parseCertificate(pem) {
|
|
|
5131
5333
|
}
|
|
5132
5334
|
function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
5133
5335
|
try {
|
|
5134
|
-
const der = forge.util.createBuffer(
|
|
5336
|
+
const der = forge.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
|
|
5135
5337
|
const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
|
|
5136
5338
|
const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
|
|
5137
5339
|
const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
|
|
@@ -5147,7 +5349,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
|
5147
5349
|
}
|
|
5148
5350
|
}
|
|
5149
5351
|
function verifySigningMaterial(opts) {
|
|
5150
|
-
const pems =
|
|
5352
|
+
const pems = fs35.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
5151
5353
|
const chain = (pems ?? []).map(parseCertificate);
|
|
5152
5354
|
const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
|
|
5153
5355
|
if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
|
|
@@ -5166,14 +5368,14 @@ function verifySigningMaterial(opts) {
|
|
|
5166
5368
|
|
|
5167
5369
|
// src/harmonyos/signing/autoSign.ts
|
|
5168
5370
|
import * as crypto9 from "crypto";
|
|
5169
|
-
import * as
|
|
5170
|
-
import * as
|
|
5371
|
+
import * as fs40 from "fs";
|
|
5372
|
+
import * as path39 from "path";
|
|
5171
5373
|
import JSON57 from "json5";
|
|
5172
5374
|
|
|
5173
5375
|
// src/harmonyos/signing/agc.ts
|
|
5174
5376
|
import * as crypto6 from "crypto";
|
|
5175
|
-
import * as
|
|
5176
|
-
import * as
|
|
5377
|
+
import * as fs36 from "fs";
|
|
5378
|
+
import * as path34 from "path";
|
|
5177
5379
|
var ENDPOINTS = {
|
|
5178
5380
|
CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
|
|
5179
5381
|
CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
|
|
@@ -5259,8 +5461,8 @@ function createAgcClient(http3, auth) {
|
|
|
5259
5461
|
if (target.sha256 && actual !== target.sha256.toLowerCase()) {
|
|
5260
5462
|
throw new ChecksumMismatchError(target.sha256, actual);
|
|
5261
5463
|
}
|
|
5262
|
-
|
|
5263
|
-
|
|
5464
|
+
fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
|
|
5465
|
+
fs36.writeFileSync(dest, buffer, { mode: 384 });
|
|
5264
5466
|
}
|
|
5265
5467
|
};
|
|
5266
5468
|
}
|
|
@@ -5311,22 +5513,22 @@ async function registerDevices(agc, connected, logger) {
|
|
|
5311
5513
|
|
|
5312
5514
|
// src/harmonyos/signing/keystore.ts
|
|
5313
5515
|
import * as crypto7 from "crypto";
|
|
5314
|
-
import * as
|
|
5315
|
-
import * as
|
|
5516
|
+
import * as fs38 from "fs";
|
|
5517
|
+
import * as path36 from "path";
|
|
5316
5518
|
|
|
5317
5519
|
// src/harmonyos/java.ts
|
|
5318
|
-
import * as
|
|
5319
|
-
import * as
|
|
5520
|
+
import * as fs37 from "fs";
|
|
5521
|
+
import * as path35 from "path";
|
|
5320
5522
|
var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
|
|
5321
5523
|
function findJava(sdk, env = process.env) {
|
|
5322
5524
|
const candidates = [
|
|
5323
|
-
env.JAVA_HOME &&
|
|
5324
|
-
sdk &&
|
|
5525
|
+
env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
|
|
5526
|
+
sdk && path35.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
|
|
5325
5527
|
// macOS: the JBR is itself a bundle inside Studio's Contents.
|
|
5326
|
-
sdk &&
|
|
5327
|
-
...(env.PATH ?? "").split(
|
|
5528
|
+
sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
|
|
5529
|
+
...(env.PATH ?? "").split(path35.delimiter).map((dir) => dir && path35.join(dir, JAVA_BIN))
|
|
5328
5530
|
];
|
|
5329
|
-
const found = candidates.find((c) => !!c &&
|
|
5531
|
+
const found = candidates.find((c) => !!c && fs37.existsSync(c));
|
|
5330
5532
|
if (found) return found;
|
|
5331
5533
|
throw new OniroError(
|
|
5332
5534
|
"No Java runtime found. HarmonyOS signing runs the SDK's hap-sign-tool.jar, which needs one: install a JDK and set JAVA_HOME, or install DevEco Studio, whose bundled runtime is used automatically."
|
|
@@ -5340,13 +5542,13 @@ function redactArgs(args) {
|
|
|
5340
5542
|
return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
|
|
5341
5543
|
}
|
|
5342
5544
|
async function generateKeystoreAndCsr(opts) {
|
|
5343
|
-
const jar =
|
|
5344
|
-
if (!
|
|
5545
|
+
const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
|
|
5546
|
+
if (!fs38.existsSync(jar)) {
|
|
5345
5547
|
throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
|
|
5346
5548
|
}
|
|
5347
5549
|
const java = findJava(opts.sdk);
|
|
5348
5550
|
const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
|
|
5349
|
-
|
|
5551
|
+
fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
|
|
5350
5552
|
const run = async (what, args) => {
|
|
5351
5553
|
const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
|
|
5352
5554
|
opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
|
|
@@ -5360,28 +5562,28 @@ ${detail}` : ""}`);
|
|
|
5360
5562
|
opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
|
|
5361
5563
|
await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
|
|
5362
5564
|
await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
|
|
5363
|
-
return { keyPwd, csr:
|
|
5565
|
+
return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
|
|
5364
5566
|
}
|
|
5365
5567
|
|
|
5366
5568
|
// src/harmonyos/signing/paths.ts
|
|
5367
5569
|
import * as crypto8 from "crypto";
|
|
5368
|
-
import * as
|
|
5570
|
+
import * as path37 from "path";
|
|
5369
5571
|
function sanitizeNamePart(raw) {
|
|
5370
5572
|
const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
|
|
5371
5573
|
return safe.slice(0, 64) || "default";
|
|
5372
5574
|
}
|
|
5373
5575
|
function resolveSigningMaterialPaths(config, productName, projectRoot) {
|
|
5374
5576
|
const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
|
|
5375
|
-
const base =
|
|
5577
|
+
const base = path37.join(
|
|
5376
5578
|
config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
|
|
5377
|
-
`${sanitizeNamePart(productName)}_${
|
|
5579
|
+
`${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
|
|
5378
5580
|
);
|
|
5379
5581
|
return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
|
|
5380
5582
|
}
|
|
5381
5583
|
|
|
5382
5584
|
// src/harmonyos/signing/project.ts
|
|
5383
|
-
import * as
|
|
5384
|
-
import * as
|
|
5585
|
+
import * as fs39 from "fs";
|
|
5586
|
+
import * as path38 from "path";
|
|
5385
5587
|
import JSON56 from "json5";
|
|
5386
5588
|
|
|
5387
5589
|
// src/harmonyos/signing/aclPermissionList.ts
|
|
@@ -5449,9 +5651,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
|
|
|
5449
5651
|
|
|
5450
5652
|
// src/harmonyos/signing/project.ts
|
|
5451
5653
|
function readRequestedPermissions(moduleConfigPath) {
|
|
5452
|
-
if (!
|
|
5654
|
+
if (!fs39.existsSync(moduleConfigPath)) return [];
|
|
5453
5655
|
try {
|
|
5454
|
-
const parsed = JSON56.parse(
|
|
5656
|
+
const parsed = JSON56.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
|
|
5455
5657
|
return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
5456
5658
|
} catch {
|
|
5457
5659
|
return [];
|
|
@@ -5460,10 +5662,10 @@ function readRequestedPermissions(moduleConfigPath) {
|
|
|
5460
5662
|
function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
5461
5663
|
const requested = /* @__PURE__ */ new Set();
|
|
5462
5664
|
for (const module of listModules({ projectDir })) {
|
|
5463
|
-
const moduleRoot =
|
|
5665
|
+
const moduleRoot = path38.resolve(projectDir, module.srcPath);
|
|
5464
5666
|
for (const sourceSet of ["main", "ohosTest"]) {
|
|
5465
5667
|
for (const name of readRequestedPermissions(
|
|
5466
|
-
|
|
5668
|
+
path38.join(moduleRoot, "src", sourceSet, "module.json5")
|
|
5467
5669
|
)) {
|
|
5468
5670
|
requested.add(name);
|
|
5469
5671
|
}
|
|
@@ -5479,7 +5681,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
|
5479
5681
|
// src/harmonyos/signing/autoSign.ts
|
|
5480
5682
|
function shouldRegenerate(input) {
|
|
5481
5683
|
if (input.force) return "--force was passed";
|
|
5482
|
-
const missing = Object.values(input.paths).find((file) => !
|
|
5684
|
+
const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
|
|
5483
5685
|
if (missing) return `${missing} is missing`;
|
|
5484
5686
|
const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
|
|
5485
5687
|
if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
|
|
@@ -5518,16 +5720,16 @@ function writeSigningConfig(opts) {
|
|
|
5518
5720
|
for (const product of app.products ?? []) {
|
|
5519
5721
|
if (product.name === opts.productName) product.signingConfig = opts.productName;
|
|
5520
5722
|
}
|
|
5521
|
-
|
|
5723
|
+
fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
|
|
5522
5724
|
}
|
|
5523
5725
|
async function harmonyOsAutoSign(opts) {
|
|
5524
5726
|
const logger = opts.logger ?? noopLogger;
|
|
5525
5727
|
const productName = opts.productName ?? "default";
|
|
5526
|
-
const projectDir =
|
|
5527
|
-
const buildProfilePath =
|
|
5728
|
+
const projectDir = path39.resolve(opts.projectDir);
|
|
5729
|
+
const buildProfilePath = path39.join(projectDir, "build-profile.json5");
|
|
5528
5730
|
let buildProfile;
|
|
5529
5731
|
try {
|
|
5530
|
-
buildProfile = JSON57.parse(
|
|
5732
|
+
buildProfile = JSON57.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
|
|
5531
5733
|
} catch (err) {
|
|
5532
5734
|
throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
|
|
5533
5735
|
}
|
|
@@ -5567,7 +5769,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5567
5769
|
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
5568
5770
|
const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
|
|
5569
5771
|
const deviceIds = await registerDevices(agc, connected, logger);
|
|
5570
|
-
for (const file of Object.values(paths))
|
|
5772
|
+
for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
|
|
5571
5773
|
const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
|
|
5572
5774
|
const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
|
|
5573
5775
|
if (stale) {
|
|
@@ -5598,10 +5800,10 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5598
5800
|
try {
|
|
5599
5801
|
verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
|
|
5600
5802
|
} catch (err) {
|
|
5601
|
-
|
|
5803
|
+
fs40.rmSync(paths.profilePath, { force: true });
|
|
5602
5804
|
throw err;
|
|
5603
5805
|
}
|
|
5604
|
-
const materialDir =
|
|
5806
|
+
const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
|
|
5605
5807
|
try {
|
|
5606
5808
|
getKey(materialDir);
|
|
5607
5809
|
} catch {
|
|
@@ -5613,7 +5815,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5613
5815
|
}
|
|
5614
5816
|
|
|
5615
5817
|
// src/index.ts
|
|
5616
|
-
var VERSION = "0.
|
|
5818
|
+
var VERSION = "0.15.0";
|
|
5617
5819
|
export {
|
|
5618
5820
|
ALL_SDKS,
|
|
5619
5821
|
APL_VALUES,
|
|
@@ -5627,6 +5829,7 @@ export {
|
|
|
5627
5829
|
HUAWEI_SITES,
|
|
5628
5830
|
INSTALL_TMP_DIRNAME,
|
|
5629
5831
|
InsufficientSpaceError,
|
|
5832
|
+
OHOS_DAILY_BUILDS_URL,
|
|
5630
5833
|
OHOS_URL_BASE,
|
|
5631
5834
|
OniroError,
|
|
5632
5835
|
SIGNING_ERRORS,
|
|
@@ -5647,6 +5850,7 @@ export {
|
|
|
5647
5850
|
buildHvigorwArgs,
|
|
5648
5851
|
buildInputCommand,
|
|
5649
5852
|
buildRawTouchCommand,
|
|
5853
|
+
bundleNameFromHapConfig,
|
|
5650
5854
|
captureBurst,
|
|
5651
5855
|
captureLog,
|
|
5652
5856
|
cleanCodelinterOutput,
|
|
@@ -5700,6 +5904,7 @@ export {
|
|
|
5700
5904
|
findLayoutNodes,
|
|
5701
5905
|
findMissingProjectSdk,
|
|
5702
5906
|
findRunningProcess,
|
|
5907
|
+
findSdkOsDir,
|
|
5703
5908
|
flattenLayout,
|
|
5704
5909
|
forceStop,
|
|
5705
5910
|
formatBytes,
|
|
@@ -5733,10 +5938,12 @@ export {
|
|
|
5733
5938
|
hdcTargetArgs,
|
|
5734
5939
|
hdcTargetArgsFor,
|
|
5735
5940
|
hideKeyboard,
|
|
5941
|
+
hilogTimeMs,
|
|
5736
5942
|
installApp,
|
|
5737
5943
|
installCmdTools,
|
|
5738
5944
|
installEmulator,
|
|
5739
5945
|
installFailureReason,
|
|
5946
|
+
installSdkFromArchive,
|
|
5740
5947
|
isBundleInstalled,
|
|
5741
5948
|
isCmdToolsInstalled,
|
|
5742
5949
|
isEmulatorInstalled,
|
|
@@ -5753,6 +5960,7 @@ export {
|
|
|
5753
5960
|
listRunningProcesses,
|
|
5754
5961
|
listTemplates,
|
|
5755
5962
|
locateTarget,
|
|
5963
|
+
missingProjectRootFiles,
|
|
5756
5964
|
needsCmdWrapper,
|
|
5757
5965
|
nonInteractivePrompter,
|
|
5758
5966
|
noopLogger,
|
|
@@ -5776,9 +5984,11 @@ export {
|
|
|
5776
5984
|
pickTarget,
|
|
5777
5985
|
prepareSigning,
|
|
5778
5986
|
pruneLayout,
|
|
5987
|
+
readHapBundleName,
|
|
5779
5988
|
readJson5File,
|
|
5780
5989
|
readJsonFile,
|
|
5781
5990
|
readProvisionProfile,
|
|
5991
|
+
readZipEntry,
|
|
5782
5992
|
reboot,
|
|
5783
5993
|
recvFile,
|
|
5784
5994
|
removeCmdTools,
|