@oniroproject/core 0.15.0 → 0.16.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/dist/index.cjs +700 -374
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +110 -2
- package/dist/index.d.ts +110 -2
- package/dist/index.js +692 -373
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -581,14 +581,14 @@ var pipelineAsync = promisify(pipeline);
|
|
|
581
581
|
async function downloadFile(opts) {
|
|
582
582
|
const { url, dest, progress, abortSignal } = opts;
|
|
583
583
|
const proto = url.startsWith("https") ? https : http;
|
|
584
|
-
return new Promise((
|
|
584
|
+
return new Promise((resolve9, reject) => {
|
|
585
585
|
const s = Math.max(0, Math.min(100, opts.start ?? 0));
|
|
586
586
|
const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
|
|
587
587
|
let settled = false;
|
|
588
588
|
const done = (err) => {
|
|
589
589
|
if (settled) return;
|
|
590
590
|
settled = true;
|
|
591
|
-
err ? reject(err) :
|
|
591
|
+
err ? reject(err) : resolve9();
|
|
592
592
|
};
|
|
593
593
|
if (abortSignal?.aborted) {
|
|
594
594
|
done(new CancelledError("Download cancelled before start."));
|
|
@@ -766,15 +766,26 @@ async function extractZipWithProgress(opts) {
|
|
|
766
766
|
async function extractTarball(tarPath, dest, strip = 0, filter) {
|
|
767
767
|
await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
|
|
768
768
|
}
|
|
769
|
-
|
|
770
|
-
|
|
769
|
+
var SIGNED_HAP_CHUNK_SIZE = 128 * 1024;
|
|
770
|
+
async function withHapZip(zipPath, use) {
|
|
771
|
+
const zip = new StreamZip({ file: zipPath, chunkSize: SIGNED_HAP_CHUNK_SIZE, storeEntries: true });
|
|
772
|
+
const close = () => new Promise((resolve9) => zip.close(() => resolve9()));
|
|
771
773
|
try {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
+
await new Promise((resolve9, reject) => {
|
|
775
|
+
zip.once("ready", () => resolve9());
|
|
776
|
+
zip.once("error", (err) => reject(err));
|
|
777
|
+
});
|
|
778
|
+
return await use({ entries: () => zip.entries(), entryData: (name) => zip.entryDataSync(name) });
|
|
774
779
|
} finally {
|
|
775
|
-
await
|
|
780
|
+
await close();
|
|
776
781
|
}
|
|
777
782
|
}
|
|
783
|
+
function readZipEntry(zipPath, match) {
|
|
784
|
+
return withHapZip(zipPath, (zip) => {
|
|
785
|
+
const entry = Object.values(zip.entries()).find((e) => !e.isDirectory && match(e.name));
|
|
786
|
+
return entry ? zip.entryData(entry.name) : void 0;
|
|
787
|
+
});
|
|
788
|
+
}
|
|
778
789
|
|
|
779
790
|
// src/sdk/list.ts
|
|
780
791
|
import * as fs7 from "fs";
|
|
@@ -1425,9 +1436,16 @@ var SIGNING_KIND_CONFIG = {
|
|
|
1425
1436
|
certBasename: "OpenHarmonyApplication.cer"
|
|
1426
1437
|
}
|
|
1427
1438
|
};
|
|
1428
|
-
function
|
|
1429
|
-
|
|
1430
|
-
|
|
1439
|
+
function sdkSigningFiles(sdkPath) {
|
|
1440
|
+
return {
|
|
1441
|
+
signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1442
|
+
keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1443
|
+
profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1444
|
+
unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
function copySigningFiles(signaturesDir, paths, kind, logger, applicationCertPath) {
|
|
1448
|
+
logger.info(`[sign] Copying signing material into ${signaturesDir}...`);
|
|
1431
1449
|
fs13.mkdirSync(signaturesDir, { recursive: true });
|
|
1432
1450
|
fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
|
|
1433
1451
|
fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
|
|
@@ -1454,10 +1472,7 @@ function extractLeafCertificate(chainPath) {
|
|
|
1454
1472
|
return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
|
|
1455
1473
|
}
|
|
1456
1474
|
function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
|
|
1457
|
-
const kind = options.kind ?? pickSigningKind(apl);
|
|
1458
|
-
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
1459
1475
|
const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
|
|
1460
|
-
const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
|
|
1461
1476
|
if (!fs13.existsSync(appJsonPath)) {
|
|
1462
1477
|
throw new OniroError(`${appJsonPath} does not exist.`);
|
|
1463
1478
|
}
|
|
@@ -1467,26 +1482,28 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1467
1482
|
} catch (e) {
|
|
1468
1483
|
throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
|
|
1469
1484
|
}
|
|
1485
|
+
if (!appJson.app?.bundleName) {
|
|
1486
|
+
throw new OniroError("app.json5 does not contain app.bundleName.");
|
|
1487
|
+
}
|
|
1488
|
+
applyProfileTemplate(path14.join(projectDir, "signatures"), appJson.app.bundleName, apl, appFeature, logger, options);
|
|
1489
|
+
}
|
|
1490
|
+
function applyProfileTemplate(signaturesDir, bundleName, apl, appFeature, logger, options = {}) {
|
|
1491
|
+
const kind = options.kind ?? pickSigningKind(apl);
|
|
1492
|
+
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
1493
|
+
const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1470
1494
|
let profileTemplate;
|
|
1471
1495
|
try {
|
|
1472
1496
|
profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
|
|
1473
1497
|
} catch (e) {
|
|
1474
1498
|
throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
|
|
1475
1499
|
}
|
|
1476
|
-
if (!appJson.app?.bundleName) {
|
|
1477
|
-
throw new OniroError("app.json5 does not contain app.bundleName.");
|
|
1478
|
-
}
|
|
1479
1500
|
if (!profileTemplate["bundle-info"]) {
|
|
1480
1501
|
throw new OniroError("UnsgnedReleasedProfileTemplate.json is missing the bundle-info section.");
|
|
1481
1502
|
}
|
|
1482
|
-
profileTemplate["bundle-info"]["bundle-name"] =
|
|
1503
|
+
profileTemplate["bundle-info"]["bundle-name"] = bundleName;
|
|
1483
1504
|
profileTemplate["bundle-info"]["apl"] = apl;
|
|
1484
1505
|
profileTemplate["bundle-info"]["app-feature"] = appFeature;
|
|
1485
|
-
const distCertChainPath = path14.join(
|
|
1486
|
-
projectDir,
|
|
1487
|
-
"signatures",
|
|
1488
|
-
SIGNING_KIND_CONFIG[kind].certBasename
|
|
1489
|
-
);
|
|
1506
|
+
const distCertChainPath = path14.join(signaturesDir, SIGNING_KIND_CONFIG[kind].certBasename);
|
|
1490
1507
|
profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
|
|
1491
1508
|
if (options.acls !== void 0) {
|
|
1492
1509
|
profileTemplate.acls = profileTemplate.acls ?? {};
|
|
@@ -1497,9 +1514,8 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1497
1514
|
}
|
|
1498
1515
|
fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
|
|
1499
1516
|
}
|
|
1500
|
-
function generateP7bFile(
|
|
1517
|
+
function generateP7bFile(signaturesDir, paths, logger, passwords) {
|
|
1501
1518
|
logger.info("[sign] Generating P7b profile via hap-sign-tool...");
|
|
1502
|
-
const signaturesDir = path14.join(projectDir, "signatures");
|
|
1503
1519
|
const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1504
1520
|
const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
|
|
1505
1521
|
const args = [
|
|
@@ -1623,30 +1639,201 @@ function generateSigningConfigs(options) {
|
|
|
1623
1639
|
if (!fs13.existsSync(sdkPath)) {
|
|
1624
1640
|
throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
|
|
1625
1641
|
}
|
|
1626
|
-
const paths =
|
|
1627
|
-
signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1628
|
-
keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1629
|
-
profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1630
|
-
unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1631
|
-
};
|
|
1642
|
+
const paths = sdkSigningFiles(sdkPath);
|
|
1632
1643
|
const kind = pickSigningKind(apl);
|
|
1633
1644
|
const passwords = options.passwords ?? DEFAULT_PASSWORDS;
|
|
1634
1645
|
logger.info("[sign] Starting signing configuration generation...");
|
|
1635
|
-
|
|
1646
|
+
const signaturesDir = path14.join(projectDir, "signatures");
|
|
1647
|
+
copySigningFiles(signaturesDir, paths, kind, logger, options.applicationCertPath);
|
|
1636
1648
|
modifyProfileTemplate(projectDir, apl, appFeature, logger, {
|
|
1637
1649
|
kind,
|
|
1638
1650
|
acls: options.acls,
|
|
1639
1651
|
appPrivilegeCapabilities: options.appPrivilegeCapabilities
|
|
1640
1652
|
});
|
|
1641
|
-
generateP7bFile(
|
|
1653
|
+
generateP7bFile(signaturesDir, paths, logger, passwords);
|
|
1642
1654
|
prepareMaterialDirectory(projectDir, logger);
|
|
1643
1655
|
updateBuildProfile(projectDir, kind, logger, passwords);
|
|
1644
1656
|
logger.info("[sign] Signing configuration generated successfully.");
|
|
1645
1657
|
}
|
|
1646
1658
|
|
|
1647
|
-
// src/
|
|
1659
|
+
// src/sign/hapInfo.ts
|
|
1660
|
+
import * as crypto3 from "crypto";
|
|
1648
1661
|
import * as fs14 from "fs";
|
|
1649
1662
|
import * as path15 from "path";
|
|
1663
|
+
function parseHapModuleJson(text, hapPath) {
|
|
1664
|
+
let parsed;
|
|
1665
|
+
try {
|
|
1666
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
throw new OniroError(`The module.json in ${hapPath} is not valid JSON: ${err.message}`, err);
|
|
1669
|
+
}
|
|
1670
|
+
const bundleName = parsed.app?.bundleName;
|
|
1671
|
+
if (typeof bundleName !== "string" || !bundleName) {
|
|
1672
|
+
throw new OniroError(`The module.json in ${hapPath} names no app.bundleName.`);
|
|
1673
|
+
}
|
|
1674
|
+
const requested = Array.isArray(parsed.module?.requestPermissions) ? parsed.module.requestPermissions : [];
|
|
1675
|
+
const { compileSdkType, targetAPIVersion } = parsed.app ?? {};
|
|
1676
|
+
return {
|
|
1677
|
+
bundleName,
|
|
1678
|
+
requestedPermissions: requested.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0),
|
|
1679
|
+
compileSdkType: typeof compileSdkType === "string" && compileSdkType ? compileSdkType : void 0,
|
|
1680
|
+
targetApiVersion: typeof targetAPIVersion === "number" ? targetAPIVersion : void 0
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
async function readHapSigningInfo(hapPath) {
|
|
1684
|
+
if (!fs14.existsSync(hapPath)) throw new OniroError(`${hapPath} does not exist.`);
|
|
1685
|
+
let moduleJson;
|
|
1686
|
+
try {
|
|
1687
|
+
moduleJson = await readZipEntry(hapPath, (n) => n === "module.json");
|
|
1688
|
+
if (!moduleJson && await readZipEntry(hapPath, (n) => n === "config.json")) {
|
|
1689
|
+
throw new OniroError(
|
|
1690
|
+
`${hapPath} is an FA-model HAP (config.json, no module.json). Only stage-model HAPs can be signed without their project.`
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
} catch (err) {
|
|
1694
|
+
if (err instanceof OniroError) throw err;
|
|
1695
|
+
throw new OniroError(`Could not read ${hapPath} as a HAP: ${err.message}`, err);
|
|
1696
|
+
}
|
|
1697
|
+
if (!moduleJson) throw new OniroError(`${hapPath} holds no module.json, so it is not a HAP.`);
|
|
1698
|
+
return parseHapModuleJson(moduleJson.toString("utf8"), hapPath);
|
|
1699
|
+
}
|
|
1700
|
+
function defaultSignedHapPath(hapPath) {
|
|
1701
|
+
const dir = path15.dirname(hapPath);
|
|
1702
|
+
const name = path15.basename(hapPath);
|
|
1703
|
+
if (/-signed\.hap$/i.test(name)) return path15.join(dir, name.replace(/-signed\.hap$/i, "-resigned.hap"));
|
|
1704
|
+
return path15.join(dir, `${name.replace(/(-unsigned)?\.hap$/i, "")}-signed.hap`);
|
|
1705
|
+
}
|
|
1706
|
+
async function writeHapAtomically(outPath, sign) {
|
|
1707
|
+
const dir = path15.dirname(outPath);
|
|
1708
|
+
try {
|
|
1709
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
1710
|
+
} catch (err) {
|
|
1711
|
+
throw new OniroError(`Cannot create ${dir} for the signed HAP: ${err.message}`, err);
|
|
1712
|
+
}
|
|
1713
|
+
const tmpPath = path15.join(dir, `.${path15.basename(outPath)}.${crypto3.randomBytes(4).toString("hex")}.tmp`);
|
|
1714
|
+
try {
|
|
1715
|
+
await sign(tmpPath);
|
|
1716
|
+
fs14.renameSync(tmpPath, outPath);
|
|
1717
|
+
} finally {
|
|
1718
|
+
fs14.rmSync(tmpPath, { force: true });
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// src/sign/signHap.ts
|
|
1723
|
+
import * as fs15 from "fs";
|
|
1724
|
+
import * as os8 from "os";
|
|
1725
|
+
import * as path16 from "path";
|
|
1726
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1727
|
+
|
|
1728
|
+
// src/project/validators.ts
|
|
1729
|
+
function isValidProjectName(name) {
|
|
1730
|
+
if (!name) return false;
|
|
1731
|
+
if (name.includes("/") || name.includes("\\")) return false;
|
|
1732
|
+
return /^[A-Za-z0-9._-]+$/.test(name);
|
|
1733
|
+
}
|
|
1734
|
+
function isValidBundleName(bundleName) {
|
|
1735
|
+
return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// src/sign/signHap.ts
|
|
1739
|
+
function pickSigningSdk(sdkHome, targetApiVersion) {
|
|
1740
|
+
let apis;
|
|
1741
|
+
try {
|
|
1742
|
+
apis = fs15.readdirSync(sdkHome).filter((api2) => /^\d+$/.test(api2));
|
|
1743
|
+
} catch {
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
const usable = apis.filter((api2) => fs15.existsSync(sdkSigningFiles(path16.join(sdkHome, api2)).signTool)).sort((a, b) => Number(b) - Number(a));
|
|
1747
|
+
const api = usable.find((a) => targetApiVersion !== void 0 && Number(a) === targetApiVersion) ?? usable[0];
|
|
1748
|
+
return api ? { api, sdkPath: path16.join(sdkHome, api) } : null;
|
|
1749
|
+
}
|
|
1750
|
+
async function signOpenHarmonyHap(opts) {
|
|
1751
|
+
const logger = opts.logger ?? noopLogger;
|
|
1752
|
+
const hapPath = path16.resolve(opts.hapPath);
|
|
1753
|
+
const outPath = path16.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
|
|
1754
|
+
const apl = opts.apl ?? "normal";
|
|
1755
|
+
if (!APL_VALUES.includes(apl)) {
|
|
1756
|
+
throw new OniroError(`Invalid apl '${apl}'. Expected one of: ${APL_VALUES.join(", ")}.`);
|
|
1757
|
+
}
|
|
1758
|
+
const appFeature = resolveAppFeature(apl, opts.appFeature);
|
|
1759
|
+
if (!APP_FEATURE_VALUES.includes(appFeature)) {
|
|
1760
|
+
throw new OniroError(`Invalid appFeature '${appFeature}'. Expected one of: ${APP_FEATURE_VALUES.join(", ")}.`);
|
|
1761
|
+
}
|
|
1762
|
+
const info = await readHapSigningInfo(hapPath);
|
|
1763
|
+
if (info.compileSdkType === "HarmonyOS") {
|
|
1764
|
+
throw new OniroError(
|
|
1765
|
+
`${hapPath} was built for HarmonyOS, which needs a certificate from AppGallery Connect: run \`oniro-app auth login\` once, then \`oniro-app sign --harmonyos --hap\`.`
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
if (!isValidBundleName(info.bundleName)) {
|
|
1769
|
+
throw new OniroError(`${hapPath} names an invalid bundle name '${info.bundleName}'.`);
|
|
1770
|
+
}
|
|
1771
|
+
const sdk = pickSigningSdk(opts.sdkHome, info.targetApiVersion);
|
|
1772
|
+
if (!sdk) {
|
|
1773
|
+
throw new OniroError(
|
|
1774
|
+
`No OpenHarmony SDK with hap-sign-tool.jar under ${opts.sdkHome}; install one with \`oniro-app sdk install\`.`
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
const files = sdkSigningFiles(sdk.sdkPath);
|
|
1778
|
+
const kind = pickSigningKind(apl);
|
|
1779
|
+
const passwords = opts.passwords ?? DEFAULT_PASSWORDS;
|
|
1780
|
+
logger.info(`[sign] Signing ${path16.basename(hapPath)} as ${info.bundleName} with the API ${sdk.api} SDK's material...`);
|
|
1781
|
+
const signaturesDir = fs15.mkdtempSync(path16.join(os8.tmpdir(), "oniro-sign-hap-"));
|
|
1782
|
+
try {
|
|
1783
|
+
copySigningFiles(signaturesDir, files, kind, logger, opts.applicationCertPath);
|
|
1784
|
+
applyProfileTemplate(signaturesDir, info.bundleName, apl, appFeature, logger, {
|
|
1785
|
+
kind,
|
|
1786
|
+
acls: opts.acls,
|
|
1787
|
+
appPrivilegeCapabilities: opts.appPrivilegeCapabilities
|
|
1788
|
+
});
|
|
1789
|
+
generateP7bFile(signaturesDir, files, logger, passwords);
|
|
1790
|
+
logger.info("[sign] Signing the HAP via hap-sign-tool...");
|
|
1791
|
+
const { hapKeyAlias, certBasename } = SIGNING_KIND_CONFIG[kind];
|
|
1792
|
+
await writeHapAtomically(outPath, (tmpPath) => {
|
|
1793
|
+
const args = [
|
|
1794
|
+
"-jar",
|
|
1795
|
+
files.signTool,
|
|
1796
|
+
"sign-app",
|
|
1797
|
+
"-mode",
|
|
1798
|
+
"localSign",
|
|
1799
|
+
"-keyAlias",
|
|
1800
|
+
hapKeyAlias,
|
|
1801
|
+
"-signAlg",
|
|
1802
|
+
"SHA256withECDSA",
|
|
1803
|
+
"-appCertFile",
|
|
1804
|
+
path16.join(signaturesDir, certBasename),
|
|
1805
|
+
"-profileFile",
|
|
1806
|
+
path16.join(signaturesDir, "app1-profile.p7b"),
|
|
1807
|
+
"-inFile",
|
|
1808
|
+
hapPath,
|
|
1809
|
+
"-outFile",
|
|
1810
|
+
tmpPath,
|
|
1811
|
+
"-keystoreFile",
|
|
1812
|
+
path16.join(signaturesDir, "OpenHarmony.p12"),
|
|
1813
|
+
"-keyPwd",
|
|
1814
|
+
passwords.key,
|
|
1815
|
+
"-keystorePwd",
|
|
1816
|
+
passwords.store
|
|
1817
|
+
];
|
|
1818
|
+
try {
|
|
1819
|
+
execFileSync2("java", args, { stdio: "inherit" });
|
|
1820
|
+
} catch (e) {
|
|
1821
|
+
throw new OniroError(
|
|
1822
|
+
`hap-sign-tool failed to sign ${hapPath}. Ensure a JDK (with java) is on PATH. Underlying error: ${e.message}`,
|
|
1823
|
+
e
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
} finally {
|
|
1828
|
+
fs15.rmSync(signaturesDir, { recursive: true, force: true });
|
|
1829
|
+
}
|
|
1830
|
+
logger.info(`[sign] Wrote ${outPath}.`);
|
|
1831
|
+
return { bundleName: info.bundleName, outPath, sdkApi: sdk.api, kind };
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// src/emulator/install.ts
|
|
1835
|
+
import * as fs16 from "fs";
|
|
1836
|
+
import * as path17 from "path";
|
|
1650
1837
|
async function installEmulator(opts) {
|
|
1651
1838
|
const { config, progress, abortSignal } = opts;
|
|
1652
1839
|
const logger = opts.logger ?? noopLogger;
|
|
@@ -1656,20 +1843,20 @@ async function installEmulator(opts) {
|
|
|
1656
1843
|
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
1657
1844
|
config,
|
|
1658
1845
|
override: opts.tmpDir,
|
|
1659
|
-
installRoot:
|
|
1846
|
+
installRoot: path17.dirname(emulatorDir),
|
|
1660
1847
|
prefix: "oniro-emulator-",
|
|
1661
1848
|
logger
|
|
1662
1849
|
});
|
|
1663
|
-
const tmpZip =
|
|
1850
|
+
const tmpZip = path17.join(tmpDir, "oniro_emulator.zip");
|
|
1664
1851
|
try {
|
|
1665
|
-
|
|
1852
|
+
fs16.mkdirSync(emulatorDir, { recursive: true });
|
|
1666
1853
|
progress?.report({ message: "Downloading emulator...", increment: 0 });
|
|
1667
1854
|
await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
|
|
1668
1855
|
progress?.report({ message: "Extracting emulator...", increment: 0 });
|
|
1669
1856
|
await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
|
|
1670
|
-
const runSh =
|
|
1671
|
-
if (
|
|
1672
|
-
|
|
1857
|
+
const runSh = path17.join(emulatorDir, "images", "run.sh");
|
|
1858
|
+
if (fs16.existsSync(runSh)) {
|
|
1859
|
+
fs16.chmodSync(runSh, 493);
|
|
1673
1860
|
}
|
|
1674
1861
|
progress?.report({ message: "Finalizing installation...", increment: 5 });
|
|
1675
1862
|
} catch (err) {
|
|
@@ -1679,24 +1866,24 @@ async function installEmulator(opts) {
|
|
|
1679
1866
|
}
|
|
1680
1867
|
}
|
|
1681
1868
|
function isEmulatorInstalled(config) {
|
|
1682
|
-
const imagesDir =
|
|
1683
|
-
return
|
|
1869
|
+
const imagesDir = path17.join(getEmulatorDir(config), "images");
|
|
1870
|
+
return fs16.existsSync(path17.join(imagesDir, "run.sh")) || fs16.existsSync(path17.join(imagesDir, "run.bat"));
|
|
1684
1871
|
}
|
|
1685
1872
|
function removeEmulator(config) {
|
|
1686
1873
|
const emulatorDir = getEmulatorDir(config);
|
|
1687
|
-
if (
|
|
1688
|
-
|
|
1874
|
+
if (fs16.existsSync(emulatorDir)) {
|
|
1875
|
+
fs16.rmSync(emulatorDir, { recursive: true, force: true });
|
|
1689
1876
|
}
|
|
1690
1877
|
}
|
|
1691
1878
|
|
|
1692
1879
|
// src/emulator/lifecycle.ts
|
|
1693
|
-
import * as
|
|
1694
|
-
import * as
|
|
1695
|
-
import * as
|
|
1880
|
+
import * as fs18 from "fs";
|
|
1881
|
+
import * as os9 from "os";
|
|
1882
|
+
import * as path18 from "path";
|
|
1696
1883
|
import { spawn as spawn2 } from "child_process";
|
|
1697
1884
|
|
|
1698
1885
|
// src/hdc/exec.ts
|
|
1699
|
-
import * as
|
|
1886
|
+
import * as fs17 from "fs";
|
|
1700
1887
|
import { StringDecoder } from "string_decoder";
|
|
1701
1888
|
function errorMessage(err) {
|
|
1702
1889
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -1705,12 +1892,12 @@ function runProcess(opts) {
|
|
|
1705
1892
|
const { command, args, cwd, env, onOutput } = opts;
|
|
1706
1893
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1707
1894
|
const { abortSignal } = opts;
|
|
1708
|
-
return new Promise((
|
|
1895
|
+
return new Promise((resolve9, reject) => {
|
|
1709
1896
|
if (abortSignal?.aborted) {
|
|
1710
1897
|
reject(new CancelledError(`Command aborted before start: ${command}`));
|
|
1711
1898
|
return;
|
|
1712
1899
|
}
|
|
1713
|
-
if (needsCmdWrapper(command) && !
|
|
1900
|
+
if (needsCmdWrapper(command) && !fs17.existsSync(command)) {
|
|
1714
1901
|
reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
|
|
1715
1902
|
return;
|
|
1716
1903
|
}
|
|
@@ -1778,7 +1965,7 @@ ${stderr.trim()}` : "")
|
|
|
1778
1965
|
);
|
|
1779
1966
|
return;
|
|
1780
1967
|
}
|
|
1781
|
-
|
|
1968
|
+
resolve9({ code: code ?? -1, stdout, stderr });
|
|
1782
1969
|
});
|
|
1783
1970
|
});
|
|
1784
1971
|
}
|
|
@@ -1851,7 +2038,7 @@ function ensureRemoteOk(result, command) {
|
|
|
1851
2038
|
}
|
|
1852
2039
|
|
|
1853
2040
|
// src/emulator/lifecycle.ts
|
|
1854
|
-
var PID_FILE =
|
|
2041
|
+
var PID_FILE = path18.join(os9.tmpdir(), "oniro_emulator.pid");
|
|
1855
2042
|
async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
|
|
1856
2043
|
try {
|
|
1857
2044
|
await hdcExec({ config, args: ["start", "-r"], logger });
|
|
@@ -1864,24 +2051,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
|
|
|
1864
2051
|
}
|
|
1865
2052
|
}
|
|
1866
2053
|
function resolveLauncher(imagesPath, headless, connect) {
|
|
1867
|
-
const runSh =
|
|
1868
|
-
const runBat =
|
|
2054
|
+
const runSh = path18.join(imagesPath, "run.sh");
|
|
2055
|
+
const runBat = path18.join(imagesPath, "run.bat");
|
|
1869
2056
|
const extra = [];
|
|
1870
2057
|
if (headless) extra.push("--headless");
|
|
1871
2058
|
if (connect) extra.push("--connect", connect);
|
|
1872
|
-
if (
|
|
1873
|
-
if (
|
|
2059
|
+
if (os9.platform() === "win32") {
|
|
2060
|
+
if (fs18.existsSync(runBat)) {
|
|
1874
2061
|
return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
|
|
1875
2062
|
}
|
|
1876
|
-
if (
|
|
2063
|
+
if (fs18.existsSync(runSh)) {
|
|
1877
2064
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1878
2065
|
}
|
|
1879
2066
|
throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
|
|
1880
2067
|
}
|
|
1881
|
-
if (
|
|
2068
|
+
if (fs18.existsSync(runSh)) {
|
|
1882
2069
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1883
2070
|
}
|
|
1884
|
-
if (
|
|
2071
|
+
if (fs18.existsSync(runBat)) {
|
|
1885
2072
|
return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
|
|
1886
2073
|
}
|
|
1887
2074
|
throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
|
|
@@ -1889,9 +2076,9 @@ function resolveLauncher(imagesPath, headless, connect) {
|
|
|
1889
2076
|
async function startEmulator(opts) {
|
|
1890
2077
|
const { config } = opts;
|
|
1891
2078
|
const logger = opts.logger ?? noopLogger;
|
|
1892
|
-
if (
|
|
2079
|
+
if (os9.platform() !== "win32" && fs18.existsSync(PID_FILE)) {
|
|
1893
2080
|
try {
|
|
1894
|
-
const pid = Number(
|
|
2081
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
1895
2082
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1896
2083
|
try {
|
|
1897
2084
|
process.kill(pid, 0);
|
|
@@ -1907,13 +2094,13 @@ async function startEmulator(opts) {
|
|
|
1907
2094
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
1908
2095
|
}
|
|
1909
2096
|
}
|
|
1910
|
-
const imagesPath =
|
|
1911
|
-
if (!
|
|
2097
|
+
const imagesPath = path18.join(getEmulatorDir(config), "images");
|
|
2098
|
+
if (!fs18.existsSync(imagesPath)) {
|
|
1912
2099
|
throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
|
|
1913
2100
|
}
|
|
1914
2101
|
const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
|
|
1915
|
-
const logPath = opts.logFile ?? (
|
|
1916
|
-
const logFd =
|
|
2102
|
+
const logPath = opts.logFile ?? (os9.platform() === "win32" ? "NUL" : "/dev/null");
|
|
2103
|
+
const logFd = fs18.openSync(logPath, "a");
|
|
1917
2104
|
let child;
|
|
1918
2105
|
try {
|
|
1919
2106
|
child = spawn2(launcher.command, [...launcher.args], {
|
|
@@ -1923,19 +2110,19 @@ async function startEmulator(opts) {
|
|
|
1923
2110
|
windowsHide: true
|
|
1924
2111
|
});
|
|
1925
2112
|
} finally {
|
|
1926
|
-
|
|
2113
|
+
fs18.closeSync(logFd);
|
|
1927
2114
|
}
|
|
1928
2115
|
if (!child.pid) {
|
|
1929
2116
|
throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
|
|
1930
2117
|
}
|
|
1931
|
-
|
|
2118
|
+
fs18.writeFileSync(PID_FILE, String(child.pid));
|
|
1932
2119
|
child.unref();
|
|
1933
2120
|
logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
|
|
1934
|
-
const earlyExit = new Promise((
|
|
1935
|
-
child.once("exit", (code) =>
|
|
1936
|
-
child.once("error", () =>
|
|
2121
|
+
const earlyExit = new Promise((resolve9) => {
|
|
2122
|
+
child.once("exit", (code) => resolve9(code));
|
|
2123
|
+
child.once("error", () => resolve9(-1));
|
|
1937
2124
|
});
|
|
1938
|
-
const liveness = new Promise((
|
|
2125
|
+
const liveness = new Promise((resolve9) => setTimeout(() => resolve9("alive"), 2e3));
|
|
1939
2126
|
const outcome = await Promise.race([earlyExit, liveness]);
|
|
1940
2127
|
if (outcome !== "alive") {
|
|
1941
2128
|
const tail = readTail(logPath, 4e3);
|
|
@@ -1975,24 +2162,24 @@ ${tail}` : ""}`);
|
|
|
1975
2162
|
}
|
|
1976
2163
|
function readTail(filePath, bytes) {
|
|
1977
2164
|
try {
|
|
1978
|
-
const stat =
|
|
2165
|
+
const stat = fs18.statSync(filePath);
|
|
1979
2166
|
const start = Math.max(0, stat.size - bytes);
|
|
1980
|
-
const fd =
|
|
2167
|
+
const fd = fs18.openSync(filePath, "r");
|
|
1981
2168
|
try {
|
|
1982
2169
|
const buf = Buffer.alloc(stat.size - start);
|
|
1983
|
-
|
|
2170
|
+
fs18.readSync(fd, buf, 0, buf.length, start);
|
|
1984
2171
|
return buf.toString("utf8");
|
|
1985
2172
|
} finally {
|
|
1986
|
-
|
|
2173
|
+
fs18.closeSync(fd);
|
|
1987
2174
|
}
|
|
1988
2175
|
} catch {
|
|
1989
2176
|
return "";
|
|
1990
2177
|
}
|
|
1991
2178
|
}
|
|
1992
2179
|
async function stopEmulator(logger = noopLogger) {
|
|
1993
|
-
if (
|
|
2180
|
+
if (fs18.existsSync(PID_FILE)) {
|
|
1994
2181
|
try {
|
|
1995
|
-
const pid = Number(
|
|
2182
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
1996
2183
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1997
2184
|
try {
|
|
1998
2185
|
process.kill(pid, "SIGTERM");
|
|
@@ -2005,20 +2192,20 @@ async function stopEmulator(logger = noopLogger) {
|
|
|
2005
2192
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
2006
2193
|
}
|
|
2007
2194
|
}
|
|
2008
|
-
const [killCmd, ...killArgs] =
|
|
2195
|
+
const [killCmd, ...killArgs] = os9.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
|
|
2009
2196
|
try {
|
|
2010
2197
|
await runProcess({ command: killCmd, args: killArgs, logger });
|
|
2011
2198
|
} catch (err) {
|
|
2012
2199
|
logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
|
|
2013
2200
|
}
|
|
2014
|
-
|
|
2201
|
+
fs18.rm(PID_FILE, { force: true }, (err) => {
|
|
2015
2202
|
if (err) logger.warn(`Could not remove PID file: ${err.message}`);
|
|
2016
2203
|
});
|
|
2017
2204
|
}
|
|
2018
2205
|
async function isQemuRunning(logger) {
|
|
2019
|
-
if (
|
|
2206
|
+
if (os9.platform() !== "win32") {
|
|
2020
2207
|
try {
|
|
2021
|
-
const pid = Number(
|
|
2208
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
2022
2209
|
if (Number.isFinite(pid) && pid > 0) {
|
|
2023
2210
|
process.kill(pid, 0);
|
|
2024
2211
|
return true;
|
|
@@ -2026,10 +2213,10 @@ async function isQemuRunning(logger) {
|
|
|
2026
2213
|
} catch {
|
|
2027
2214
|
}
|
|
2028
2215
|
}
|
|
2029
|
-
const [cmd, ...args] =
|
|
2216
|
+
const [cmd, ...args] = os9.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
|
|
2030
2217
|
try {
|
|
2031
2218
|
const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
|
|
2032
|
-
return
|
|
2219
|
+
return os9.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
|
|
2033
2220
|
} catch {
|
|
2034
2221
|
return false;
|
|
2035
2222
|
}
|
|
@@ -2090,12 +2277,13 @@ async function listDevices(config, opts = {}) {
|
|
|
2090
2277
|
});
|
|
2091
2278
|
return parseDeviceList(res.stdout);
|
|
2092
2279
|
}
|
|
2280
|
+
function isUsableDevice(device) {
|
|
2281
|
+
return device.status === "Connected" || device.status === "Unknown";
|
|
2282
|
+
}
|
|
2093
2283
|
async function selectDevice(config, serial, opts = {}) {
|
|
2094
2284
|
const resolved = resolveDeviceSerial(serial);
|
|
2095
2285
|
if (resolved) return resolved;
|
|
2096
|
-
const usable = (await listDevices(config, opts)).filter(
|
|
2097
|
-
(d) => d.status === "Connected" || d.status === "Unknown"
|
|
2098
|
-
);
|
|
2286
|
+
const usable = (await listDevices(config, opts)).filter(isUsableDevice);
|
|
2099
2287
|
if (usable.length === 1) return usable[0].serial;
|
|
2100
2288
|
if (usable.length === 0) {
|
|
2101
2289
|
throw new OniroError("No connected device found. Connect a device/emulator or set ONIRO_DEVICE_SERIAL.");
|
|
@@ -2153,11 +2341,11 @@ async function paramSet(config, key, value, opts = {}) {
|
|
|
2153
2341
|
}
|
|
2154
2342
|
|
|
2155
2343
|
// src/hdc/files.ts
|
|
2156
|
-
import * as
|
|
2344
|
+
import * as path19 from "path";
|
|
2157
2345
|
function normaliseLocalPath(local) {
|
|
2158
|
-
const abs =
|
|
2346
|
+
const abs = path19.resolve(local);
|
|
2159
2347
|
const long = toLongPath(abs);
|
|
2160
|
-
return long !== abs ? long :
|
|
2348
|
+
return long !== abs ? long : path19.join(toLongPath(path19.dirname(abs)), path19.basename(abs));
|
|
2161
2349
|
}
|
|
2162
2350
|
function assertDevicePath(remote) {
|
|
2163
2351
|
if (/^[A-Za-z]:[\\/]/.test(remote)) {
|
|
@@ -2176,13 +2364,13 @@ async function sendFile(opts) {
|
|
|
2176
2364
|
assertDevicePath(opts.remote);
|
|
2177
2365
|
const local = normaliseLocalPath(opts.local);
|
|
2178
2366
|
const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
|
|
2179
|
-
const remote = sandbox && !opts.remote.startsWith("/") ?
|
|
2367
|
+
const remote = sandbox && !opts.remote.startsWith("/") ? path19.posix.join(sandbox, opts.remote) : opts.remote;
|
|
2180
2368
|
if (sandbox && !remote.startsWith(`${sandbox}/`)) {
|
|
2181
2369
|
throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
|
|
2182
2370
|
}
|
|
2183
2371
|
const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
|
|
2184
2372
|
if (sandbox) {
|
|
2185
|
-
const mkdir = `mkdir -p ${shQuote(
|
|
2373
|
+
const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
|
|
2186
2374
|
ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
|
|
2187
2375
|
}
|
|
2188
2376
|
const res = await hdcExec({
|
|
@@ -2194,7 +2382,7 @@ async function sendFile(opts) {
|
|
|
2194
2382
|
});
|
|
2195
2383
|
ensureOk(res, `hdc file send ${local} ${remote}`);
|
|
2196
2384
|
if (sandbox) {
|
|
2197
|
-
const top =
|
|
2385
|
+
const top = path19.posix.join(sandbox, path19.posix.relative(sandbox, remote).split("/")[0]);
|
|
2198
2386
|
const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
|
|
2199
2387
|
ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
|
|
2200
2388
|
}
|
|
@@ -2232,7 +2420,7 @@ async function getDisplaySize(config, opts = {}) {
|
|
|
2232
2420
|
|
|
2233
2421
|
// src/hdc/wait.ts
|
|
2234
2422
|
function delay(ms, signal) {
|
|
2235
|
-
return new Promise((
|
|
2423
|
+
return new Promise((resolve9, reject) => {
|
|
2236
2424
|
if (signal?.aborted) {
|
|
2237
2425
|
reject(new CancelledError("Wait cancelled."));
|
|
2238
2426
|
return;
|
|
@@ -2247,7 +2435,7 @@ function delay(ms, signal) {
|
|
|
2247
2435
|
};
|
|
2248
2436
|
const timer = setTimeout(() => {
|
|
2249
2437
|
cleanup();
|
|
2250
|
-
|
|
2438
|
+
resolve9();
|
|
2251
2439
|
}, ms);
|
|
2252
2440
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2253
2441
|
});
|
|
@@ -2276,18 +2464,18 @@ async function waitForCondition(opts) {
|
|
|
2276
2464
|
}
|
|
2277
2465
|
|
|
2278
2466
|
// src/hdc/app.ts
|
|
2279
|
-
import * as
|
|
2280
|
-
import * as
|
|
2467
|
+
import * as fs21 from "fs";
|
|
2468
|
+
import * as path22 from "path";
|
|
2281
2469
|
|
|
2282
2470
|
// src/build/discoverHaps.ts
|
|
2283
|
-
import * as
|
|
2284
|
-
import * as
|
|
2471
|
+
import * as fs19 from "fs";
|
|
2472
|
+
import * as path20 from "path";
|
|
2285
2473
|
function moduleOfHap(hapFileName) {
|
|
2286
2474
|
const dash = hapFileName.indexOf("-");
|
|
2287
2475
|
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
2288
2476
|
}
|
|
2289
2477
|
function isAppProjectRoot(dir) {
|
|
2290
|
-
return
|
|
2478
|
+
return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
|
|
2291
2479
|
}
|
|
2292
2480
|
async function discoverHaps(opts) {
|
|
2293
2481
|
const { projectDir } = opts;
|
|
@@ -2296,9 +2484,9 @@ async function discoverHaps(opts) {
|
|
|
2296
2484
|
const result = {};
|
|
2297
2485
|
const walk = async (dir, depth) => {
|
|
2298
2486
|
if (depth > maxDepth) return;
|
|
2299
|
-
const entries = await
|
|
2487
|
+
const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2300
2488
|
for (const e of entries) {
|
|
2301
|
-
const full =
|
|
2489
|
+
const full = path20.join(dir, e.name);
|
|
2302
2490
|
if (e.isDirectory()) {
|
|
2303
2491
|
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2304
2492
|
if (skipNestedProjects && isAppProjectRoot(full)) continue;
|
|
@@ -2312,7 +2500,7 @@ async function discoverHaps(opts) {
|
|
|
2312
2500
|
}
|
|
2313
2501
|
};
|
|
2314
2502
|
await walk(projectDir, 0);
|
|
2315
|
-
const mtime = (p) =>
|
|
2503
|
+
const mtime = (p) => fs19.statSync(p).mtimeMs;
|
|
2316
2504
|
for (const bucket of Object.values(result)) {
|
|
2317
2505
|
bucket.signed.sort((a, b) => mtime(b) - mtime(a));
|
|
2318
2506
|
bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
|
|
@@ -2338,16 +2526,16 @@ function pickHap(haps, module) {
|
|
|
2338
2526
|
}
|
|
2339
2527
|
|
|
2340
2528
|
// src/hdc/project.ts
|
|
2341
|
-
import * as
|
|
2342
|
-
import * as
|
|
2529
|
+
import * as fs20 from "fs";
|
|
2530
|
+
import * as path21 from "path";
|
|
2343
2531
|
import JSON53 from "json5";
|
|
2344
2532
|
function readJson5(filePath) {
|
|
2345
|
-
const content =
|
|
2533
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
2346
2534
|
return JSON53.parse(content);
|
|
2347
2535
|
}
|
|
2348
2536
|
function getBundleName(projectDir) {
|
|
2349
|
-
const appJsonPath =
|
|
2350
|
-
if (!
|
|
2537
|
+
const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
|
|
2538
|
+
if (!fs20.existsSync(appJsonPath)) {
|
|
2351
2539
|
throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
|
|
2352
2540
|
}
|
|
2353
2541
|
const appJson = readJson5(appJsonPath);
|
|
@@ -2357,8 +2545,8 @@ function getBundleName(projectDir) {
|
|
|
2357
2545
|
return appJson.app.bundleName;
|
|
2358
2546
|
}
|
|
2359
2547
|
function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
2360
|
-
const moduleJsonPath =
|
|
2361
|
-
if (!
|
|
2548
|
+
const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
|
|
2549
|
+
if (!fs20.existsSync(moduleJsonPath)) {
|
|
2362
2550
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
2363
2551
|
}
|
|
2364
2552
|
const moduleJson = readJson5(moduleJsonPath);
|
|
@@ -2382,7 +2570,7 @@ function detectInstallFailure(output) {
|
|
|
2382
2570
|
return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
|
|
2383
2571
|
}
|
|
2384
2572
|
function isUnsignedHap(hapPath) {
|
|
2385
|
-
return
|
|
2573
|
+
return path22.basename(hapPath).includes("unsigned");
|
|
2386
2574
|
}
|
|
2387
2575
|
function installFailureReason(output) {
|
|
2388
2576
|
const code = /code:\s*(\d+)/i.exec(output);
|
|
@@ -2391,7 +2579,7 @@ function installFailureReason(output) {
|
|
|
2391
2579
|
return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
|
|
2392
2580
|
}
|
|
2393
2581
|
function unsignedHapRejection(output, hap) {
|
|
2394
|
-
const name =
|
|
2582
|
+
const name = path22.basename(hap.path);
|
|
2395
2583
|
const reason = installFailureReason(output);
|
|
2396
2584
|
const what = hap.fallback ? `No signed HAP was found, so the unsigned ${name} was tried, and the target rejected it (${reason}).` : `The target rejected the unsigned ${name} (${reason}).`;
|
|
2397
2585
|
if (!/9568320|signature|pkcs7/i.test(reason)) return what;
|
|
@@ -2409,24 +2597,24 @@ ${result.stderr}`;
|
|
|
2409
2597
|
return result;
|
|
2410
2598
|
}
|
|
2411
2599
|
function existingHapOrUnsigned(hapPath) {
|
|
2412
|
-
if (
|
|
2600
|
+
if (fs21.existsSync(hapPath)) return { path: hapPath };
|
|
2413
2601
|
const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
|
|
2414
|
-
return sibling !== hapPath &&
|
|
2602
|
+
return sibling !== hapPath && fs21.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
|
|
2415
2603
|
}
|
|
2416
2604
|
async function resolveInstallHap(opts, logger) {
|
|
2417
2605
|
const warnIfFallback = (hap) => {
|
|
2418
|
-
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${
|
|
2606
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path22.basename(hap.path)}.`);
|
|
2419
2607
|
return hap;
|
|
2420
2608
|
};
|
|
2421
2609
|
const configured = opts.hapPath ?? opts.config.get("hapPath", "");
|
|
2422
2610
|
const relative3 = configured || defaultPaths.hapPath;
|
|
2423
|
-
const hapPath =
|
|
2611
|
+
const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
|
|
2424
2612
|
if (configured) {
|
|
2425
2613
|
const hap = existingHapOrUnsigned(hapPath);
|
|
2426
2614
|
if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
|
|
2427
2615
|
return warnIfFallback(hap);
|
|
2428
2616
|
}
|
|
2429
|
-
if (
|
|
2617
|
+
if (fs21.existsSync(hapPath)) return { path: hapPath };
|
|
2430
2618
|
const haps = await discoverHaps({ projectDir: opts.projectDir });
|
|
2431
2619
|
const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
|
|
2432
2620
|
const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
|
|
@@ -2537,7 +2725,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2537
2725
|
const logger = options.logger ?? noopLogger;
|
|
2538
2726
|
const timeoutMs = options.timeoutMs ?? 1e3;
|
|
2539
2727
|
const hdc = getHdcPath(config);
|
|
2540
|
-
return new Promise((
|
|
2728
|
+
return new Promise((resolve9, reject) => {
|
|
2541
2729
|
let proc;
|
|
2542
2730
|
try {
|
|
2543
2731
|
proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
|
|
@@ -2557,7 +2745,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2557
2745
|
if (options.targetProcessName && name === options.targetProcessName && !resolved) {
|
|
2558
2746
|
resolved = true;
|
|
2559
2747
|
killProcessTree(proc);
|
|
2560
|
-
|
|
2748
|
+
resolve9(pid);
|
|
2561
2749
|
return;
|
|
2562
2750
|
}
|
|
2563
2751
|
}
|
|
@@ -2578,7 +2766,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2578
2766
|
if (options.targetProcessName) {
|
|
2579
2767
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2580
2768
|
} else {
|
|
2581
|
-
|
|
2769
|
+
resolve9(processes);
|
|
2582
2770
|
}
|
|
2583
2771
|
}, timeoutMs);
|
|
2584
2772
|
proc.on("close", () => {
|
|
@@ -2588,7 +2776,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2588
2776
|
if (options.targetProcessName) {
|
|
2589
2777
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2590
2778
|
} else {
|
|
2591
|
-
|
|
2779
|
+
resolve9(processes);
|
|
2592
2780
|
}
|
|
2593
2781
|
});
|
|
2594
2782
|
});
|
|
@@ -2757,12 +2945,12 @@ async function reboot(opts) {
|
|
|
2757
2945
|
}
|
|
2758
2946
|
|
|
2759
2947
|
// src/hdc/screen.ts
|
|
2760
|
-
import * as
|
|
2761
|
-
import * as
|
|
2762
|
-
import * as
|
|
2763
|
-
import { randomBytes as
|
|
2948
|
+
import * as fs22 from "fs";
|
|
2949
|
+
import * as os10 from "os";
|
|
2950
|
+
import * as path23 from "path";
|
|
2951
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
2764
2952
|
function tmpName(ext) {
|
|
2765
|
-
return
|
|
2953
|
+
return path23.join(os10.tmpdir(), `oniro_screen_${randomBytes3(6).toString("hex")}.${ext}`);
|
|
2766
2954
|
}
|
|
2767
2955
|
async function takeScreenshot(opts) {
|
|
2768
2956
|
const remote = "/data/local/tmp/oniro_screenshot.jpeg";
|
|
@@ -2772,11 +2960,11 @@ async function takeScreenshot(opts) {
|
|
|
2772
2960
|
const local = tmpName("jpeg");
|
|
2773
2961
|
try {
|
|
2774
2962
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2775
|
-
const pixels = await
|
|
2963
|
+
const pixels = await fs22.promises.readFile(local);
|
|
2776
2964
|
const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
|
|
2777
2965
|
return { pixels, width: size.width, height: size.height };
|
|
2778
2966
|
} finally {
|
|
2779
|
-
|
|
2967
|
+
fs22.promises.unlink(local).catch(() => {
|
|
2780
2968
|
});
|
|
2781
2969
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2782
2970
|
});
|
|
@@ -2784,7 +2972,7 @@ async function takeScreenshot(opts) {
|
|
|
2784
2972
|
}
|
|
2785
2973
|
async function captureBurst(opts) {
|
|
2786
2974
|
const count = Math.max(1, Math.floor(opts.count));
|
|
2787
|
-
const remoteDir = `/data/local/tmp/oniro_burst_${
|
|
2975
|
+
const remoteDir = `/data/local/tmp/oniro_burst_${randomBytes3(6).toString("hex")}`;
|
|
2788
2976
|
const sleepSec = opts.intervalMs > 0 ? (opts.intervalMs / 1e3).toFixed(3) : "0";
|
|
2789
2977
|
const loop = `mkdir -p ${remoteDir}; i=0; while [ $i -lt ${count} ]; do snapshot_display -f ${remoteDir}/f$i.jpeg; [ ${count} -gt 1 ] && sleep ${sleepSec}; i=$((i+1)); done`;
|
|
2790
2978
|
const cap = await shell({ config: opts.config, command: loop, deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 12e4 });
|
|
@@ -2795,9 +2983,9 @@ async function captureBurst(opts) {
|
|
|
2795
2983
|
const local = tmpName(`burst_${i}.jpeg`);
|
|
2796
2984
|
try {
|
|
2797
2985
|
await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
|
|
2798
|
-
frames.push(await
|
|
2986
|
+
frames.push(await fs22.promises.readFile(local));
|
|
2799
2987
|
} finally {
|
|
2800
|
-
|
|
2988
|
+
fs22.promises.unlink(local).catch(() => {
|
|
2801
2989
|
});
|
|
2802
2990
|
}
|
|
2803
2991
|
}
|
|
@@ -2972,10 +3160,10 @@ async function sendRawTouch(opts) {
|
|
|
2972
3160
|
}
|
|
2973
3161
|
|
|
2974
3162
|
// src/hdc/dump.ts
|
|
2975
|
-
import * as
|
|
2976
|
-
import * as
|
|
2977
|
-
import * as
|
|
2978
|
-
import { randomBytes as
|
|
3163
|
+
import * as fs23 from "fs";
|
|
3164
|
+
import * as os11 from "os";
|
|
3165
|
+
import * as path24 from "path";
|
|
3166
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
2979
3167
|
|
|
2980
3168
|
// src/hdc/ime.ts
|
|
2981
3169
|
var INPUT_METHOD_SA_ID = 3703;
|
|
@@ -3237,10 +3425,10 @@ async function dumpLayout(opts) {
|
|
|
3237
3425
|
if (!m) throw new OniroError(`Could not parse dumpLayout output:
|
|
3238
3426
|
${dump.stdout}`);
|
|
3239
3427
|
const remote = m[1];
|
|
3240
|
-
const local =
|
|
3428
|
+
const local = path24.join(os11.tmpdir(), `oniro_layout_${randomBytes4(6).toString("hex")}.json`);
|
|
3241
3429
|
try {
|
|
3242
3430
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
3243
|
-
const parsed = JSON.parse(await
|
|
3431
|
+
const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
|
|
3244
3432
|
const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
|
|
3245
3433
|
if (opts.prune === false) {
|
|
3246
3434
|
return { display, tree: parsed };
|
|
@@ -3253,7 +3441,7 @@ ${dump.stdout}`);
|
|
|
3253
3441
|
const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
|
|
3254
3442
|
return { display, tree, keyboard };
|
|
3255
3443
|
} finally {
|
|
3256
|
-
|
|
3444
|
+
fs23.promises.unlink(local).catch(() => {
|
|
3257
3445
|
});
|
|
3258
3446
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
3259
3447
|
});
|
|
@@ -3263,7 +3451,7 @@ ${dump.stdout}`);
|
|
|
3263
3451
|
// src/hdc/uiActions.ts
|
|
3264
3452
|
var FOCUS_SETTLE_MS = 400;
|
|
3265
3453
|
var KEYBOARD_SETTLE_MS = 400;
|
|
3266
|
-
var sleep = (ms) => new Promise((
|
|
3454
|
+
var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
3267
3455
|
function targetTree(layout) {
|
|
3268
3456
|
const kb = layout.keyboard;
|
|
3269
3457
|
return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
|
|
@@ -3416,11 +3604,11 @@ async function waitForTarget(opts) {
|
|
|
3416
3604
|
}
|
|
3417
3605
|
|
|
3418
3606
|
// src/hdc/hilog.ts
|
|
3419
|
-
import * as
|
|
3607
|
+
import * as fs24 from "fs";
|
|
3420
3608
|
function setHilogLevel(opts) {
|
|
3421
3609
|
const logger = opts.logger ?? noopLogger;
|
|
3422
3610
|
const hdc = getHdcPath(opts.config);
|
|
3423
|
-
return new Promise((
|
|
3611
|
+
return new Promise((resolve9, reject) => {
|
|
3424
3612
|
let child;
|
|
3425
3613
|
try {
|
|
3426
3614
|
child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
|
|
@@ -3440,7 +3628,7 @@ function setHilogLevel(opts) {
|
|
|
3440
3628
|
});
|
|
3441
3629
|
child.once("close", (code) => {
|
|
3442
3630
|
if (code === 0) {
|
|
3443
|
-
|
|
3631
|
+
resolve9();
|
|
3444
3632
|
} else {
|
|
3445
3633
|
const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
|
|
3446
3634
|
logger.error(`[hilog] ${msg}`);
|
|
@@ -3542,7 +3730,7 @@ async function recentEntryFilter(opts) {
|
|
|
3542
3730
|
};
|
|
3543
3731
|
}
|
|
3544
3732
|
function waitForLog(opts) {
|
|
3545
|
-
return new Promise((
|
|
3733
|
+
return new Promise((resolve9, reject) => {
|
|
3546
3734
|
const deadline = Date.now() + opts.timeoutMs;
|
|
3547
3735
|
let settled = false;
|
|
3548
3736
|
let child = null;
|
|
@@ -3563,7 +3751,7 @@ function waitForLog(opts) {
|
|
|
3563
3751
|
if (settled) return;
|
|
3564
3752
|
settled = true;
|
|
3565
3753
|
cleanup();
|
|
3566
|
-
|
|
3754
|
+
resolve9(entry);
|
|
3567
3755
|
};
|
|
3568
3756
|
const fail = (err) => {
|
|
3569
3757
|
if (settled) return;
|
|
@@ -3618,7 +3806,7 @@ function waitForLog(opts) {
|
|
|
3618
3806
|
});
|
|
3619
3807
|
}
|
|
3620
3808
|
function watchLog(opts) {
|
|
3621
|
-
return new Promise((
|
|
3809
|
+
return new Promise((resolve9, reject) => {
|
|
3622
3810
|
const matches = [];
|
|
3623
3811
|
let settled = false;
|
|
3624
3812
|
let child = null;
|
|
@@ -3637,7 +3825,7 @@ function watchLog(opts) {
|
|
|
3637
3825
|
if (settled) return;
|
|
3638
3826
|
settled = true;
|
|
3639
3827
|
cleanup();
|
|
3640
|
-
|
|
3828
|
+
resolve9(opts.dedup === false ? matches : dedupEntries(matches));
|
|
3641
3829
|
};
|
|
3642
3830
|
const onAbort = () => {
|
|
3643
3831
|
if (settled) return;
|
|
@@ -3682,11 +3870,11 @@ async function captureLog(opts) {
|
|
|
3682
3870
|
const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
|
|
3683
3871
|
ensureOk(res, "hdc shell hilog -r");
|
|
3684
3872
|
}
|
|
3685
|
-
const fd =
|
|
3873
|
+
const fd = fs24.openSync(opts.file, "a");
|
|
3686
3874
|
let lines = 0;
|
|
3687
3875
|
let reconnects = 0;
|
|
3688
3876
|
try {
|
|
3689
|
-
await new Promise((
|
|
3877
|
+
await new Promise((resolve9, reject) => {
|
|
3690
3878
|
const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
|
|
3691
3879
|
let settled = false;
|
|
3692
3880
|
let child = null;
|
|
@@ -3695,7 +3883,7 @@ async function captureLog(opts) {
|
|
|
3695
3883
|
let open = true;
|
|
3696
3884
|
const write = (line) => {
|
|
3697
3885
|
if (!open || opts.pattern && !opts.pattern.test(line)) return;
|
|
3698
|
-
|
|
3886
|
+
fs24.writeSync(fd, `${line}
|
|
3699
3887
|
`);
|
|
3700
3888
|
lines++;
|
|
3701
3889
|
};
|
|
@@ -3716,7 +3904,7 @@ async function captureLog(opts) {
|
|
|
3716
3904
|
if (settled) return;
|
|
3717
3905
|
settled = true;
|
|
3718
3906
|
cleanup();
|
|
3719
|
-
|
|
3907
|
+
resolve9();
|
|
3720
3908
|
}
|
|
3721
3909
|
const fail = (err) => {
|
|
3722
3910
|
if (settled) return;
|
|
@@ -3760,7 +3948,7 @@ async function captureLog(opts) {
|
|
|
3760
3948
|
spawnStream();
|
|
3761
3949
|
});
|
|
3762
3950
|
} finally {
|
|
3763
|
-
|
|
3951
|
+
fs24.closeSync(fd);
|
|
3764
3952
|
}
|
|
3765
3953
|
return { file: opts.file, lines, reconnects };
|
|
3766
3954
|
}
|
|
@@ -3788,17 +3976,17 @@ async function dumpLog(opts) {
|
|
|
3788
3976
|
}
|
|
3789
3977
|
|
|
3790
3978
|
// src/build/runHvigorw.ts
|
|
3791
|
-
import * as
|
|
3792
|
-
import * as
|
|
3979
|
+
import * as fs26 from "fs";
|
|
3980
|
+
import * as path26 from "path";
|
|
3793
3981
|
|
|
3794
3982
|
// src/harmonyos/target.ts
|
|
3795
|
-
import * as
|
|
3796
|
-
import * as
|
|
3983
|
+
import * as fs25 from "fs";
|
|
3984
|
+
import * as path25 from "path";
|
|
3797
3985
|
import JSON54 from "json5";
|
|
3798
3986
|
function detectRuntimeOs(projectDir, productName) {
|
|
3799
3987
|
let products = [];
|
|
3800
3988
|
try {
|
|
3801
|
-
const parsed = JSON54.parse(
|
|
3989
|
+
const parsed = JSON54.parse(fs25.readFileSync(path25.join(projectDir, "build-profile.json5"), "utf-8"));
|
|
3802
3990
|
if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
|
|
3803
3991
|
} catch {
|
|
3804
3992
|
return "OpenHarmony";
|
|
@@ -3839,7 +4027,7 @@ function runHvigorw(opts) {
|
|
|
3839
4027
|
const { config } = opts;
|
|
3840
4028
|
const projectDir = toLongPath(opts.projectDir);
|
|
3841
4029
|
const logger = opts.logger ?? noopLogger;
|
|
3842
|
-
if (!
|
|
4030
|
+
if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
|
|
3843
4031
|
throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
|
|
3844
4032
|
}
|
|
3845
4033
|
let hvigorw;
|
|
@@ -3851,24 +4039,24 @@ function runHvigorw(opts) {
|
|
|
3851
4039
|
hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
|
|
3852
4040
|
logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
|
|
3853
4041
|
} else {
|
|
3854
|
-
if (!
|
|
4042
|
+
if (!fs26.existsSync(getCmdToolsPath(config))) {
|
|
3855
4043
|
throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
|
|
3856
4044
|
}
|
|
3857
4045
|
sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
|
|
3858
4046
|
hvigorw = getHvigorwPath(config, projectDir);
|
|
3859
4047
|
}
|
|
3860
|
-
if (!
|
|
4048
|
+
if (!fs26.existsSync(hvigorw)) {
|
|
3861
4049
|
throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
|
|
3862
4050
|
}
|
|
3863
4051
|
const args = buildHvigorwArgs(opts);
|
|
3864
4052
|
if (process.platform !== "win32") {
|
|
3865
4053
|
try {
|
|
3866
|
-
|
|
4054
|
+
fs26.chmodSync(hvigorw, 493);
|
|
3867
4055
|
} catch {
|
|
3868
4056
|
}
|
|
3869
4057
|
}
|
|
3870
4058
|
const env = { ...process.env, ...sdkEnv };
|
|
3871
|
-
return new Promise((
|
|
4059
|
+
return new Promise((resolve9, reject) => {
|
|
3872
4060
|
if (opts.abortSignal?.aborted) {
|
|
3873
4061
|
reject(new CancelledError("hvigorw build cancelled."));
|
|
3874
4062
|
return;
|
|
@@ -3912,15 +4100,15 @@ function runHvigorw(opts) {
|
|
|
3912
4100
|
settled = true;
|
|
3913
4101
|
cleanup();
|
|
3914
4102
|
const exitCode = code ?? 1;
|
|
3915
|
-
if (exitCode === 0)
|
|
4103
|
+
if (exitCode === 0) resolve9({ exitCode });
|
|
3916
4104
|
else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
|
|
3917
4105
|
});
|
|
3918
4106
|
});
|
|
3919
4107
|
}
|
|
3920
4108
|
|
|
3921
4109
|
// src/build/ohpm.ts
|
|
3922
|
-
import * as
|
|
3923
|
-
import * as
|
|
4110
|
+
import * as fs27 from "fs";
|
|
4111
|
+
import * as path27 from "path";
|
|
3924
4112
|
function runOhpm(opts) {
|
|
3925
4113
|
return runProcess({
|
|
3926
4114
|
command: opts.ohpmPath ?? getOhpmPath(opts.config),
|
|
@@ -3933,7 +4121,7 @@ function runOhpm(opts) {
|
|
|
3933
4121
|
});
|
|
3934
4122
|
}
|
|
3935
4123
|
async function ensureOhModules(opts) {
|
|
3936
|
-
if (
|
|
4124
|
+
if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
|
|
3937
4125
|
return { installed: false };
|
|
3938
4126
|
}
|
|
3939
4127
|
const res = await runOhpm({
|
|
@@ -3950,8 +4138,8 @@ async function ensureOhModules(opts) {
|
|
|
3950
4138
|
}
|
|
3951
4139
|
|
|
3952
4140
|
// src/build/codelinter.ts
|
|
3953
|
-
import * as
|
|
3954
|
-
import * as
|
|
4141
|
+
import * as fs28 from "fs";
|
|
4142
|
+
import * as path28 from "path";
|
|
3955
4143
|
var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
|
|
3956
4144
|
var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
|
|
3957
4145
|
var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
|
|
@@ -4041,10 +4229,10 @@ function parseCodelinterFindings(output) {
|
|
|
4041
4229
|
var PROJECT_ROOT_FILES = [
|
|
4042
4230
|
["build-profile.json5"],
|
|
4043
4231
|
["hvigorfile.ts", "hvigorfile.js"],
|
|
4044
|
-
[
|
|
4232
|
+
[path28.join("hvigor", "hvigor-config.json5")]
|
|
4045
4233
|
];
|
|
4046
4234
|
function missingProjectRootFiles(projectDir) {
|
|
4047
|
-
return PROJECT_ROOT_FILES.filter((names) => !names.some((f) =>
|
|
4235
|
+
return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs28.existsSync(path28.join(projectDir, f)))).map(
|
|
4048
4236
|
(names) => names[0]
|
|
4049
4237
|
);
|
|
4050
4238
|
}
|
|
@@ -4126,32 +4314,22 @@ ${hint}`, err);
|
|
|
4126
4314
|
return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
|
|
4127
4315
|
}
|
|
4128
4316
|
|
|
4129
|
-
// src/project/validators.ts
|
|
4130
|
-
function isValidProjectName(name) {
|
|
4131
|
-
if (!name) return false;
|
|
4132
|
-
if (name.includes("/") || name.includes("\\")) return false;
|
|
4133
|
-
return /^[A-Za-z0-9._-]+$/.test(name);
|
|
4134
|
-
}
|
|
4135
|
-
function isValidBundleName(bundleName) {
|
|
4136
|
-
return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
|
|
4137
|
-
}
|
|
4138
|
-
|
|
4139
4317
|
// src/project/templates.ts
|
|
4140
|
-
import * as
|
|
4141
|
-
import * as
|
|
4318
|
+
import * as fs29 from "fs";
|
|
4319
|
+
import * as path29 from "path";
|
|
4142
4320
|
function toHumanTemplateName(folderName) {
|
|
4143
4321
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
4144
4322
|
}
|
|
4145
4323
|
function listTemplates(templateRoot) {
|
|
4146
|
-
if (!
|
|
4147
|
-
const entries =
|
|
4324
|
+
if (!fs29.existsSync(templateRoot)) return [];
|
|
4325
|
+
const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
|
|
4148
4326
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
4149
|
-
const dir =
|
|
4150
|
-
const metaPath =
|
|
4327
|
+
const dir = path29.join(templateRoot, e.name);
|
|
4328
|
+
const metaPath = path29.join(dir, "template.json");
|
|
4151
4329
|
let meta = {};
|
|
4152
|
-
if (
|
|
4330
|
+
if (fs29.existsSync(metaPath)) {
|
|
4153
4331
|
try {
|
|
4154
|
-
meta = JSON.parse(
|
|
4332
|
+
meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
|
|
4155
4333
|
} catch {
|
|
4156
4334
|
}
|
|
4157
4335
|
}
|
|
@@ -4166,57 +4344,57 @@ function listTemplates(templateRoot) {
|
|
|
4166
4344
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
4167
4345
|
const required = [
|
|
4168
4346
|
"build-profile.json5",
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4347
|
+
path29.join("AppScope", "app.json5"),
|
|
4348
|
+
path29.join(defaultModuleName, "src", "main", "module.json5"),
|
|
4349
|
+
path29.join(defaultModuleName, "oh-package.json5"),
|
|
4172
4350
|
"hvigorfile.ts"
|
|
4173
4351
|
];
|
|
4174
|
-
return required.filter((rel) => !
|
|
4352
|
+
return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
|
|
4175
4353
|
}
|
|
4176
4354
|
|
|
4177
4355
|
// src/project/createScaffold.ts
|
|
4178
|
-
import * as
|
|
4179
|
-
import * as
|
|
4356
|
+
import * as fs31 from "fs";
|
|
4357
|
+
import * as path30 from "path";
|
|
4180
4358
|
|
|
4181
4359
|
// src/project/jsonHelpers.ts
|
|
4182
|
-
import * as
|
|
4360
|
+
import * as fs30 from "fs";
|
|
4183
4361
|
import JSON55 from "json5";
|
|
4184
4362
|
function readJson5File(filePath) {
|
|
4185
|
-
return JSON55.parse(
|
|
4363
|
+
return JSON55.parse(fs30.readFileSync(filePath, "utf8"));
|
|
4186
4364
|
}
|
|
4187
4365
|
function writeJson5File(filePath, value) {
|
|
4188
|
-
|
|
4366
|
+
fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4189
4367
|
}
|
|
4190
4368
|
function readJsonFile(filePath) {
|
|
4191
|
-
return JSON.parse(
|
|
4369
|
+
return JSON.parse(fs30.readFileSync(filePath, "utf8"));
|
|
4192
4370
|
}
|
|
4193
4371
|
function writeJsonFile(filePath, value) {
|
|
4194
|
-
|
|
4372
|
+
fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4195
4373
|
}
|
|
4196
4374
|
|
|
4197
4375
|
// src/project/createScaffold.ts
|
|
4198
4376
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
4199
4377
|
async function pathExists(p) {
|
|
4200
4378
|
try {
|
|
4201
|
-
await
|
|
4379
|
+
await fs31.promises.access(p);
|
|
4202
4380
|
return true;
|
|
4203
4381
|
} catch {
|
|
4204
4382
|
return false;
|
|
4205
4383
|
}
|
|
4206
4384
|
}
|
|
4207
4385
|
async function copyDirRecursive(srcDir, destDir) {
|
|
4208
|
-
await
|
|
4209
|
-
const entries = await
|
|
4386
|
+
await fs31.promises.mkdir(destDir, { recursive: true });
|
|
4387
|
+
const entries = await fs31.promises.readdir(srcDir, { withFileTypes: true });
|
|
4210
4388
|
for (const entry of entries) {
|
|
4211
|
-
const src =
|
|
4212
|
-
const dest =
|
|
4389
|
+
const src = path30.join(srcDir, entry.name);
|
|
4390
|
+
const dest = path30.join(destDir, entry.name);
|
|
4213
4391
|
if (entry.isDirectory()) {
|
|
4214
4392
|
await copyDirRecursive(src, dest);
|
|
4215
4393
|
} else if (entry.isSymbolicLink()) {
|
|
4216
4394
|
continue;
|
|
4217
4395
|
} else if (entry.isFile()) {
|
|
4218
4396
|
if (entry.name === "template.json") continue;
|
|
4219
|
-
await
|
|
4397
|
+
await fs31.promises.copyFile(src, dest);
|
|
4220
4398
|
}
|
|
4221
4399
|
}
|
|
4222
4400
|
}
|
|
@@ -4224,9 +4402,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
4224
4402
|
const stack = [projectDir];
|
|
4225
4403
|
while (stack.length > 0) {
|
|
4226
4404
|
const currentDir = stack.pop();
|
|
4227
|
-
const entries = await
|
|
4405
|
+
const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
|
|
4228
4406
|
for (const entry of entries) {
|
|
4229
|
-
const fullPath =
|
|
4407
|
+
const fullPath = path30.join(currentDir, entry.name);
|
|
4230
4408
|
if (entry.isDirectory()) {
|
|
4231
4409
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4232
4410
|
stack.push(fullPath);
|
|
@@ -4248,23 +4426,23 @@ function toJavaPropertiesPath(p) {
|
|
|
4248
4426
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
4249
4427
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
4250
4428
|
`;
|
|
4251
|
-
|
|
4429
|
+
fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
|
|
4252
4430
|
}
|
|
4253
4431
|
function renameIfExists(fromPath, toPath) {
|
|
4254
|
-
if (
|
|
4255
|
-
|
|
4432
|
+
if (fs31.existsSync(fromPath) && fromPath !== toPath) {
|
|
4433
|
+
fs31.renameSync(fromPath, toPath);
|
|
4256
4434
|
}
|
|
4257
4435
|
}
|
|
4258
4436
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
4259
|
-
const appJsonPath =
|
|
4260
|
-
if (
|
|
4437
|
+
const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
|
|
4438
|
+
if (fs31.existsSync(appJsonPath)) {
|
|
4261
4439
|
const appJson = readJson5File(appJsonPath);
|
|
4262
4440
|
appJson.app = appJson.app ?? {};
|
|
4263
4441
|
appJson.app.bundleName = args.bundleName;
|
|
4264
4442
|
writeJson5File(appJsonPath, appJson);
|
|
4265
4443
|
}
|
|
4266
|
-
const stringsPath =
|
|
4267
|
-
if (
|
|
4444
|
+
const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
4445
|
+
if (fs31.existsSync(stringsPath)) {
|
|
4268
4446
|
const strings = readJsonFile(stringsPath);
|
|
4269
4447
|
if (Array.isArray(strings.string)) {
|
|
4270
4448
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -4272,8 +4450,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4272
4450
|
}
|
|
4273
4451
|
writeJsonFile(stringsPath, strings);
|
|
4274
4452
|
}
|
|
4275
|
-
const buildProfilePath =
|
|
4276
|
-
if (
|
|
4453
|
+
const buildProfilePath = path30.join(projectDir, "build-profile.json5");
|
|
4454
|
+
if (fs31.existsSync(buildProfilePath)) {
|
|
4277
4455
|
const buildProfile = readJson5File(buildProfilePath);
|
|
4278
4456
|
buildProfile.app = buildProfile.app ?? {};
|
|
4279
4457
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -4286,23 +4464,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4286
4464
|
}
|
|
4287
4465
|
writeJson5File(buildProfilePath, buildProfile);
|
|
4288
4466
|
}
|
|
4289
|
-
const moduleJsonPath =
|
|
4290
|
-
if (
|
|
4467
|
+
const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
4468
|
+
if (fs31.existsSync(moduleJsonPath)) {
|
|
4291
4469
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
4292
4470
|
moduleJson.module = moduleJson.module ?? {};
|
|
4293
4471
|
moduleJson.module.name = args.moduleName;
|
|
4294
4472
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
4295
4473
|
}
|
|
4296
|
-
const moduleOhPackagePath =
|
|
4297
|
-
if (
|
|
4474
|
+
const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
|
|
4475
|
+
if (fs31.existsSync(moduleOhPackagePath)) {
|
|
4298
4476
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
4299
4477
|
pkg.name = args.moduleName;
|
|
4300
4478
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
4301
4479
|
}
|
|
4302
|
-
const vscodeDir =
|
|
4303
|
-
|
|
4480
|
+
const vscodeDir = path30.join(projectDir, ".vscode");
|
|
4481
|
+
fs31.mkdirSync(vscodeDir, { recursive: true });
|
|
4304
4482
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
4305
|
-
writeJsonFile(
|
|
4483
|
+
writeJsonFile(path30.join(vscodeDir, "settings.json"), {
|
|
4306
4484
|
"oniro.hapPath": hapPath,
|
|
4307
4485
|
"files.associations": { "*.json5": "jsonc" }
|
|
4308
4486
|
});
|
|
@@ -4319,7 +4497,7 @@ async function createScaffold(opts) {
|
|
|
4319
4497
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
4320
4498
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
4321
4499
|
}
|
|
4322
|
-
const templateDir =
|
|
4500
|
+
const templateDir = path30.join(opts.templateRoot, opts.templateId);
|
|
4323
4501
|
if (!await pathExists(templateDir)) {
|
|
4324
4502
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
4325
4503
|
}
|
|
@@ -4354,17 +4532,17 @@ async function createScaffold(opts) {
|
|
|
4354
4532
|
}
|
|
4355
4533
|
}
|
|
4356
4534
|
}
|
|
4357
|
-
const projectDir =
|
|
4535
|
+
const projectDir = path30.join(opts.location, opts.projectName);
|
|
4358
4536
|
if (await pathExists(projectDir)) {
|
|
4359
4537
|
if (!opts.overwrite) {
|
|
4360
4538
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
4361
4539
|
}
|
|
4362
|
-
await
|
|
4540
|
+
await fs31.promises.rm(projectDir, { recursive: true, force: true });
|
|
4363
4541
|
}
|
|
4364
4542
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
4365
4543
|
await copyDirRecursive(templateDir, projectDir);
|
|
4366
4544
|
if (moduleName !== defaultModuleName) {
|
|
4367
|
-
renameIfExists(
|
|
4545
|
+
renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
|
|
4368
4546
|
}
|
|
4369
4547
|
updateTemplateConfigs(
|
|
4370
4548
|
projectDir,
|
|
@@ -4381,11 +4559,11 @@ async function createScaffold(opts) {
|
|
|
4381
4559
|
}
|
|
4382
4560
|
|
|
4383
4561
|
// src/project/listModules.ts
|
|
4384
|
-
import * as
|
|
4385
|
-
import * as
|
|
4562
|
+
import * as fs32 from "fs";
|
|
4563
|
+
import * as path31 from "path";
|
|
4386
4564
|
function listModules(opts) {
|
|
4387
|
-
const buildProfile =
|
|
4388
|
-
if (!
|
|
4565
|
+
const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
|
|
4566
|
+
if (!fs32.existsSync(buildProfile)) {
|
|
4389
4567
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
4390
4568
|
}
|
|
4391
4569
|
const parsed = readJson5File(buildProfile);
|
|
@@ -4397,12 +4575,12 @@ function listModules(opts) {
|
|
|
4397
4575
|
}
|
|
4398
4576
|
|
|
4399
4577
|
// src/project/abilities.ts
|
|
4400
|
-
import * as
|
|
4401
|
-
import * as
|
|
4578
|
+
import * as fs33 from "fs";
|
|
4579
|
+
import * as path32 from "path";
|
|
4402
4580
|
function listAbilities(opts) {
|
|
4403
4581
|
const moduleName = opts.moduleName ?? "entry";
|
|
4404
|
-
const moduleJsonPath =
|
|
4405
|
-
if (!
|
|
4582
|
+
const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
4583
|
+
if (!fs33.existsSync(moduleJsonPath)) {
|
|
4406
4584
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
4407
4585
|
}
|
|
4408
4586
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -4410,12 +4588,12 @@ function listAbilities(opts) {
|
|
|
4410
4588
|
}
|
|
4411
4589
|
|
|
4412
4590
|
// src/project/prepareSigning.ts
|
|
4413
|
-
import * as
|
|
4414
|
-
import * as
|
|
4591
|
+
import * as fs34 from "fs";
|
|
4592
|
+
import * as path33 from "path";
|
|
4415
4593
|
function hasSigningArtifacts(dir) {
|
|
4416
|
-
if (!
|
|
4417
|
-
const buildProfile =
|
|
4418
|
-
if (!
|
|
4594
|
+
if (!fs34.existsSync(path33.join(dir, "signatures"))) return false;
|
|
4595
|
+
const buildProfile = path33.join(dir, "build-profile.json5");
|
|
4596
|
+
if (!fs34.existsSync(buildProfile)) return false;
|
|
4419
4597
|
try {
|
|
4420
4598
|
const parsed = readJson5File(buildProfile);
|
|
4421
4599
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -4494,7 +4672,6 @@ async function getDeviceInfo(opts) {
|
|
|
4494
4672
|
}
|
|
4495
4673
|
|
|
4496
4674
|
// src/install/diffHapAssets.ts
|
|
4497
|
-
import StreamZip2 from "node-stream-zip";
|
|
4498
4675
|
function diffEntryNames(installed, next) {
|
|
4499
4676
|
const installedSet = new Set(installed);
|
|
4500
4677
|
const nextSet = new Set(next);
|
|
@@ -4503,14 +4680,11 @@ function diffEntryNames(installed, next) {
|
|
|
4503
4680
|
removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
|
|
4504
4681
|
};
|
|
4505
4682
|
}
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
} finally {
|
|
4512
|
-
await zip.close();
|
|
4513
|
-
}
|
|
4683
|
+
function listFileEntries(hapPath) {
|
|
4684
|
+
return withHapZip(
|
|
4685
|
+
hapPath,
|
|
4686
|
+
(zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
|
|
4687
|
+
);
|
|
4514
4688
|
}
|
|
4515
4689
|
async function diffHapAssets(opts) {
|
|
4516
4690
|
const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
|
|
@@ -4518,8 +4692,8 @@ async function diffHapAssets(opts) {
|
|
|
4518
4692
|
}
|
|
4519
4693
|
|
|
4520
4694
|
// src/install/apply.ts
|
|
4521
|
-
import * as
|
|
4522
|
-
import * as
|
|
4695
|
+
import * as fs35 from "fs";
|
|
4696
|
+
import * as path34 from "path";
|
|
4523
4697
|
|
|
4524
4698
|
// src/install/hapBundleName.ts
|
|
4525
4699
|
function bundleNameFromHapConfig(text) {
|
|
@@ -4557,7 +4731,7 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
4557
4731
|
async function resolveHap(opts, logger) {
|
|
4558
4732
|
let hap;
|
|
4559
4733
|
if (opts.hapPath) {
|
|
4560
|
-
const hapPath = !
|
|
4734
|
+
const hapPath = !path34.isAbsolute(opts.hapPath) && opts.projectDir ? path34.join(opts.projectDir, opts.hapPath) : opts.hapPath;
|
|
4561
4735
|
hap = existingHapOrUnsigned(hapPath);
|
|
4562
4736
|
if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
4563
4737
|
} else {
|
|
@@ -4566,7 +4740,7 @@ async function resolveHap(opts, logger) {
|
|
|
4566
4740
|
if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
|
|
4567
4741
|
hap = { path: picked.path, fallback: !picked.signed };
|
|
4568
4742
|
}
|
|
4569
|
-
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${
|
|
4743
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path34.basename(hap.path)}.`);
|
|
4570
4744
|
return hap;
|
|
4571
4745
|
}
|
|
4572
4746
|
async function applyChanges(opts) {
|
|
@@ -4582,12 +4756,12 @@ async function applyChanges(opts) {
|
|
|
4582
4756
|
});
|
|
4583
4757
|
if (hapBundle && hapBundle !== bundle) {
|
|
4584
4758
|
throw new OniroError(
|
|
4585
|
-
`${
|
|
4759
|
+
`${path34.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
4760
|
);
|
|
4587
4761
|
}
|
|
4588
4762
|
logger.info(`installing ${hapPath}`);
|
|
4589
4763
|
let assetsChanged = false;
|
|
4590
|
-
if (opts.installedHapPath &&
|
|
4764
|
+
if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
|
|
4591
4765
|
try {
|
|
4592
4766
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
4593
4767
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -4620,7 +4794,7 @@ async function applyChanges(opts) {
|
|
|
4620
4794
|
}
|
|
4621
4795
|
if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
|
|
4622
4796
|
throw new OniroError(
|
|
4623
|
-
`hdc reported ${
|
|
4797
|
+
`hdc reported ${path34.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
|
|
4624
4798
|
);
|
|
4625
4799
|
}
|
|
4626
4800
|
let postInstallPid = await pidOf(config, bundle, deviceSerial);
|
|
@@ -4697,10 +4871,10 @@ var AUTH_PATHS = {
|
|
|
4697
4871
|
};
|
|
4698
4872
|
|
|
4699
4873
|
// src/harmonyos/auth/tokenStore.ts
|
|
4700
|
-
import * as
|
|
4701
|
-
import * as
|
|
4702
|
-
import * as
|
|
4703
|
-
import * as
|
|
4874
|
+
import * as fs36 from "fs";
|
|
4875
|
+
import * as os12 from "os";
|
|
4876
|
+
import * as path35 from "path";
|
|
4877
|
+
import * as crypto4 from "crypto";
|
|
4704
4878
|
var ALGORITHM = "aes-256-gcm";
|
|
4705
4879
|
var KEY_LENGTH = 32;
|
|
4706
4880
|
var IV_LENGTH = 12;
|
|
@@ -4709,8 +4883,8 @@ var KEY_FILE = "kek.bin";
|
|
|
4709
4883
|
var DIR_MODE = 448;
|
|
4710
4884
|
var FILE_MODE = 384;
|
|
4711
4885
|
function encrypt2(key, plaintext) {
|
|
4712
|
-
const iv =
|
|
4713
|
-
const cipher =
|
|
4886
|
+
const iv = crypto4.randomBytes(IV_LENGTH);
|
|
4887
|
+
const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
|
|
4714
4888
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
4715
4889
|
return {
|
|
4716
4890
|
version: 1,
|
|
@@ -4721,7 +4895,7 @@ function encrypt2(key, plaintext) {
|
|
|
4721
4895
|
};
|
|
4722
4896
|
}
|
|
4723
4897
|
function decrypt2(key, blob) {
|
|
4724
|
-
const decipher =
|
|
4898
|
+
const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
|
|
4725
4899
|
decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
|
|
4726
4900
|
return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
|
|
4727
4901
|
}
|
|
@@ -4735,7 +4909,7 @@ function permissionHint(dir) {
|
|
|
4735
4909
|
}
|
|
4736
4910
|
function ensureDir(dir) {
|
|
4737
4911
|
try {
|
|
4738
|
-
|
|
4912
|
+
fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
4739
4913
|
} catch (err) {
|
|
4740
4914
|
if (err.code === "EACCES") {
|
|
4741
4915
|
throw new OniroError(permissionHint(dir), err);
|
|
@@ -4746,17 +4920,17 @@ function ensureDir(dir) {
|
|
|
4746
4920
|
function createTokenStore(opts) {
|
|
4747
4921
|
const logger = opts.logger ?? noopLogger;
|
|
4748
4922
|
const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
|
|
4749
|
-
const tokenPath =
|
|
4750
|
-
const keyPath =
|
|
4923
|
+
const tokenPath = path35.join(authDir, TOKEN_FILE);
|
|
4924
|
+
const keyPath = path35.join(os12.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
|
|
4751
4925
|
function loadOrCreateKey() {
|
|
4752
|
-
if (
|
|
4753
|
-
const existing =
|
|
4926
|
+
if (fs36.existsSync(keyPath)) {
|
|
4927
|
+
const existing = fs36.readFileSync(keyPath);
|
|
4754
4928
|
if (existing.length === KEY_LENGTH) return existing;
|
|
4755
4929
|
logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
|
|
4756
4930
|
}
|
|
4757
|
-
ensureDir(
|
|
4758
|
-
const key =
|
|
4759
|
-
|
|
4931
|
+
ensureDir(path35.dirname(keyPath));
|
|
4932
|
+
const key = crypto4.randomBytes(KEY_LENGTH);
|
|
4933
|
+
fs36.writeFileSync(keyPath, key, { mode: FILE_MODE });
|
|
4760
4934
|
return key;
|
|
4761
4935
|
}
|
|
4762
4936
|
return {
|
|
@@ -4766,12 +4940,12 @@ function createTokenStore(opts) {
|
|
|
4766
4940
|
const payload = Buffer.from(JSON.stringify(session), "utf8");
|
|
4767
4941
|
const blob = encrypt2(loadOrCreateKey(), payload);
|
|
4768
4942
|
ensureDir(authDir);
|
|
4769
|
-
|
|
4943
|
+
fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
|
|
4770
4944
|
},
|
|
4771
4945
|
load() {
|
|
4772
|
-
if (!
|
|
4946
|
+
if (!fs36.existsSync(tokenPath)) return null;
|
|
4773
4947
|
try {
|
|
4774
|
-
const blob = JSON.parse(
|
|
4948
|
+
const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
|
|
4775
4949
|
if (!isEncryptedBlob(blob)) return null;
|
|
4776
4950
|
const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
|
|
4777
4951
|
return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
|
|
@@ -4783,7 +4957,7 @@ function createTokenStore(opts) {
|
|
|
4783
4957
|
}
|
|
4784
4958
|
logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
|
|
4785
4959
|
try {
|
|
4786
|
-
|
|
4960
|
+
fs36.rmSync(tokenPath, { force: true });
|
|
4787
4961
|
} catch {
|
|
4788
4962
|
}
|
|
4789
4963
|
return null;
|
|
@@ -4791,7 +4965,7 @@ function createTokenStore(opts) {
|
|
|
4791
4965
|
},
|
|
4792
4966
|
clear() {
|
|
4793
4967
|
try {
|
|
4794
|
-
|
|
4968
|
+
fs36.rmSync(tokenPath, { force: true });
|
|
4795
4969
|
} catch (err) {
|
|
4796
4970
|
throw new OniroError(`Failed to remove ${tokenPath}.`, err);
|
|
4797
4971
|
}
|
|
@@ -4800,7 +4974,7 @@ function createTokenStore(opts) {
|
|
|
4800
4974
|
}
|
|
4801
4975
|
|
|
4802
4976
|
// src/harmonyos/auth/session.ts
|
|
4803
|
-
import * as
|
|
4977
|
+
import * as crypto6 from "crypto";
|
|
4804
4978
|
|
|
4805
4979
|
// src/harmonyos/http.ts
|
|
4806
4980
|
import { EnvHttpProxyAgent, fetch } from "undici";
|
|
@@ -4891,13 +5065,13 @@ function createHttpClient(opts = {}) {
|
|
|
4891
5065
|
|
|
4892
5066
|
// src/harmonyos/auth/callbackServer.ts
|
|
4893
5067
|
import * as http2 from "http";
|
|
4894
|
-
import * as
|
|
5068
|
+
import * as crypto5 from "crypto";
|
|
4895
5069
|
var CALLBACK_PATH = "/callback";
|
|
4896
5070
|
var MAX_BODY_BYTES = 65536;
|
|
4897
5071
|
function codeMatches(code, secret) {
|
|
4898
5072
|
const a = Buffer.from(code ?? "", "utf8");
|
|
4899
5073
|
const b = Buffer.from(secret, "utf8");
|
|
4900
|
-
return a.length === b.length &&
|
|
5074
|
+
return a.length === b.length && crypto5.timingSafeEqual(a, b);
|
|
4901
5075
|
}
|
|
4902
5076
|
function isQuit(quit) {
|
|
4903
5077
|
return quit === "true" || quit === "access_denied" || quit === "quit";
|
|
@@ -4905,8 +5079,8 @@ function isQuit(quit) {
|
|
|
4905
5079
|
async function startCallbackServer(opts) {
|
|
4906
5080
|
let resolveCallback;
|
|
4907
5081
|
let rejectCallback;
|
|
4908
|
-
const callback = new Promise((
|
|
4909
|
-
resolveCallback =
|
|
5082
|
+
const callback = new Promise((resolve9, reject) => {
|
|
5083
|
+
resolveCallback = resolve9;
|
|
4910
5084
|
rejectCallback = reject;
|
|
4911
5085
|
});
|
|
4912
5086
|
callback.catch(() => {
|
|
@@ -4965,9 +5139,9 @@ async function startCallbackServer(opts) {
|
|
|
4965
5139
|
}
|
|
4966
5140
|
});
|
|
4967
5141
|
server.keepAliveTimeout = 1;
|
|
4968
|
-
await new Promise((
|
|
5142
|
+
await new Promise((resolve9, reject) => {
|
|
4969
5143
|
server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
|
|
4970
|
-
server.listen(0, "127.0.0.1",
|
|
5144
|
+
server.listen(0, "127.0.0.1", resolve9);
|
|
4971
5145
|
});
|
|
4972
5146
|
const port = server.address().port;
|
|
4973
5147
|
return {
|
|
@@ -4983,12 +5157,12 @@ async function startCallbackServer(opts) {
|
|
|
4983
5157
|
return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
|
|
4984
5158
|
},
|
|
4985
5159
|
stop() {
|
|
4986
|
-
return new Promise((
|
|
5160
|
+
return new Promise((resolve9) => {
|
|
4987
5161
|
server.closeAllConnections?.();
|
|
4988
|
-
const timer = setTimeout(
|
|
5162
|
+
const timer = setTimeout(resolve9, 100);
|
|
4989
5163
|
server.close(() => {
|
|
4990
5164
|
clearTimeout(timer);
|
|
4991
|
-
|
|
5165
|
+
resolve9();
|
|
4992
5166
|
});
|
|
4993
5167
|
});
|
|
4994
5168
|
}
|
|
@@ -5020,11 +5194,11 @@ function openBrowser(url) {
|
|
|
5020
5194
|
windowsHide: true
|
|
5021
5195
|
});
|
|
5022
5196
|
child.unref();
|
|
5023
|
-
return new Promise((
|
|
5197
|
+
return new Promise((resolve9, reject) => {
|
|
5024
5198
|
child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
|
|
5025
5199
|
child.on(
|
|
5026
5200
|
"exit",
|
|
5027
|
-
(code) => code === 0 ?
|
|
5201
|
+
(code) => code === 0 ? resolve9() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
|
|
5028
5202
|
);
|
|
5029
5203
|
});
|
|
5030
5204
|
}
|
|
@@ -5106,7 +5280,7 @@ function createHarmonyOsSession(opts) {
|
|
|
5106
5280
|
getDeveloperAgreement,
|
|
5107
5281
|
async login(o = {}) {
|
|
5108
5282
|
const start = HUAWEI_SITES.CN.loginUrl;
|
|
5109
|
-
const clientSecret =
|
|
5283
|
+
const clientSecret = crypto6.randomUUID().replace(/-/g, "");
|
|
5110
5284
|
const server = await startCallbackServer({
|
|
5111
5285
|
clientSecret,
|
|
5112
5286
|
baseUrl: start,
|
|
@@ -5227,7 +5401,7 @@ var SIGNING_ERRORS = {
|
|
|
5227
5401
|
DEVICE_ADD: "AppGallery Connect did not register the device.",
|
|
5228
5402
|
DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
|
|
5229
5403
|
DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
|
|
5230
|
-
DEVICE_NONE: "
|
|
5404
|
+
DEVICE_NONE: "This team has no registered device and none could be registered from here, but a debug profile must name at least one. Connect a device that reports a UDID (`hdc shell bm get -u`), or register one in AppGallery Connect, and retry.",
|
|
5231
5405
|
PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
|
|
5232
5406
|
PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
|
|
5233
5407
|
PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
|
|
@@ -5272,7 +5446,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
|
|
|
5272
5446
|
}
|
|
5273
5447
|
|
|
5274
5448
|
// src/harmonyos/signing/profile.ts
|
|
5275
|
-
import * as
|
|
5449
|
+
import * as fs37 from "fs";
|
|
5276
5450
|
import { X509Certificate } from "crypto";
|
|
5277
5451
|
import forge from "node-forge";
|
|
5278
5452
|
function extractProfileJson(buffer) {
|
|
@@ -5304,7 +5478,7 @@ function extractProfileJson(buffer) {
|
|
|
5304
5478
|
function readProvisionProfile(profilePath) {
|
|
5305
5479
|
let raw;
|
|
5306
5480
|
try {
|
|
5307
|
-
const json = extractProfileJson(
|
|
5481
|
+
const json = extractProfileJson(fs37.readFileSync(profilePath));
|
|
5308
5482
|
if (!json) return null;
|
|
5309
5483
|
raw = JSON.parse(json);
|
|
5310
5484
|
} catch {
|
|
@@ -5333,7 +5507,7 @@ function parseCertificate(pem) {
|
|
|
5333
5507
|
}
|
|
5334
5508
|
function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
5335
5509
|
try {
|
|
5336
|
-
const der = forge.util.createBuffer(
|
|
5510
|
+
const der = forge.util.createBuffer(fs37.readFileSync(p12Path).toString("binary"));
|
|
5337
5511
|
const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
|
|
5338
5512
|
const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
|
|
5339
5513
|
const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
|
|
@@ -5349,7 +5523,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
|
5349
5523
|
}
|
|
5350
5524
|
}
|
|
5351
5525
|
function verifySigningMaterial(opts) {
|
|
5352
|
-
const pems =
|
|
5526
|
+
const pems = fs37.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
5353
5527
|
const chain = (pems ?? []).map(parseCertificate);
|
|
5354
5528
|
const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
|
|
5355
5529
|
if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
|
|
@@ -5367,15 +5541,15 @@ function verifySigningMaterial(opts) {
|
|
|
5367
5541
|
}
|
|
5368
5542
|
|
|
5369
5543
|
// src/harmonyos/signing/autoSign.ts
|
|
5370
|
-
import * as
|
|
5371
|
-
import * as
|
|
5372
|
-
import * as
|
|
5544
|
+
import * as crypto10 from "crypto";
|
|
5545
|
+
import * as fs42 from "fs";
|
|
5546
|
+
import * as path41 from "path";
|
|
5373
5547
|
import JSON57 from "json5";
|
|
5374
5548
|
|
|
5375
5549
|
// src/harmonyos/signing/agc.ts
|
|
5376
|
-
import * as
|
|
5377
|
-
import * as
|
|
5378
|
-
import * as
|
|
5550
|
+
import * as crypto7 from "crypto";
|
|
5551
|
+
import * as fs38 from "fs";
|
|
5552
|
+
import * as path36 from "path";
|
|
5379
5553
|
var ENDPOINTS = {
|
|
5380
5554
|
CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
|
|
5381
5555
|
CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
|
|
@@ -5423,7 +5597,7 @@ function createAgcClient(http3, auth) {
|
|
|
5423
5597
|
}
|
|
5424
5598
|
},
|
|
5425
5599
|
async addDevice(udid, deviceKind) {
|
|
5426
|
-
const deviceName = `oniro_app_device_${
|
|
5600
|
+
const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
|
|
5427
5601
|
await call("device", "post", ENDPOINTS.DEVICE_ADD, {
|
|
5428
5602
|
body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
|
|
5429
5603
|
});
|
|
@@ -5457,12 +5631,12 @@ function createAgcClient(http3, auth) {
|
|
|
5457
5631
|
const target = urlsInfo?.[0];
|
|
5458
5632
|
if (!target) throw mapAgcError(operation, 200, "", "");
|
|
5459
5633
|
const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
|
|
5460
|
-
const actual =
|
|
5634
|
+
const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
|
|
5461
5635
|
if (target.sha256 && actual !== target.sha256.toLowerCase()) {
|
|
5462
5636
|
throw new ChecksumMismatchError(target.sha256, actual);
|
|
5463
5637
|
}
|
|
5464
|
-
|
|
5465
|
-
|
|
5638
|
+
fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
|
|
5639
|
+
fs38.writeFileSync(dest, buffer, { mode: 384 });
|
|
5466
5640
|
}
|
|
5467
5641
|
};
|
|
5468
5642
|
}
|
|
@@ -5476,21 +5650,31 @@ function parseDeviceKind(stdout) {
|
|
|
5476
5650
|
}
|
|
5477
5651
|
async function collectLocalDevices(config, logger) {
|
|
5478
5652
|
const devices = [];
|
|
5653
|
+
const skipped = [];
|
|
5479
5654
|
for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
|
|
5480
|
-
|
|
5655
|
+
const skip = (why) => {
|
|
5656
|
+
logger.warn(`[harmonyos] ${target.serial} ${why}`);
|
|
5657
|
+
skipped.push(target.serial);
|
|
5658
|
+
};
|
|
5659
|
+
if (!isUsableDevice(target)) {
|
|
5660
|
+
skip(
|
|
5661
|
+
target.status === "Unauthorized" ? "is unauthorized; accept the debugging prompt on the device to register it." : `is ${target.status.toLowerCase()}; skipping it.`
|
|
5662
|
+
);
|
|
5663
|
+
continue;
|
|
5664
|
+
}
|
|
5481
5665
|
const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
|
|
5482
5666
|
try {
|
|
5483
5667
|
const udid = parseUdid(await shell2("bm", "get", "-u"));
|
|
5484
5668
|
if (!udid) {
|
|
5485
|
-
|
|
5669
|
+
skip("did not report a UDID (`bm get -u`), so it cannot be registered.");
|
|
5486
5670
|
continue;
|
|
5487
5671
|
}
|
|
5488
5672
|
devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
|
|
5489
5673
|
} catch (err) {
|
|
5490
|
-
|
|
5674
|
+
skip(`could not be read: ${err.message}`);
|
|
5491
5675
|
}
|
|
5492
5676
|
}
|
|
5493
|
-
return devices;
|
|
5677
|
+
return { devices, skipped };
|
|
5494
5678
|
}
|
|
5495
5679
|
async function registerDevices(agc, connected, logger) {
|
|
5496
5680
|
let devices = await agc.listDevices();
|
|
@@ -5503,32 +5687,27 @@ async function registerDevices(agc, connected, logger) {
|
|
|
5503
5687
|
if (unregistered.length > 0) devices = await agc.listDevices();
|
|
5504
5688
|
const deviceIds = devices.map((d) => d.id);
|
|
5505
5689
|
if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
|
|
5506
|
-
if (connected.length === 0) {
|
|
5507
|
-
logger.warn(
|
|
5508
|
-
`[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
|
|
5509
|
-
);
|
|
5510
|
-
}
|
|
5511
5690
|
return deviceIds;
|
|
5512
5691
|
}
|
|
5513
5692
|
|
|
5514
5693
|
// src/harmonyos/signing/keystore.ts
|
|
5515
|
-
import * as
|
|
5516
|
-
import * as
|
|
5517
|
-
import * as
|
|
5694
|
+
import * as crypto8 from "crypto";
|
|
5695
|
+
import * as fs40 from "fs";
|
|
5696
|
+
import * as path38 from "path";
|
|
5518
5697
|
|
|
5519
5698
|
// src/harmonyos/java.ts
|
|
5520
|
-
import * as
|
|
5521
|
-
import * as
|
|
5699
|
+
import * as fs39 from "fs";
|
|
5700
|
+
import * as path37 from "path";
|
|
5522
5701
|
var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
|
|
5523
5702
|
function findJava(sdk, env = process.env) {
|
|
5524
5703
|
const candidates = [
|
|
5525
|
-
env.JAVA_HOME &&
|
|
5526
|
-
sdk &&
|
|
5704
|
+
env.JAVA_HOME && path37.join(env.JAVA_HOME, "bin", JAVA_BIN),
|
|
5705
|
+
sdk && path37.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
|
|
5527
5706
|
// macOS: the JBR is itself a bundle inside Studio's Contents.
|
|
5528
|
-
sdk &&
|
|
5529
|
-
...(env.PATH ?? "").split(
|
|
5707
|
+
sdk && path37.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
|
|
5708
|
+
...(env.PATH ?? "").split(path37.delimiter).map((dir) => dir && path37.join(dir, JAVA_BIN))
|
|
5530
5709
|
];
|
|
5531
|
-
const found = candidates.find((c) => !!c &&
|
|
5710
|
+
const found = candidates.find((c) => !!c && fs39.existsSync(c));
|
|
5532
5711
|
if (found) return found;
|
|
5533
5712
|
throw new OniroError(
|
|
5534
5713
|
"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."
|
|
@@ -5541,49 +5720,77 @@ var SIGN_ALG = "SHA256withECDSA";
|
|
|
5541
5720
|
function redactArgs(args) {
|
|
5542
5721
|
return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
|
|
5543
5722
|
}
|
|
5544
|
-
|
|
5545
|
-
const jar =
|
|
5546
|
-
if (!
|
|
5723
|
+
function hapSignToolJar(sdk) {
|
|
5724
|
+
const jar = path38.join(sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
|
|
5725
|
+
if (!fs40.existsSync(jar)) {
|
|
5547
5726
|
throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
|
|
5548
5727
|
}
|
|
5728
|
+
return jar;
|
|
5729
|
+
}
|
|
5730
|
+
async function runHapSignTool(opts) {
|
|
5731
|
+
const jar = hapSignToolJar(opts.sdk);
|
|
5549
5732
|
const java = findJava(opts.sdk);
|
|
5550
|
-
const
|
|
5551
|
-
|
|
5552
|
-
const
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
if (result.code !== 0) {
|
|
5557
|
-
const detail = (result.stderr || result.stdout || "").trim();
|
|
5558
|
-
throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
|
|
5733
|
+
const argv = ["-jar", jar, ...opts.args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", opts.keyPwd, "-keyPwd", opts.keyPwd];
|
|
5734
|
+
opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
|
|
5735
|
+
const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
|
|
5736
|
+
if (result.code !== 0) {
|
|
5737
|
+
const detail = (result.stderr || result.stdout || "").trim();
|
|
5738
|
+
throw new OniroError(`hap-sign-tool failed to ${opts.what} (exit ${result.code}).${detail ? `
|
|
5559
5739
|
${detail}` : ""}`);
|
|
5560
|
-
|
|
5561
|
-
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
async function generateKeystoreAndCsr(opts) {
|
|
5743
|
+
hapSignToolJar(opts.sdk);
|
|
5744
|
+
findJava(opts.sdk);
|
|
5745
|
+
const keyPwd = crypto8.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
|
|
5746
|
+
fs40.mkdirSync(path38.dirname(opts.p12Path), { recursive: true, mode: 448 });
|
|
5747
|
+
const run = (what, args) => runHapSignTool({ ...opts, what, args, keyPwd });
|
|
5562
5748
|
opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
|
|
5563
5749
|
await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
|
|
5564
5750
|
await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
|
|
5565
|
-
return { keyPwd, csr:
|
|
5751
|
+
return { keyPwd, csr: fs40.readFileSync(opts.csrPath, "utf8") };
|
|
5752
|
+
}
|
|
5753
|
+
async function signHapFile(opts) {
|
|
5754
|
+
await runHapSignTool({
|
|
5755
|
+
...opts,
|
|
5756
|
+
what: `sign ${opts.inFile}`,
|
|
5757
|
+
args: [
|
|
5758
|
+
"sign-app",
|
|
5759
|
+
"-mode",
|
|
5760
|
+
"localSign",
|
|
5761
|
+
"-signAlg",
|
|
5762
|
+
SIGN_ALG,
|
|
5763
|
+
"-appCertFile",
|
|
5764
|
+
opts.cerPath,
|
|
5765
|
+
"-profileFile",
|
|
5766
|
+
opts.profilePath,
|
|
5767
|
+
"-inFile",
|
|
5768
|
+
opts.inFile,
|
|
5769
|
+
"-outFile",
|
|
5770
|
+
opts.outFile
|
|
5771
|
+
]
|
|
5772
|
+
});
|
|
5566
5773
|
}
|
|
5567
5774
|
|
|
5568
5775
|
// src/harmonyos/signing/paths.ts
|
|
5569
|
-
import * as
|
|
5570
|
-
import * as
|
|
5776
|
+
import * as crypto9 from "crypto";
|
|
5777
|
+
import * as path39 from "path";
|
|
5571
5778
|
function sanitizeNamePart(raw) {
|
|
5572
5779
|
const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
|
|
5573
5780
|
return safe.slice(0, 64) || "default";
|
|
5574
5781
|
}
|
|
5575
5782
|
function resolveSigningMaterialPaths(config, productName, projectRoot) {
|
|
5576
|
-
const hash =
|
|
5577
|
-
const base =
|
|
5783
|
+
const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
|
|
5784
|
+
const base = path39.join(
|
|
5578
5785
|
config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
|
|
5579
|
-
`${sanitizeNamePart(productName)}_${
|
|
5786
|
+
`${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
|
|
5580
5787
|
);
|
|
5581
5788
|
return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
|
|
5582
5789
|
}
|
|
5583
5790
|
|
|
5584
5791
|
// src/harmonyos/signing/project.ts
|
|
5585
|
-
import * as
|
|
5586
|
-
import * as
|
|
5792
|
+
import * as fs41 from "fs";
|
|
5793
|
+
import * as path40 from "path";
|
|
5587
5794
|
import JSON56 from "json5";
|
|
5588
5795
|
|
|
5589
5796
|
// src/harmonyos/signing/aclPermissionList.ts
|
|
@@ -5651,9 +5858,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
|
|
|
5651
5858
|
|
|
5652
5859
|
// src/harmonyos/signing/project.ts
|
|
5653
5860
|
function readRequestedPermissions(moduleConfigPath) {
|
|
5654
|
-
if (!
|
|
5861
|
+
if (!fs41.existsSync(moduleConfigPath)) return [];
|
|
5655
5862
|
try {
|
|
5656
|
-
const parsed = JSON56.parse(
|
|
5863
|
+
const parsed = JSON56.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
|
|
5657
5864
|
return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
5658
5865
|
} catch {
|
|
5659
5866
|
return [];
|
|
@@ -5662,32 +5869,38 @@ function readRequestedPermissions(moduleConfigPath) {
|
|
|
5662
5869
|
function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
5663
5870
|
const requested = /* @__PURE__ */ new Set();
|
|
5664
5871
|
for (const module of listModules({ projectDir })) {
|
|
5665
|
-
const moduleRoot =
|
|
5872
|
+
const moduleRoot = path40.resolve(projectDir, module.srcPath);
|
|
5666
5873
|
for (const sourceSet of ["main", "ohosTest"]) {
|
|
5667
5874
|
for (const name of readRequestedPermissions(
|
|
5668
|
-
|
|
5875
|
+
path40.join(moduleRoot, "src", sourceSet, "module.json5")
|
|
5669
5876
|
)) {
|
|
5670
5877
|
requested.add(name);
|
|
5671
5878
|
}
|
|
5672
5879
|
}
|
|
5673
5880
|
}
|
|
5674
|
-
const acls =
|
|
5881
|
+
const acls = aclPermissionsAmong(requested);
|
|
5675
5882
|
if (acls.length > 0) {
|
|
5676
5883
|
logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
|
|
5677
5884
|
}
|
|
5678
5885
|
return acls;
|
|
5679
5886
|
}
|
|
5887
|
+
function aclPermissionsAmong(names) {
|
|
5888
|
+
return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
|
|
5889
|
+
}
|
|
5680
5890
|
|
|
5681
5891
|
// src/harmonyos/signing/autoSign.ts
|
|
5682
5892
|
function shouldRegenerate(input) {
|
|
5683
5893
|
if (input.force) return "--force was passed";
|
|
5684
|
-
const missing = Object.values(input.paths).find((file) => !
|
|
5894
|
+
const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
|
|
5685
5895
|
if (missing) return `${missing} is missing`;
|
|
5686
5896
|
const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
|
|
5687
5897
|
if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
|
|
5688
5898
|
return "build-profile.json5 has no signingConfig for this material";
|
|
5689
5899
|
}
|
|
5690
|
-
|
|
5900
|
+
return profileProblem({ ...input, profilePath: input.paths.profilePath });
|
|
5901
|
+
}
|
|
5902
|
+
function profileProblem(input) {
|
|
5903
|
+
const profile = readProvisionProfile(input.profilePath);
|
|
5691
5904
|
if (!profile) return "the provisioning profile could not be read";
|
|
5692
5905
|
if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
|
|
5693
5906
|
if (profile.developerId && profile.developerId !== input.teamId) {
|
|
@@ -5720,31 +5933,29 @@ function writeSigningConfig(opts) {
|
|
|
5720
5933
|
for (const product of app.products ?? []) {
|
|
5721
5934
|
if (product.name === opts.productName) product.signingConfig = opts.productName;
|
|
5722
5935
|
}
|
|
5723
|
-
|
|
5936
|
+
fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
|
|
5724
5937
|
}
|
|
5725
5938
|
async function harmonyOsAutoSign(opts) {
|
|
5726
5939
|
const logger = opts.logger ?? noopLogger;
|
|
5727
5940
|
const productName = opts.productName ?? "default";
|
|
5728
|
-
const projectDir =
|
|
5729
|
-
const buildProfilePath =
|
|
5941
|
+
const projectDir = path41.resolve(opts.projectDir);
|
|
5942
|
+
const buildProfilePath = path41.join(projectDir, "build-profile.json5");
|
|
5730
5943
|
let buildProfile;
|
|
5731
5944
|
try {
|
|
5732
|
-
buildProfile = JSON57.parse(
|
|
5945
|
+
buildProfile = JSON57.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
|
|
5733
5946
|
} catch (err) {
|
|
5734
5947
|
throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
|
|
5735
5948
|
}
|
|
5736
5949
|
const product = buildProfile.app?.products?.find((p) => p.name === productName);
|
|
5737
5950
|
if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
|
|
5738
5951
|
const bundleName = product.bundleName || getBundleName(projectDir);
|
|
5739
|
-
|
|
5740
|
-
throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
|
|
5741
|
-
}
|
|
5952
|
+
assertAgcBundleName(bundleName);
|
|
5742
5953
|
const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
|
|
5743
5954
|
const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
|
|
5744
5955
|
const aclPermissions = collectAclPermissions(projectDir, logger);
|
|
5745
5956
|
const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
|
|
5746
5957
|
logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
|
|
5747
|
-
const
|
|
5958
|
+
const local = await collectLocalDevices(opts.config, logger);
|
|
5748
5959
|
const result = (regenerated) => ({
|
|
5749
5960
|
bundleName,
|
|
5750
5961
|
teamId: auth.teamId,
|
|
@@ -5758,7 +5969,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5758
5969
|
productName,
|
|
5759
5970
|
bundleName,
|
|
5760
5971
|
teamId: auth.teamId,
|
|
5761
|
-
connectedUdids:
|
|
5972
|
+
connectedUdids: local.devices.map((d) => d.udid),
|
|
5762
5973
|
aclPermissions,
|
|
5763
5974
|
force: opts.force
|
|
5764
5975
|
});
|
|
@@ -5767,10 +5978,47 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5767
5978
|
return result(false);
|
|
5768
5979
|
}
|
|
5769
5980
|
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
5770
|
-
const
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5981
|
+
const keyPwd = await issueSigningMaterial({
|
|
5982
|
+
agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
|
|
5983
|
+
local,
|
|
5984
|
+
sdk,
|
|
5985
|
+
paths,
|
|
5986
|
+
bundleName,
|
|
5987
|
+
aclPermissions,
|
|
5988
|
+
teamId: auth.teamId,
|
|
5989
|
+
logger
|
|
5990
|
+
});
|
|
5991
|
+
const encryptedPassword = encryptPwd(keyPwd, sharedMaterialDir(paths));
|
|
5992
|
+
writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword });
|
|
5993
|
+
logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
|
|
5994
|
+
return result(true);
|
|
5995
|
+
}
|
|
5996
|
+
function assertAgcBundleName(bundleName) {
|
|
5997
|
+
if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
|
|
5998
|
+
throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
function materialDirFor(paths) {
|
|
6002
|
+
return path41.join(path41.dirname(paths.p12Path), "material");
|
|
6003
|
+
}
|
|
6004
|
+
function sharedMaterialDir(paths) {
|
|
6005
|
+
const materialDir = materialDirFor(paths);
|
|
6006
|
+
try {
|
|
6007
|
+
getKey(materialDir);
|
|
6008
|
+
} catch {
|
|
6009
|
+
createMaterial(materialDir);
|
|
6010
|
+
}
|
|
6011
|
+
return materialDir;
|
|
6012
|
+
}
|
|
6013
|
+
async function issueSigningMaterial(opts) {
|
|
6014
|
+
const { agc, local, sdk, paths, logger } = opts;
|
|
6015
|
+
const deviceIds = await registerDevices(agc, local.devices, logger);
|
|
6016
|
+
if (local.devices.length === 0) {
|
|
6017
|
+
const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
|
|
6018
|
+
logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
|
|
6019
|
+
}
|
|
6020
|
+
for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
|
|
6021
|
+
const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
|
|
5774
6022
|
const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
|
|
5775
6023
|
if (stale) {
|
|
5776
6024
|
logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
|
|
@@ -5784,11 +6032,11 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5784
6032
|
await agc.download("cert", cert.certObjectId, paths.cerPath);
|
|
5785
6033
|
logger.info("[harmonyos] Issuing the debug provisioning profile...");
|
|
5786
6034
|
const profile = await agc.addDebugProfile({
|
|
5787
|
-
provisionName: `oniro_${
|
|
5788
|
-
bundleName,
|
|
6035
|
+
provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
|
|
6036
|
+
bundleName: opts.bundleName,
|
|
5789
6037
|
certId: cert.id,
|
|
5790
6038
|
deviceIds,
|
|
5791
|
-
aclPermissions
|
|
6039
|
+
aclPermissions: opts.aclPermissions
|
|
5792
6040
|
});
|
|
5793
6041
|
try {
|
|
5794
6042
|
await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
|
|
@@ -5800,22 +6048,86 @@ async function harmonyOsAutoSign(opts) {
|
|
|
5800
6048
|
try {
|
|
5801
6049
|
verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
|
|
5802
6050
|
} catch (err) {
|
|
5803
|
-
|
|
6051
|
+
fs42.rmSync(paths.profilePath, { force: true });
|
|
5804
6052
|
throw err;
|
|
5805
6053
|
}
|
|
5806
|
-
|
|
6054
|
+
return keyPwd;
|
|
6055
|
+
}
|
|
6056
|
+
|
|
6057
|
+
// src/harmonyos/signing/signHap.ts
|
|
6058
|
+
import * as fs43 from "fs";
|
|
6059
|
+
import * as path42 from "path";
|
|
6060
|
+
function resolveHapSigningMaterialPaths(config, bundleName) {
|
|
6061
|
+
const paths = resolveSigningMaterialPaths(config, "hap", bundleName);
|
|
6062
|
+
return { ...paths, pwdPath: paths.p12Path.replace(/\.p12$/, ".pwd") };
|
|
6063
|
+
}
|
|
6064
|
+
function readStoredPassword(paths) {
|
|
5807
6065
|
try {
|
|
5808
|
-
|
|
6066
|
+
return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
|
|
5809
6067
|
} catch {
|
|
5810
|
-
|
|
6068
|
+
return null;
|
|
5811
6069
|
}
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
6070
|
+
}
|
|
6071
|
+
async function harmonyOsSignHap(opts) {
|
|
6072
|
+
const logger = opts.logger ?? noopLogger;
|
|
6073
|
+
const hapPath = path42.resolve(opts.hapPath);
|
|
6074
|
+
const outPath = path42.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
|
|
6075
|
+
const info = await readHapSigningInfo(hapPath);
|
|
6076
|
+
const { bundleName } = info;
|
|
6077
|
+
if (info.compileSdkType === "OpenHarmony") {
|
|
6078
|
+
throw new OniroError(
|
|
6079
|
+
`${hapPath} was built for OpenHarmony, so a HarmonyOS device would not run it; sign it without --harmonyos.`
|
|
6080
|
+
);
|
|
6081
|
+
}
|
|
6082
|
+
assertAgcBundleName(bundleName);
|
|
6083
|
+
const aclPermissions = aclPermissionsAmong(info.requestedPermissions);
|
|
6084
|
+
if (aclPermissions.length > 0) {
|
|
6085
|
+
logger.info(`[harmonyos] HAP requests ${aclPermissions.length} ACL permission(s): ${aclPermissions.join(", ")}`);
|
|
6086
|
+
}
|
|
6087
|
+
const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
|
|
6088
|
+
const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
|
|
6089
|
+
const paths = resolveHapSigningMaterialPaths(opts.config, bundleName);
|
|
6090
|
+
const { pwdPath, ...material } = paths;
|
|
6091
|
+
logger.info(`[harmonyos] Signing ${path42.basename(hapPath)} as ${bundleName} (team ${auth.teamId}).`);
|
|
6092
|
+
const local = await collectLocalDevices(opts.config, logger);
|
|
6093
|
+
let keyPwd = opts.force ? null : readStoredPassword(paths);
|
|
6094
|
+
const missing = Object.values(material).find((file) => !fs43.existsSync(file));
|
|
6095
|
+
const reason = opts.force ? "--force was passed" : missing ? `${missing} is missing` : keyPwd === null ? "the stored keystore password could not be read" : profileProblem({
|
|
6096
|
+
profilePath: material.profilePath,
|
|
6097
|
+
bundleName,
|
|
6098
|
+
teamId: auth.teamId,
|
|
6099
|
+
connectedUdids: local.devices.map((d) => d.udid),
|
|
6100
|
+
aclPermissions
|
|
6101
|
+
});
|
|
6102
|
+
if (!reason && keyPwd !== null) {
|
|
6103
|
+
logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
|
|
6104
|
+
} else {
|
|
6105
|
+
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
6106
|
+
fs43.rmSync(pwdPath, { force: true });
|
|
6107
|
+
keyPwd = await issueSigningMaterial({
|
|
6108
|
+
agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
|
|
6109
|
+
local,
|
|
6110
|
+
sdk,
|
|
6111
|
+
paths: material,
|
|
6112
|
+
bundleName,
|
|
6113
|
+
aclPermissions,
|
|
6114
|
+
teamId: auth.teamId,
|
|
6115
|
+
logger
|
|
6116
|
+
});
|
|
6117
|
+
fs43.writeFileSync(pwdPath, encryptPwd(keyPwd, sharedMaterialDir(material)), { mode: 384 });
|
|
6118
|
+
}
|
|
6119
|
+
const password = keyPwd;
|
|
6120
|
+
await writeHapAtomically(
|
|
6121
|
+
outPath,
|
|
6122
|
+
(tmpPath) => signHapFile({ sdk, inFile: hapPath, outFile: tmpPath, ...material, keyPwd: password, logger })
|
|
6123
|
+
);
|
|
6124
|
+
const deviceIds = readProvisionProfile(material.profilePath)?.deviceIds ?? [];
|
|
6125
|
+
logger.info(`[harmonyos] Wrote ${outPath}.`);
|
|
6126
|
+
return { bundleName, teamId: auth.teamId, paths: material, regenerated: !!reason, deviceIds, outPath };
|
|
5815
6127
|
}
|
|
5816
6128
|
|
|
5817
6129
|
// src/index.ts
|
|
5818
|
-
var VERSION = "0.
|
|
6130
|
+
var VERSION = "0.16.0";
|
|
5819
6131
|
export {
|
|
5820
6132
|
ALL_SDKS,
|
|
5821
6133
|
APL_VALUES,
|
|
@@ -5832,6 +6144,7 @@ export {
|
|
|
5832
6144
|
OHOS_DAILY_BUILDS_URL,
|
|
5833
6145
|
OHOS_URL_BASE,
|
|
5834
6146
|
OniroError,
|
|
6147
|
+
SIGNED_HAP_CHUNK_SIZE,
|
|
5835
6148
|
SIGNING_ERRORS,
|
|
5836
6149
|
SdkNotInstalledError,
|
|
5837
6150
|
TMP_DIR_HINT,
|
|
@@ -5867,6 +6180,7 @@ export {
|
|
|
5867
6180
|
dedupEntries,
|
|
5868
6181
|
defaultContext,
|
|
5869
6182
|
defaultPaths,
|
|
6183
|
+
defaultSignedHapPath,
|
|
5870
6184
|
describeLayoutNode,
|
|
5871
6185
|
detectAaStartFailure,
|
|
5872
6186
|
detectCmdToolsFlavour,
|
|
@@ -5933,6 +6247,7 @@ export {
|
|
|
5933
6247
|
getSupportedSdksForUi,
|
|
5934
6248
|
harmonyOsAutoSign,
|
|
5935
6249
|
harmonyOsSdkLabelForApi,
|
|
6250
|
+
harmonyOsSignHap,
|
|
5936
6251
|
hdcClientFailure,
|
|
5937
6252
|
hdcExec,
|
|
5938
6253
|
hdcTargetArgs,
|
|
@@ -5950,6 +6265,7 @@ export {
|
|
|
5950
6265
|
isOutOfSpaceError,
|
|
5951
6266
|
isRamBackedPath,
|
|
5952
6267
|
isUnsignedHap,
|
|
6268
|
+
isUsableDevice,
|
|
5953
6269
|
isValidBundleName,
|
|
5954
6270
|
isValidProjectName,
|
|
5955
6271
|
killProcessTree,
|
|
@@ -5985,6 +6301,7 @@ export {
|
|
|
5985
6301
|
prepareSigning,
|
|
5986
6302
|
pruneLayout,
|
|
5987
6303
|
readHapBundleName,
|
|
6304
|
+
readHapSigningInfo,
|
|
5988
6305
|
readJson5File,
|
|
5989
6306
|
readJsonFile,
|
|
5990
6307
|
readProvisionProfile,
|
|
@@ -6012,6 +6329,7 @@ export {
|
|
|
6012
6329
|
setHilogLevel,
|
|
6013
6330
|
shell,
|
|
6014
6331
|
shellChecked,
|
|
6332
|
+
signOpenHarmonyHap,
|
|
6015
6333
|
spawnCompat,
|
|
6016
6334
|
startEmulator,
|
|
6017
6335
|
staticConfig,
|
|
@@ -6031,6 +6349,7 @@ export {
|
|
|
6031
6349
|
waitForTarget,
|
|
6032
6350
|
watchLog,
|
|
6033
6351
|
waypointsToRawEvents,
|
|
6352
|
+
withHapZip,
|
|
6034
6353
|
withoutWindow,
|
|
6035
6354
|
writeJson5File,
|
|
6036
6355
|
writeJsonFile
|