@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.cjs
CHANGED
|
@@ -45,6 +45,7 @@ __export(index_exports, {
|
|
|
45
45
|
OHOS_DAILY_BUILDS_URL: () => OHOS_DAILY_BUILDS_URL,
|
|
46
46
|
OHOS_URL_BASE: () => OHOS_URL_BASE,
|
|
47
47
|
OniroError: () => OniroError,
|
|
48
|
+
SIGNED_HAP_CHUNK_SIZE: () => SIGNED_HAP_CHUNK_SIZE,
|
|
48
49
|
SIGNING_ERRORS: () => SIGNING_ERRORS,
|
|
49
50
|
SdkNotInstalledError: () => SdkNotInstalledError,
|
|
50
51
|
TMP_DIR_HINT: () => TMP_DIR_HINT,
|
|
@@ -80,6 +81,7 @@ __export(index_exports, {
|
|
|
80
81
|
dedupEntries: () => dedupEntries,
|
|
81
82
|
defaultContext: () => defaultContext,
|
|
82
83
|
defaultPaths: () => defaultPaths,
|
|
84
|
+
defaultSignedHapPath: () => defaultSignedHapPath,
|
|
83
85
|
describeLayoutNode: () => describeLayoutNode,
|
|
84
86
|
detectAaStartFailure: () => detectAaStartFailure,
|
|
85
87
|
detectCmdToolsFlavour: () => detectCmdToolsFlavour,
|
|
@@ -146,6 +148,7 @@ __export(index_exports, {
|
|
|
146
148
|
getSupportedSdksForUi: () => getSupportedSdksForUi,
|
|
147
149
|
harmonyOsAutoSign: () => harmonyOsAutoSign,
|
|
148
150
|
harmonyOsSdkLabelForApi: () => harmonyOsSdkLabelForApi,
|
|
151
|
+
harmonyOsSignHap: () => harmonyOsSignHap,
|
|
149
152
|
hdcClientFailure: () => hdcClientFailure,
|
|
150
153
|
hdcExec: () => hdcExec,
|
|
151
154
|
hdcTargetArgs: () => hdcTargetArgs,
|
|
@@ -163,6 +166,7 @@ __export(index_exports, {
|
|
|
163
166
|
isOutOfSpaceError: () => isOutOfSpaceError,
|
|
164
167
|
isRamBackedPath: () => isRamBackedPath,
|
|
165
168
|
isUnsignedHap: () => isUnsignedHap,
|
|
169
|
+
isUsableDevice: () => isUsableDevice,
|
|
166
170
|
isValidBundleName: () => isValidBundleName,
|
|
167
171
|
isValidProjectName: () => isValidProjectName,
|
|
168
172
|
killProcessTree: () => killProcessTree,
|
|
@@ -198,6 +202,7 @@ __export(index_exports, {
|
|
|
198
202
|
prepareSigning: () => prepareSigning,
|
|
199
203
|
pruneLayout: () => pruneLayout,
|
|
200
204
|
readHapBundleName: () => readHapBundleName,
|
|
205
|
+
readHapSigningInfo: () => readHapSigningInfo,
|
|
201
206
|
readJson5File: () => readJson5File,
|
|
202
207
|
readJsonFile: () => readJsonFile,
|
|
203
208
|
readProvisionProfile: () => readProvisionProfile,
|
|
@@ -225,6 +230,7 @@ __export(index_exports, {
|
|
|
225
230
|
setHilogLevel: () => setHilogLevel,
|
|
226
231
|
shell: () => shell,
|
|
227
232
|
shellChecked: () => shellChecked,
|
|
233
|
+
signOpenHarmonyHap: () => signOpenHarmonyHap,
|
|
228
234
|
spawnCompat: () => spawnCompat,
|
|
229
235
|
startEmulator: () => startEmulator,
|
|
230
236
|
staticConfig: () => staticConfig,
|
|
@@ -244,6 +250,7 @@ __export(index_exports, {
|
|
|
244
250
|
waitForTarget: () => waitForTarget,
|
|
245
251
|
watchLog: () => watchLog,
|
|
246
252
|
waypointsToRawEvents: () => waypointsToRawEvents,
|
|
253
|
+
withHapZip: () => withHapZip,
|
|
247
254
|
withoutWindow: () => withoutWindow,
|
|
248
255
|
writeJson5File: () => writeJson5File,
|
|
249
256
|
writeJsonFile: () => writeJsonFile
|
|
@@ -833,14 +840,14 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
|
|
|
833
840
|
async function downloadFile(opts) {
|
|
834
841
|
const { url, dest, progress, abortSignal } = opts;
|
|
835
842
|
const proto = url.startsWith("https") ? https : http;
|
|
836
|
-
return new Promise((
|
|
843
|
+
return new Promise((resolve9, reject) => {
|
|
837
844
|
const s = Math.max(0, Math.min(100, opts.start ?? 0));
|
|
838
845
|
const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
|
|
839
846
|
let settled = false;
|
|
840
847
|
const done = (err) => {
|
|
841
848
|
if (settled) return;
|
|
842
849
|
settled = true;
|
|
843
|
-
err ? reject(err) :
|
|
850
|
+
err ? reject(err) : resolve9();
|
|
844
851
|
};
|
|
845
852
|
if (abortSignal?.aborted) {
|
|
846
853
|
done(new CancelledError("Download cancelled before start."));
|
|
@@ -1018,15 +1025,26 @@ async function extractZipWithProgress(opts) {
|
|
|
1018
1025
|
async function extractTarball(tarPath, dest, strip = 0, filter) {
|
|
1019
1026
|
await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
|
|
1020
1027
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1028
|
+
var SIGNED_HAP_CHUNK_SIZE = 128 * 1024;
|
|
1029
|
+
async function withHapZip(zipPath, use) {
|
|
1030
|
+
const zip = new import_node_stream_zip.default({ file: zipPath, chunkSize: SIGNED_HAP_CHUNK_SIZE, storeEntries: true });
|
|
1031
|
+
const close = () => new Promise((resolve9) => zip.close(() => resolve9()));
|
|
1023
1032
|
try {
|
|
1024
|
-
|
|
1025
|
-
|
|
1033
|
+
await new Promise((resolve9, reject) => {
|
|
1034
|
+
zip.once("ready", () => resolve9());
|
|
1035
|
+
zip.once("error", (err) => reject(err));
|
|
1036
|
+
});
|
|
1037
|
+
return await use({ entries: () => zip.entries(), entryData: (name) => zip.entryDataSync(name) });
|
|
1026
1038
|
} finally {
|
|
1027
|
-
await
|
|
1039
|
+
await close();
|
|
1028
1040
|
}
|
|
1029
1041
|
}
|
|
1042
|
+
function readZipEntry(zipPath, match) {
|
|
1043
|
+
return withHapZip(zipPath, (zip) => {
|
|
1044
|
+
const entry = Object.values(zip.entries()).find((e) => !e.isDirectory && match(e.name));
|
|
1045
|
+
return entry ? zip.entryData(entry.name) : void 0;
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1030
1048
|
|
|
1031
1049
|
// src/sdk/list.ts
|
|
1032
1050
|
var fs7 = __toESM(require("fs"), 1);
|
|
@@ -1677,9 +1695,16 @@ var SIGNING_KIND_CONFIG = {
|
|
|
1677
1695
|
certBasename: "OpenHarmonyApplication.cer"
|
|
1678
1696
|
}
|
|
1679
1697
|
};
|
|
1680
|
-
function
|
|
1681
|
-
|
|
1682
|
-
|
|
1698
|
+
function sdkSigningFiles(sdkPath) {
|
|
1699
|
+
return {
|
|
1700
|
+
signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1701
|
+
keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1702
|
+
profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1703
|
+
unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
function copySigningFiles(signaturesDir, paths, kind, logger, applicationCertPath) {
|
|
1707
|
+
logger.info(`[sign] Copying signing material into ${signaturesDir}...`);
|
|
1683
1708
|
fs13.mkdirSync(signaturesDir, { recursive: true });
|
|
1684
1709
|
fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
|
|
1685
1710
|
fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
|
|
@@ -1706,10 +1731,7 @@ function extractLeafCertificate(chainPath) {
|
|
|
1706
1731
|
return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
|
|
1707
1732
|
}
|
|
1708
1733
|
function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
|
|
1709
|
-
const kind = options.kind ?? pickSigningKind(apl);
|
|
1710
|
-
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
1711
1734
|
const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
|
|
1712
|
-
const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
|
|
1713
1735
|
if (!fs13.existsSync(appJsonPath)) {
|
|
1714
1736
|
throw new OniroError(`${appJsonPath} does not exist.`);
|
|
1715
1737
|
}
|
|
@@ -1719,26 +1741,28 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1719
1741
|
} catch (e) {
|
|
1720
1742
|
throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
|
|
1721
1743
|
}
|
|
1744
|
+
if (!appJson.app?.bundleName) {
|
|
1745
|
+
throw new OniroError("app.json5 does not contain app.bundleName.");
|
|
1746
|
+
}
|
|
1747
|
+
applyProfileTemplate(path14.join(projectDir, "signatures"), appJson.app.bundleName, apl, appFeature, logger, options);
|
|
1748
|
+
}
|
|
1749
|
+
function applyProfileTemplate(signaturesDir, bundleName, apl, appFeature, logger, options = {}) {
|
|
1750
|
+
const kind = options.kind ?? pickSigningKind(apl);
|
|
1751
|
+
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
1752
|
+
const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1722
1753
|
let profileTemplate;
|
|
1723
1754
|
try {
|
|
1724
1755
|
profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
|
|
1725
1756
|
} catch (e) {
|
|
1726
1757
|
throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
|
|
1727
1758
|
}
|
|
1728
|
-
if (!appJson.app?.bundleName) {
|
|
1729
|
-
throw new OniroError("app.json5 does not contain app.bundleName.");
|
|
1730
|
-
}
|
|
1731
1759
|
if (!profileTemplate["bundle-info"]) {
|
|
1732
1760
|
throw new OniroError("UnsgnedReleasedProfileTemplate.json is missing the bundle-info section.");
|
|
1733
1761
|
}
|
|
1734
|
-
profileTemplate["bundle-info"]["bundle-name"] =
|
|
1762
|
+
profileTemplate["bundle-info"]["bundle-name"] = bundleName;
|
|
1735
1763
|
profileTemplate["bundle-info"]["apl"] = apl;
|
|
1736
1764
|
profileTemplate["bundle-info"]["app-feature"] = appFeature;
|
|
1737
|
-
const distCertChainPath = path14.join(
|
|
1738
|
-
projectDir,
|
|
1739
|
-
"signatures",
|
|
1740
|
-
SIGNING_KIND_CONFIG[kind].certBasename
|
|
1741
|
-
);
|
|
1765
|
+
const distCertChainPath = path14.join(signaturesDir, SIGNING_KIND_CONFIG[kind].certBasename);
|
|
1742
1766
|
profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
|
|
1743
1767
|
if (options.acls !== void 0) {
|
|
1744
1768
|
profileTemplate.acls = profileTemplate.acls ?? {};
|
|
@@ -1749,9 +1773,8 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1749
1773
|
}
|
|
1750
1774
|
fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
|
|
1751
1775
|
}
|
|
1752
|
-
function generateP7bFile(
|
|
1776
|
+
function generateP7bFile(signaturesDir, paths, logger, passwords) {
|
|
1753
1777
|
logger.info("[sign] Generating P7b profile via hap-sign-tool...");
|
|
1754
|
-
const signaturesDir = path14.join(projectDir, "signatures");
|
|
1755
1778
|
const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1756
1779
|
const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
|
|
1757
1780
|
const args = [
|
|
@@ -1875,30 +1898,201 @@ function generateSigningConfigs(options) {
|
|
|
1875
1898
|
if (!fs13.existsSync(sdkPath)) {
|
|
1876
1899
|
throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
|
|
1877
1900
|
}
|
|
1878
|
-
const paths =
|
|
1879
|
-
signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1880
|
-
keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1881
|
-
profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1882
|
-
unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1883
|
-
};
|
|
1901
|
+
const paths = sdkSigningFiles(sdkPath);
|
|
1884
1902
|
const kind = pickSigningKind(apl);
|
|
1885
1903
|
const passwords = options.passwords ?? DEFAULT_PASSWORDS;
|
|
1886
1904
|
logger.info("[sign] Starting signing configuration generation...");
|
|
1887
|
-
|
|
1905
|
+
const signaturesDir = path14.join(projectDir, "signatures");
|
|
1906
|
+
copySigningFiles(signaturesDir, paths, kind, logger, options.applicationCertPath);
|
|
1888
1907
|
modifyProfileTemplate(projectDir, apl, appFeature, logger, {
|
|
1889
1908
|
kind,
|
|
1890
1909
|
acls: options.acls,
|
|
1891
1910
|
appPrivilegeCapabilities: options.appPrivilegeCapabilities
|
|
1892
1911
|
});
|
|
1893
|
-
generateP7bFile(
|
|
1912
|
+
generateP7bFile(signaturesDir, paths, logger, passwords);
|
|
1894
1913
|
prepareMaterialDirectory(projectDir, logger);
|
|
1895
1914
|
updateBuildProfile(projectDir, kind, logger, passwords);
|
|
1896
1915
|
logger.info("[sign] Signing configuration generated successfully.");
|
|
1897
1916
|
}
|
|
1898
1917
|
|
|
1899
|
-
// src/
|
|
1918
|
+
// src/sign/hapInfo.ts
|
|
1919
|
+
var crypto3 = __toESM(require("crypto"), 1);
|
|
1900
1920
|
var fs14 = __toESM(require("fs"), 1);
|
|
1901
1921
|
var path15 = __toESM(require("path"), 1);
|
|
1922
|
+
function parseHapModuleJson(text, hapPath) {
|
|
1923
|
+
let parsed;
|
|
1924
|
+
try {
|
|
1925
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
1926
|
+
} catch (err) {
|
|
1927
|
+
throw new OniroError(`The module.json in ${hapPath} is not valid JSON: ${err.message}`, err);
|
|
1928
|
+
}
|
|
1929
|
+
const bundleName = parsed.app?.bundleName;
|
|
1930
|
+
if (typeof bundleName !== "string" || !bundleName) {
|
|
1931
|
+
throw new OniroError(`The module.json in ${hapPath} names no app.bundleName.`);
|
|
1932
|
+
}
|
|
1933
|
+
const requested = Array.isArray(parsed.module?.requestPermissions) ? parsed.module.requestPermissions : [];
|
|
1934
|
+
const { compileSdkType, targetAPIVersion } = parsed.app ?? {};
|
|
1935
|
+
return {
|
|
1936
|
+
bundleName,
|
|
1937
|
+
requestedPermissions: requested.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0),
|
|
1938
|
+
compileSdkType: typeof compileSdkType === "string" && compileSdkType ? compileSdkType : void 0,
|
|
1939
|
+
targetApiVersion: typeof targetAPIVersion === "number" ? targetAPIVersion : void 0
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
async function readHapSigningInfo(hapPath) {
|
|
1943
|
+
if (!fs14.existsSync(hapPath)) throw new OniroError(`${hapPath} does not exist.`);
|
|
1944
|
+
let moduleJson;
|
|
1945
|
+
try {
|
|
1946
|
+
moduleJson = await readZipEntry(hapPath, (n) => n === "module.json");
|
|
1947
|
+
if (!moduleJson && await readZipEntry(hapPath, (n) => n === "config.json")) {
|
|
1948
|
+
throw new OniroError(
|
|
1949
|
+
`${hapPath} is an FA-model HAP (config.json, no module.json). Only stage-model HAPs can be signed without their project.`
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
} catch (err) {
|
|
1953
|
+
if (err instanceof OniroError) throw err;
|
|
1954
|
+
throw new OniroError(`Could not read ${hapPath} as a HAP: ${err.message}`, err);
|
|
1955
|
+
}
|
|
1956
|
+
if (!moduleJson) throw new OniroError(`${hapPath} holds no module.json, so it is not a HAP.`);
|
|
1957
|
+
return parseHapModuleJson(moduleJson.toString("utf8"), hapPath);
|
|
1958
|
+
}
|
|
1959
|
+
function defaultSignedHapPath(hapPath) {
|
|
1960
|
+
const dir = path15.dirname(hapPath);
|
|
1961
|
+
const name = path15.basename(hapPath);
|
|
1962
|
+
if (/-signed\.hap$/i.test(name)) return path15.join(dir, name.replace(/-signed\.hap$/i, "-resigned.hap"));
|
|
1963
|
+
return path15.join(dir, `${name.replace(/(-unsigned)?\.hap$/i, "")}-signed.hap`);
|
|
1964
|
+
}
|
|
1965
|
+
async function writeHapAtomically(outPath, sign) {
|
|
1966
|
+
const dir = path15.dirname(outPath);
|
|
1967
|
+
try {
|
|
1968
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
1969
|
+
} catch (err) {
|
|
1970
|
+
throw new OniroError(`Cannot create ${dir} for the signed HAP: ${err.message}`, err);
|
|
1971
|
+
}
|
|
1972
|
+
const tmpPath = path15.join(dir, `.${path15.basename(outPath)}.${crypto3.randomBytes(4).toString("hex")}.tmp`);
|
|
1973
|
+
try {
|
|
1974
|
+
await sign(tmpPath);
|
|
1975
|
+
fs14.renameSync(tmpPath, outPath);
|
|
1976
|
+
} finally {
|
|
1977
|
+
fs14.rmSync(tmpPath, { force: true });
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// src/sign/signHap.ts
|
|
1982
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
1983
|
+
var os8 = __toESM(require("os"), 1);
|
|
1984
|
+
var path16 = __toESM(require("path"), 1);
|
|
1985
|
+
var import_node_child_process4 = require("child_process");
|
|
1986
|
+
|
|
1987
|
+
// src/project/validators.ts
|
|
1988
|
+
function isValidProjectName(name) {
|
|
1989
|
+
if (!name) return false;
|
|
1990
|
+
if (name.includes("/") || name.includes("\\")) return false;
|
|
1991
|
+
return /^[A-Za-z0-9._-]+$/.test(name);
|
|
1992
|
+
}
|
|
1993
|
+
function isValidBundleName(bundleName) {
|
|
1994
|
+
return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// src/sign/signHap.ts
|
|
1998
|
+
function pickSigningSdk(sdkHome, targetApiVersion) {
|
|
1999
|
+
let apis;
|
|
2000
|
+
try {
|
|
2001
|
+
apis = fs15.readdirSync(sdkHome).filter((api2) => /^\d+$/.test(api2));
|
|
2002
|
+
} catch {
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
2005
|
+
const usable = apis.filter((api2) => fs15.existsSync(sdkSigningFiles(path16.join(sdkHome, api2)).signTool)).sort((a, b) => Number(b) - Number(a));
|
|
2006
|
+
const api = usable.find((a) => targetApiVersion !== void 0 && Number(a) === targetApiVersion) ?? usable[0];
|
|
2007
|
+
return api ? { api, sdkPath: path16.join(sdkHome, api) } : null;
|
|
2008
|
+
}
|
|
2009
|
+
async function signOpenHarmonyHap(opts) {
|
|
2010
|
+
const logger = opts.logger ?? noopLogger;
|
|
2011
|
+
const hapPath = path16.resolve(opts.hapPath);
|
|
2012
|
+
const outPath = path16.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
|
|
2013
|
+
const apl = opts.apl ?? "normal";
|
|
2014
|
+
if (!APL_VALUES.includes(apl)) {
|
|
2015
|
+
throw new OniroError(`Invalid apl '${apl}'. Expected one of: ${APL_VALUES.join(", ")}.`);
|
|
2016
|
+
}
|
|
2017
|
+
const appFeature = resolveAppFeature(apl, opts.appFeature);
|
|
2018
|
+
if (!APP_FEATURE_VALUES.includes(appFeature)) {
|
|
2019
|
+
throw new OniroError(`Invalid appFeature '${appFeature}'. Expected one of: ${APP_FEATURE_VALUES.join(", ")}.`);
|
|
2020
|
+
}
|
|
2021
|
+
const info = await readHapSigningInfo(hapPath);
|
|
2022
|
+
if (info.compileSdkType === "HarmonyOS") {
|
|
2023
|
+
throw new OniroError(
|
|
2024
|
+
`${hapPath} was built for HarmonyOS, which needs a certificate from AppGallery Connect: run \`oniro-app auth login\` once, then \`oniro-app sign --harmonyos --hap\`.`
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
if (!isValidBundleName(info.bundleName)) {
|
|
2028
|
+
throw new OniroError(`${hapPath} names an invalid bundle name '${info.bundleName}'.`);
|
|
2029
|
+
}
|
|
2030
|
+
const sdk = pickSigningSdk(opts.sdkHome, info.targetApiVersion);
|
|
2031
|
+
if (!sdk) {
|
|
2032
|
+
throw new OniroError(
|
|
2033
|
+
`No OpenHarmony SDK with hap-sign-tool.jar under ${opts.sdkHome}; install one with \`oniro-app sdk install\`.`
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
const files = sdkSigningFiles(sdk.sdkPath);
|
|
2037
|
+
const kind = pickSigningKind(apl);
|
|
2038
|
+
const passwords = opts.passwords ?? DEFAULT_PASSWORDS;
|
|
2039
|
+
logger.info(`[sign] Signing ${path16.basename(hapPath)} as ${info.bundleName} with the API ${sdk.api} SDK's material...`);
|
|
2040
|
+
const signaturesDir = fs15.mkdtempSync(path16.join(os8.tmpdir(), "oniro-sign-hap-"));
|
|
2041
|
+
try {
|
|
2042
|
+
copySigningFiles(signaturesDir, files, kind, logger, opts.applicationCertPath);
|
|
2043
|
+
applyProfileTemplate(signaturesDir, info.bundleName, apl, appFeature, logger, {
|
|
2044
|
+
kind,
|
|
2045
|
+
acls: opts.acls,
|
|
2046
|
+
appPrivilegeCapabilities: opts.appPrivilegeCapabilities
|
|
2047
|
+
});
|
|
2048
|
+
generateP7bFile(signaturesDir, files, logger, passwords);
|
|
2049
|
+
logger.info("[sign] Signing the HAP via hap-sign-tool...");
|
|
2050
|
+
const { hapKeyAlias, certBasename } = SIGNING_KIND_CONFIG[kind];
|
|
2051
|
+
await writeHapAtomically(outPath, (tmpPath) => {
|
|
2052
|
+
const args = [
|
|
2053
|
+
"-jar",
|
|
2054
|
+
files.signTool,
|
|
2055
|
+
"sign-app",
|
|
2056
|
+
"-mode",
|
|
2057
|
+
"localSign",
|
|
2058
|
+
"-keyAlias",
|
|
2059
|
+
hapKeyAlias,
|
|
2060
|
+
"-signAlg",
|
|
2061
|
+
"SHA256withECDSA",
|
|
2062
|
+
"-appCertFile",
|
|
2063
|
+
path16.join(signaturesDir, certBasename),
|
|
2064
|
+
"-profileFile",
|
|
2065
|
+
path16.join(signaturesDir, "app1-profile.p7b"),
|
|
2066
|
+
"-inFile",
|
|
2067
|
+
hapPath,
|
|
2068
|
+
"-outFile",
|
|
2069
|
+
tmpPath,
|
|
2070
|
+
"-keystoreFile",
|
|
2071
|
+
path16.join(signaturesDir, "OpenHarmony.p12"),
|
|
2072
|
+
"-keyPwd",
|
|
2073
|
+
passwords.key,
|
|
2074
|
+
"-keystorePwd",
|
|
2075
|
+
passwords.store
|
|
2076
|
+
];
|
|
2077
|
+
try {
|
|
2078
|
+
(0, import_node_child_process4.execFileSync)("java", args, { stdio: "inherit" });
|
|
2079
|
+
} catch (e) {
|
|
2080
|
+
throw new OniroError(
|
|
2081
|
+
`hap-sign-tool failed to sign ${hapPath}. Ensure a JDK (with java) is on PATH. Underlying error: ${e.message}`,
|
|
2082
|
+
e
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
} finally {
|
|
2087
|
+
fs15.rmSync(signaturesDir, { recursive: true, force: true });
|
|
2088
|
+
}
|
|
2089
|
+
logger.info(`[sign] Wrote ${outPath}.`);
|
|
2090
|
+
return { bundleName: info.bundleName, outPath, sdkApi: sdk.api, kind };
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// src/emulator/install.ts
|
|
2094
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
2095
|
+
var path17 = __toESM(require("path"), 1);
|
|
1902
2096
|
async function installEmulator(opts) {
|
|
1903
2097
|
const { config, progress, abortSignal } = opts;
|
|
1904
2098
|
const logger = opts.logger ?? noopLogger;
|
|
@@ -1908,20 +2102,20 @@ async function installEmulator(opts) {
|
|
|
1908
2102
|
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
1909
2103
|
config,
|
|
1910
2104
|
override: opts.tmpDir,
|
|
1911
|
-
installRoot:
|
|
2105
|
+
installRoot: path17.dirname(emulatorDir),
|
|
1912
2106
|
prefix: "oniro-emulator-",
|
|
1913
2107
|
logger
|
|
1914
2108
|
});
|
|
1915
|
-
const tmpZip =
|
|
2109
|
+
const tmpZip = path17.join(tmpDir, "oniro_emulator.zip");
|
|
1916
2110
|
try {
|
|
1917
|
-
|
|
2111
|
+
fs16.mkdirSync(emulatorDir, { recursive: true });
|
|
1918
2112
|
progress?.report({ message: "Downloading emulator...", increment: 0 });
|
|
1919
2113
|
await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
|
|
1920
2114
|
progress?.report({ message: "Extracting emulator...", increment: 0 });
|
|
1921
2115
|
await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
|
|
1922
|
-
const runSh =
|
|
1923
|
-
if (
|
|
1924
|
-
|
|
2116
|
+
const runSh = path17.join(emulatorDir, "images", "run.sh");
|
|
2117
|
+
if (fs16.existsSync(runSh)) {
|
|
2118
|
+
fs16.chmodSync(runSh, 493);
|
|
1925
2119
|
}
|
|
1926
2120
|
progress?.report({ message: "Finalizing installation...", increment: 5 });
|
|
1927
2121
|
} catch (err) {
|
|
@@ -1931,24 +2125,24 @@ async function installEmulator(opts) {
|
|
|
1931
2125
|
}
|
|
1932
2126
|
}
|
|
1933
2127
|
function isEmulatorInstalled(config) {
|
|
1934
|
-
const imagesDir =
|
|
1935
|
-
return
|
|
2128
|
+
const imagesDir = path17.join(getEmulatorDir(config), "images");
|
|
2129
|
+
return fs16.existsSync(path17.join(imagesDir, "run.sh")) || fs16.existsSync(path17.join(imagesDir, "run.bat"));
|
|
1936
2130
|
}
|
|
1937
2131
|
function removeEmulator(config) {
|
|
1938
2132
|
const emulatorDir = getEmulatorDir(config);
|
|
1939
|
-
if (
|
|
1940
|
-
|
|
2133
|
+
if (fs16.existsSync(emulatorDir)) {
|
|
2134
|
+
fs16.rmSync(emulatorDir, { recursive: true, force: true });
|
|
1941
2135
|
}
|
|
1942
2136
|
}
|
|
1943
2137
|
|
|
1944
2138
|
// src/emulator/lifecycle.ts
|
|
1945
|
-
var
|
|
1946
|
-
var
|
|
1947
|
-
var
|
|
1948
|
-
var
|
|
2139
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
2140
|
+
var os9 = __toESM(require("os"), 1);
|
|
2141
|
+
var path18 = __toESM(require("path"), 1);
|
|
2142
|
+
var import_node_child_process5 = require("child_process");
|
|
1949
2143
|
|
|
1950
2144
|
// src/hdc/exec.ts
|
|
1951
|
-
var
|
|
2145
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
1952
2146
|
var import_node_string_decoder = require("string_decoder");
|
|
1953
2147
|
function errorMessage(err) {
|
|
1954
2148
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -1957,12 +2151,12 @@ function runProcess(opts) {
|
|
|
1957
2151
|
const { command, args, cwd, env, onOutput } = opts;
|
|
1958
2152
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1959
2153
|
const { abortSignal } = opts;
|
|
1960
|
-
return new Promise((
|
|
2154
|
+
return new Promise((resolve9, reject) => {
|
|
1961
2155
|
if (abortSignal?.aborted) {
|
|
1962
2156
|
reject(new CancelledError(`Command aborted before start: ${command}`));
|
|
1963
2157
|
return;
|
|
1964
2158
|
}
|
|
1965
|
-
if (needsCmdWrapper(command) && !
|
|
2159
|
+
if (needsCmdWrapper(command) && !fs17.existsSync(command)) {
|
|
1966
2160
|
reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
|
|
1967
2161
|
return;
|
|
1968
2162
|
}
|
|
@@ -2030,7 +2224,7 @@ ${stderr.trim()}` : "")
|
|
|
2030
2224
|
);
|
|
2031
2225
|
return;
|
|
2032
2226
|
}
|
|
2033
|
-
|
|
2227
|
+
resolve9({ code: code ?? -1, stdout, stderr });
|
|
2034
2228
|
});
|
|
2035
2229
|
});
|
|
2036
2230
|
}
|
|
@@ -2103,7 +2297,7 @@ function ensureRemoteOk(result, command) {
|
|
|
2103
2297
|
}
|
|
2104
2298
|
|
|
2105
2299
|
// src/emulator/lifecycle.ts
|
|
2106
|
-
var PID_FILE =
|
|
2300
|
+
var PID_FILE = path18.join(os9.tmpdir(), "oniro_emulator.pid");
|
|
2107
2301
|
async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
|
|
2108
2302
|
try {
|
|
2109
2303
|
await hdcExec({ config, args: ["start", "-r"], logger });
|
|
@@ -2116,24 +2310,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
|
|
|
2116
2310
|
}
|
|
2117
2311
|
}
|
|
2118
2312
|
function resolveLauncher(imagesPath, headless, connect) {
|
|
2119
|
-
const runSh =
|
|
2120
|
-
const runBat =
|
|
2313
|
+
const runSh = path18.join(imagesPath, "run.sh");
|
|
2314
|
+
const runBat = path18.join(imagesPath, "run.bat");
|
|
2121
2315
|
const extra = [];
|
|
2122
2316
|
if (headless) extra.push("--headless");
|
|
2123
2317
|
if (connect) extra.push("--connect", connect);
|
|
2124
|
-
if (
|
|
2125
|
-
if (
|
|
2318
|
+
if (os9.platform() === "win32") {
|
|
2319
|
+
if (fs18.existsSync(runBat)) {
|
|
2126
2320
|
return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
|
|
2127
2321
|
}
|
|
2128
|
-
if (
|
|
2322
|
+
if (fs18.existsSync(runSh)) {
|
|
2129
2323
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
2130
2324
|
}
|
|
2131
2325
|
throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
|
|
2132
2326
|
}
|
|
2133
|
-
if (
|
|
2327
|
+
if (fs18.existsSync(runSh)) {
|
|
2134
2328
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
2135
2329
|
}
|
|
2136
|
-
if (
|
|
2330
|
+
if (fs18.existsSync(runBat)) {
|
|
2137
2331
|
return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
|
|
2138
2332
|
}
|
|
2139
2333
|
throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
|
|
@@ -2141,9 +2335,9 @@ function resolveLauncher(imagesPath, headless, connect) {
|
|
|
2141
2335
|
async function startEmulator(opts) {
|
|
2142
2336
|
const { config } = opts;
|
|
2143
2337
|
const logger = opts.logger ?? noopLogger;
|
|
2144
|
-
if (
|
|
2338
|
+
if (os9.platform() !== "win32" && fs18.existsSync(PID_FILE)) {
|
|
2145
2339
|
try {
|
|
2146
|
-
const pid = Number(
|
|
2340
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
2147
2341
|
if (Number.isFinite(pid) && pid > 0) {
|
|
2148
2342
|
try {
|
|
2149
2343
|
process.kill(pid, 0);
|
|
@@ -2159,35 +2353,35 @@ async function startEmulator(opts) {
|
|
|
2159
2353
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
2160
2354
|
}
|
|
2161
2355
|
}
|
|
2162
|
-
const imagesPath =
|
|
2163
|
-
if (!
|
|
2356
|
+
const imagesPath = path18.join(getEmulatorDir(config), "images");
|
|
2357
|
+
if (!fs18.existsSync(imagesPath)) {
|
|
2164
2358
|
throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
|
|
2165
2359
|
}
|
|
2166
2360
|
const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
|
|
2167
|
-
const logPath = opts.logFile ?? (
|
|
2168
|
-
const logFd =
|
|
2361
|
+
const logPath = opts.logFile ?? (os9.platform() === "win32" ? "NUL" : "/dev/null");
|
|
2362
|
+
const logFd = fs18.openSync(logPath, "a");
|
|
2169
2363
|
let child;
|
|
2170
2364
|
try {
|
|
2171
|
-
child = (0,
|
|
2365
|
+
child = (0, import_node_child_process5.spawn)(launcher.command, [...launcher.args], {
|
|
2172
2366
|
cwd: launcher.cwd,
|
|
2173
2367
|
detached: true,
|
|
2174
2368
|
stdio: ["ignore", logFd, logFd],
|
|
2175
2369
|
windowsHide: true
|
|
2176
2370
|
});
|
|
2177
2371
|
} finally {
|
|
2178
|
-
|
|
2372
|
+
fs18.closeSync(logFd);
|
|
2179
2373
|
}
|
|
2180
2374
|
if (!child.pid) {
|
|
2181
2375
|
throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
|
|
2182
2376
|
}
|
|
2183
|
-
|
|
2377
|
+
fs18.writeFileSync(PID_FILE, String(child.pid));
|
|
2184
2378
|
child.unref();
|
|
2185
2379
|
logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
|
|
2186
|
-
const earlyExit = new Promise((
|
|
2187
|
-
child.once("exit", (code) =>
|
|
2188
|
-
child.once("error", () =>
|
|
2380
|
+
const earlyExit = new Promise((resolve9) => {
|
|
2381
|
+
child.once("exit", (code) => resolve9(code));
|
|
2382
|
+
child.once("error", () => resolve9(-1));
|
|
2189
2383
|
});
|
|
2190
|
-
const liveness = new Promise((
|
|
2384
|
+
const liveness = new Promise((resolve9) => setTimeout(() => resolve9("alive"), 2e3));
|
|
2191
2385
|
const outcome = await Promise.race([earlyExit, liveness]);
|
|
2192
2386
|
if (outcome !== "alive") {
|
|
2193
2387
|
const tail = readTail(logPath, 4e3);
|
|
@@ -2227,24 +2421,24 @@ ${tail}` : ""}`);
|
|
|
2227
2421
|
}
|
|
2228
2422
|
function readTail(filePath, bytes) {
|
|
2229
2423
|
try {
|
|
2230
|
-
const stat =
|
|
2424
|
+
const stat = fs18.statSync(filePath);
|
|
2231
2425
|
const start = Math.max(0, stat.size - bytes);
|
|
2232
|
-
const fd =
|
|
2426
|
+
const fd = fs18.openSync(filePath, "r");
|
|
2233
2427
|
try {
|
|
2234
2428
|
const buf = Buffer.alloc(stat.size - start);
|
|
2235
|
-
|
|
2429
|
+
fs18.readSync(fd, buf, 0, buf.length, start);
|
|
2236
2430
|
return buf.toString("utf8");
|
|
2237
2431
|
} finally {
|
|
2238
|
-
|
|
2432
|
+
fs18.closeSync(fd);
|
|
2239
2433
|
}
|
|
2240
2434
|
} catch {
|
|
2241
2435
|
return "";
|
|
2242
2436
|
}
|
|
2243
2437
|
}
|
|
2244
2438
|
async function stopEmulator(logger = noopLogger) {
|
|
2245
|
-
if (
|
|
2439
|
+
if (fs18.existsSync(PID_FILE)) {
|
|
2246
2440
|
try {
|
|
2247
|
-
const pid = Number(
|
|
2441
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
2248
2442
|
if (Number.isFinite(pid) && pid > 0) {
|
|
2249
2443
|
try {
|
|
2250
2444
|
process.kill(pid, "SIGTERM");
|
|
@@ -2257,20 +2451,20 @@ async function stopEmulator(logger = noopLogger) {
|
|
|
2257
2451
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
2258
2452
|
}
|
|
2259
2453
|
}
|
|
2260
|
-
const [killCmd, ...killArgs] =
|
|
2454
|
+
const [killCmd, ...killArgs] = os9.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
|
|
2261
2455
|
try {
|
|
2262
2456
|
await runProcess({ command: killCmd, args: killArgs, logger });
|
|
2263
2457
|
} catch (err) {
|
|
2264
2458
|
logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
|
|
2265
2459
|
}
|
|
2266
|
-
|
|
2460
|
+
fs18.rm(PID_FILE, { force: true }, (err) => {
|
|
2267
2461
|
if (err) logger.warn(`Could not remove PID file: ${err.message}`);
|
|
2268
2462
|
});
|
|
2269
2463
|
}
|
|
2270
2464
|
async function isQemuRunning(logger) {
|
|
2271
|
-
if (
|
|
2465
|
+
if (os9.platform() !== "win32") {
|
|
2272
2466
|
try {
|
|
2273
|
-
const pid = Number(
|
|
2467
|
+
const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
|
|
2274
2468
|
if (Number.isFinite(pid) && pid > 0) {
|
|
2275
2469
|
process.kill(pid, 0);
|
|
2276
2470
|
return true;
|
|
@@ -2278,10 +2472,10 @@ async function isQemuRunning(logger) {
|
|
|
2278
2472
|
} catch {
|
|
2279
2473
|
}
|
|
2280
2474
|
}
|
|
2281
|
-
const [cmd, ...args] =
|
|
2475
|
+
const [cmd, ...args] = os9.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
|
|
2282
2476
|
try {
|
|
2283
2477
|
const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
|
|
2284
|
-
return
|
|
2478
|
+
return os9.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
|
|
2285
2479
|
} catch {
|
|
2286
2480
|
return false;
|
|
2287
2481
|
}
|
|
@@ -2342,12 +2536,13 @@ async function listDevices(config, opts = {}) {
|
|
|
2342
2536
|
});
|
|
2343
2537
|
return parseDeviceList(res.stdout);
|
|
2344
2538
|
}
|
|
2539
|
+
function isUsableDevice(device) {
|
|
2540
|
+
return device.status === "Connected" || device.status === "Unknown";
|
|
2541
|
+
}
|
|
2345
2542
|
async function selectDevice(config, serial, opts = {}) {
|
|
2346
2543
|
const resolved = resolveDeviceSerial(serial);
|
|
2347
2544
|
if (resolved) return resolved;
|
|
2348
|
-
const usable = (await listDevices(config, opts)).filter(
|
|
2349
|
-
(d) => d.status === "Connected" || d.status === "Unknown"
|
|
2350
|
-
);
|
|
2545
|
+
const usable = (await listDevices(config, opts)).filter(isUsableDevice);
|
|
2351
2546
|
if (usable.length === 1) return usable[0].serial;
|
|
2352
2547
|
if (usable.length === 0) {
|
|
2353
2548
|
throw new OniroError("No connected device found. Connect a device/emulator or set ONIRO_DEVICE_SERIAL.");
|
|
@@ -2405,11 +2600,11 @@ async function paramSet(config, key, value, opts = {}) {
|
|
|
2405
2600
|
}
|
|
2406
2601
|
|
|
2407
2602
|
// src/hdc/files.ts
|
|
2408
|
-
var
|
|
2603
|
+
var path19 = __toESM(require("path"), 1);
|
|
2409
2604
|
function normaliseLocalPath(local) {
|
|
2410
|
-
const abs =
|
|
2605
|
+
const abs = path19.resolve(local);
|
|
2411
2606
|
const long = toLongPath(abs);
|
|
2412
|
-
return long !== abs ? long :
|
|
2607
|
+
return long !== abs ? long : path19.join(toLongPath(path19.dirname(abs)), path19.basename(abs));
|
|
2413
2608
|
}
|
|
2414
2609
|
function assertDevicePath(remote) {
|
|
2415
2610
|
if (/^[A-Za-z]:[\\/]/.test(remote)) {
|
|
@@ -2428,13 +2623,13 @@ async function sendFile(opts) {
|
|
|
2428
2623
|
assertDevicePath(opts.remote);
|
|
2429
2624
|
const local = normaliseLocalPath(opts.local);
|
|
2430
2625
|
const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
|
|
2431
|
-
const remote = sandbox && !opts.remote.startsWith("/") ?
|
|
2626
|
+
const remote = sandbox && !opts.remote.startsWith("/") ? path19.posix.join(sandbox, opts.remote) : opts.remote;
|
|
2432
2627
|
if (sandbox && !remote.startsWith(`${sandbox}/`)) {
|
|
2433
2628
|
throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
|
|
2434
2629
|
}
|
|
2435
2630
|
const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
|
|
2436
2631
|
if (sandbox) {
|
|
2437
|
-
const mkdir = `mkdir -p ${shQuote(
|
|
2632
|
+
const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
|
|
2438
2633
|
ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
|
|
2439
2634
|
}
|
|
2440
2635
|
const res = await hdcExec({
|
|
@@ -2446,7 +2641,7 @@ async function sendFile(opts) {
|
|
|
2446
2641
|
});
|
|
2447
2642
|
ensureOk(res, `hdc file send ${local} ${remote}`);
|
|
2448
2643
|
if (sandbox) {
|
|
2449
|
-
const top =
|
|
2644
|
+
const top = path19.posix.join(sandbox, path19.posix.relative(sandbox, remote).split("/")[0]);
|
|
2450
2645
|
const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
|
|
2451
2646
|
ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
|
|
2452
2647
|
}
|
|
@@ -2484,7 +2679,7 @@ async function getDisplaySize(config, opts = {}) {
|
|
|
2484
2679
|
|
|
2485
2680
|
// src/hdc/wait.ts
|
|
2486
2681
|
function delay(ms, signal) {
|
|
2487
|
-
return new Promise((
|
|
2682
|
+
return new Promise((resolve9, reject) => {
|
|
2488
2683
|
if (signal?.aborted) {
|
|
2489
2684
|
reject(new CancelledError("Wait cancelled."));
|
|
2490
2685
|
return;
|
|
@@ -2499,7 +2694,7 @@ function delay(ms, signal) {
|
|
|
2499
2694
|
};
|
|
2500
2695
|
const timer = setTimeout(() => {
|
|
2501
2696
|
cleanup();
|
|
2502
|
-
|
|
2697
|
+
resolve9();
|
|
2503
2698
|
}, ms);
|
|
2504
2699
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2505
2700
|
});
|
|
@@ -2528,18 +2723,18 @@ async function waitForCondition(opts) {
|
|
|
2528
2723
|
}
|
|
2529
2724
|
|
|
2530
2725
|
// src/hdc/app.ts
|
|
2531
|
-
var
|
|
2532
|
-
var
|
|
2726
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
2727
|
+
var path22 = __toESM(require("path"), 1);
|
|
2533
2728
|
|
|
2534
2729
|
// src/build/discoverHaps.ts
|
|
2535
|
-
var
|
|
2536
|
-
var
|
|
2730
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
2731
|
+
var path20 = __toESM(require("path"), 1);
|
|
2537
2732
|
function moduleOfHap(hapFileName) {
|
|
2538
2733
|
const dash = hapFileName.indexOf("-");
|
|
2539
2734
|
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
2540
2735
|
}
|
|
2541
2736
|
function isAppProjectRoot(dir) {
|
|
2542
|
-
return
|
|
2737
|
+
return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
|
|
2543
2738
|
}
|
|
2544
2739
|
async function discoverHaps(opts) {
|
|
2545
2740
|
const { projectDir } = opts;
|
|
@@ -2548,9 +2743,9 @@ async function discoverHaps(opts) {
|
|
|
2548
2743
|
const result = {};
|
|
2549
2744
|
const walk = async (dir, depth) => {
|
|
2550
2745
|
if (depth > maxDepth) return;
|
|
2551
|
-
const entries = await
|
|
2746
|
+
const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2552
2747
|
for (const e of entries) {
|
|
2553
|
-
const full =
|
|
2748
|
+
const full = path20.join(dir, e.name);
|
|
2554
2749
|
if (e.isDirectory()) {
|
|
2555
2750
|
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2556
2751
|
if (skipNestedProjects && isAppProjectRoot(full)) continue;
|
|
@@ -2564,7 +2759,7 @@ async function discoverHaps(opts) {
|
|
|
2564
2759
|
}
|
|
2565
2760
|
};
|
|
2566
2761
|
await walk(projectDir, 0);
|
|
2567
|
-
const mtime = (p) =>
|
|
2762
|
+
const mtime = (p) => fs19.statSync(p).mtimeMs;
|
|
2568
2763
|
for (const bucket of Object.values(result)) {
|
|
2569
2764
|
bucket.signed.sort((a, b) => mtime(b) - mtime(a));
|
|
2570
2765
|
bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
|
|
@@ -2590,16 +2785,16 @@ function pickHap(haps, module2) {
|
|
|
2590
2785
|
}
|
|
2591
2786
|
|
|
2592
2787
|
// src/hdc/project.ts
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
2788
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
2789
|
+
var path21 = __toESM(require("path"), 1);
|
|
2595
2790
|
var import_json53 = __toESM(require("json5"), 1);
|
|
2596
2791
|
function readJson5(filePath) {
|
|
2597
|
-
const content =
|
|
2792
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
2598
2793
|
return import_json53.default.parse(content);
|
|
2599
2794
|
}
|
|
2600
2795
|
function getBundleName(projectDir) {
|
|
2601
|
-
const appJsonPath =
|
|
2602
|
-
if (!
|
|
2796
|
+
const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
|
|
2797
|
+
if (!fs20.existsSync(appJsonPath)) {
|
|
2603
2798
|
throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
|
|
2604
2799
|
}
|
|
2605
2800
|
const appJson = readJson5(appJsonPath);
|
|
@@ -2609,8 +2804,8 @@ function getBundleName(projectDir) {
|
|
|
2609
2804
|
return appJson.app.bundleName;
|
|
2610
2805
|
}
|
|
2611
2806
|
function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
2612
|
-
const moduleJsonPath =
|
|
2613
|
-
if (!
|
|
2807
|
+
const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
|
|
2808
|
+
if (!fs20.existsSync(moduleJsonPath)) {
|
|
2614
2809
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
2615
2810
|
}
|
|
2616
2811
|
const moduleJson = readJson5(moduleJsonPath);
|
|
@@ -2634,7 +2829,7 @@ function detectInstallFailure(output) {
|
|
|
2634
2829
|
return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
|
|
2635
2830
|
}
|
|
2636
2831
|
function isUnsignedHap(hapPath) {
|
|
2637
|
-
return
|
|
2832
|
+
return path22.basename(hapPath).includes("unsigned");
|
|
2638
2833
|
}
|
|
2639
2834
|
function installFailureReason(output) {
|
|
2640
2835
|
const code = /code:\s*(\d+)/i.exec(output);
|
|
@@ -2643,7 +2838,7 @@ function installFailureReason(output) {
|
|
|
2643
2838
|
return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
|
|
2644
2839
|
}
|
|
2645
2840
|
function unsignedHapRejection(output, hap) {
|
|
2646
|
-
const name =
|
|
2841
|
+
const name = path22.basename(hap.path);
|
|
2647
2842
|
const reason = installFailureReason(output);
|
|
2648
2843
|
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}).`;
|
|
2649
2844
|
if (!/9568320|signature|pkcs7/i.test(reason)) return what;
|
|
@@ -2661,24 +2856,24 @@ ${result.stderr}`;
|
|
|
2661
2856
|
return result;
|
|
2662
2857
|
}
|
|
2663
2858
|
function existingHapOrUnsigned(hapPath) {
|
|
2664
|
-
if (
|
|
2859
|
+
if (fs21.existsSync(hapPath)) return { path: hapPath };
|
|
2665
2860
|
const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
|
|
2666
|
-
return sibling !== hapPath &&
|
|
2861
|
+
return sibling !== hapPath && fs21.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
|
|
2667
2862
|
}
|
|
2668
2863
|
async function resolveInstallHap(opts, logger) {
|
|
2669
2864
|
const warnIfFallback = (hap) => {
|
|
2670
|
-
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${
|
|
2865
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path22.basename(hap.path)}.`);
|
|
2671
2866
|
return hap;
|
|
2672
2867
|
};
|
|
2673
2868
|
const configured = opts.hapPath ?? opts.config.get("hapPath", "");
|
|
2674
2869
|
const relative3 = configured || defaultPaths.hapPath;
|
|
2675
|
-
const hapPath =
|
|
2870
|
+
const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
|
|
2676
2871
|
if (configured) {
|
|
2677
2872
|
const hap = existingHapOrUnsigned(hapPath);
|
|
2678
2873
|
if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
|
|
2679
2874
|
return warnIfFallback(hap);
|
|
2680
2875
|
}
|
|
2681
|
-
if (
|
|
2876
|
+
if (fs21.existsSync(hapPath)) return { path: hapPath };
|
|
2682
2877
|
const haps = await discoverHaps({ projectDir: opts.projectDir });
|
|
2683
2878
|
const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
|
|
2684
2879
|
const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
|
|
@@ -2789,7 +2984,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2789
2984
|
const logger = options.logger ?? noopLogger;
|
|
2790
2985
|
const timeoutMs = options.timeoutMs ?? 1e3;
|
|
2791
2986
|
const hdc = getHdcPath(config);
|
|
2792
|
-
return new Promise((
|
|
2987
|
+
return new Promise((resolve9, reject) => {
|
|
2793
2988
|
let proc;
|
|
2794
2989
|
try {
|
|
2795
2990
|
proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
|
|
@@ -2809,7 +3004,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2809
3004
|
if (options.targetProcessName && name === options.targetProcessName && !resolved) {
|
|
2810
3005
|
resolved = true;
|
|
2811
3006
|
killProcessTree(proc);
|
|
2812
|
-
|
|
3007
|
+
resolve9(pid);
|
|
2813
3008
|
return;
|
|
2814
3009
|
}
|
|
2815
3010
|
}
|
|
@@ -2830,7 +3025,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2830
3025
|
if (options.targetProcessName) {
|
|
2831
3026
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2832
3027
|
} else {
|
|
2833
|
-
|
|
3028
|
+
resolve9(processes);
|
|
2834
3029
|
}
|
|
2835
3030
|
}, timeoutMs);
|
|
2836
3031
|
proc.on("close", () => {
|
|
@@ -2840,7 +3035,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2840
3035
|
if (options.targetProcessName) {
|
|
2841
3036
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2842
3037
|
} else {
|
|
2843
|
-
|
|
3038
|
+
resolve9(processes);
|
|
2844
3039
|
}
|
|
2845
3040
|
});
|
|
2846
3041
|
});
|
|
@@ -3009,12 +3204,12 @@ async function reboot(opts) {
|
|
|
3009
3204
|
}
|
|
3010
3205
|
|
|
3011
3206
|
// src/hdc/screen.ts
|
|
3012
|
-
var
|
|
3013
|
-
var
|
|
3014
|
-
var
|
|
3207
|
+
var fs22 = __toESM(require("fs"), 1);
|
|
3208
|
+
var os10 = __toESM(require("os"), 1);
|
|
3209
|
+
var path23 = __toESM(require("path"), 1);
|
|
3015
3210
|
var import_node_crypto = require("crypto");
|
|
3016
3211
|
function tmpName(ext) {
|
|
3017
|
-
return
|
|
3212
|
+
return path23.join(os10.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
|
|
3018
3213
|
}
|
|
3019
3214
|
async function takeScreenshot(opts) {
|
|
3020
3215
|
const remote = "/data/local/tmp/oniro_screenshot.jpeg";
|
|
@@ -3024,11 +3219,11 @@ async function takeScreenshot(opts) {
|
|
|
3024
3219
|
const local = tmpName("jpeg");
|
|
3025
3220
|
try {
|
|
3026
3221
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
3027
|
-
const pixels = await
|
|
3222
|
+
const pixels = await fs22.promises.readFile(local);
|
|
3028
3223
|
const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
|
|
3029
3224
|
return { pixels, width: size.width, height: size.height };
|
|
3030
3225
|
} finally {
|
|
3031
|
-
|
|
3226
|
+
fs22.promises.unlink(local).catch(() => {
|
|
3032
3227
|
});
|
|
3033
3228
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
3034
3229
|
});
|
|
@@ -3047,9 +3242,9 @@ async function captureBurst(opts) {
|
|
|
3047
3242
|
const local = tmpName(`burst_${i}.jpeg`);
|
|
3048
3243
|
try {
|
|
3049
3244
|
await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
|
|
3050
|
-
frames.push(await
|
|
3245
|
+
frames.push(await fs22.promises.readFile(local));
|
|
3051
3246
|
} finally {
|
|
3052
|
-
|
|
3247
|
+
fs22.promises.unlink(local).catch(() => {
|
|
3053
3248
|
});
|
|
3054
3249
|
}
|
|
3055
3250
|
}
|
|
@@ -3224,9 +3419,9 @@ async function sendRawTouch(opts) {
|
|
|
3224
3419
|
}
|
|
3225
3420
|
|
|
3226
3421
|
// src/hdc/dump.ts
|
|
3227
|
-
var
|
|
3228
|
-
var
|
|
3229
|
-
var
|
|
3422
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
3423
|
+
var os11 = __toESM(require("os"), 1);
|
|
3424
|
+
var path24 = __toESM(require("path"), 1);
|
|
3230
3425
|
var import_node_crypto2 = require("crypto");
|
|
3231
3426
|
|
|
3232
3427
|
// src/hdc/ime.ts
|
|
@@ -3489,10 +3684,10 @@ async function dumpLayout(opts) {
|
|
|
3489
3684
|
if (!m) throw new OniroError(`Could not parse dumpLayout output:
|
|
3490
3685
|
${dump.stdout}`);
|
|
3491
3686
|
const remote = m[1];
|
|
3492
|
-
const local =
|
|
3687
|
+
const local = path24.join(os11.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
|
|
3493
3688
|
try {
|
|
3494
3689
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
3495
|
-
const parsed = JSON.parse(await
|
|
3690
|
+
const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
|
|
3496
3691
|
const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
|
|
3497
3692
|
if (opts.prune === false) {
|
|
3498
3693
|
return { display, tree: parsed };
|
|
@@ -3505,7 +3700,7 @@ ${dump.stdout}`);
|
|
|
3505
3700
|
const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
|
|
3506
3701
|
return { display, tree, keyboard };
|
|
3507
3702
|
} finally {
|
|
3508
|
-
|
|
3703
|
+
fs23.promises.unlink(local).catch(() => {
|
|
3509
3704
|
});
|
|
3510
3705
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
3511
3706
|
});
|
|
@@ -3515,7 +3710,7 @@ ${dump.stdout}`);
|
|
|
3515
3710
|
// src/hdc/uiActions.ts
|
|
3516
3711
|
var FOCUS_SETTLE_MS = 400;
|
|
3517
3712
|
var KEYBOARD_SETTLE_MS = 400;
|
|
3518
|
-
var sleep = (ms) => new Promise((
|
|
3713
|
+
var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
3519
3714
|
function targetTree(layout) {
|
|
3520
3715
|
const kb = layout.keyboard;
|
|
3521
3716
|
return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
|
|
@@ -3668,11 +3863,11 @@ async function waitForTarget(opts) {
|
|
|
3668
3863
|
}
|
|
3669
3864
|
|
|
3670
3865
|
// src/hdc/hilog.ts
|
|
3671
|
-
var
|
|
3866
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
3672
3867
|
function setHilogLevel(opts) {
|
|
3673
3868
|
const logger = opts.logger ?? noopLogger;
|
|
3674
3869
|
const hdc = getHdcPath(opts.config);
|
|
3675
|
-
return new Promise((
|
|
3870
|
+
return new Promise((resolve9, reject) => {
|
|
3676
3871
|
let child;
|
|
3677
3872
|
try {
|
|
3678
3873
|
child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
|
|
@@ -3692,7 +3887,7 @@ function setHilogLevel(opts) {
|
|
|
3692
3887
|
});
|
|
3693
3888
|
child.once("close", (code) => {
|
|
3694
3889
|
if (code === 0) {
|
|
3695
|
-
|
|
3890
|
+
resolve9();
|
|
3696
3891
|
} else {
|
|
3697
3892
|
const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
|
|
3698
3893
|
logger.error(`[hilog] ${msg}`);
|
|
@@ -3794,7 +3989,7 @@ async function recentEntryFilter(opts) {
|
|
|
3794
3989
|
};
|
|
3795
3990
|
}
|
|
3796
3991
|
function waitForLog(opts) {
|
|
3797
|
-
return new Promise((
|
|
3992
|
+
return new Promise((resolve9, reject) => {
|
|
3798
3993
|
const deadline = Date.now() + opts.timeoutMs;
|
|
3799
3994
|
let settled = false;
|
|
3800
3995
|
let child = null;
|
|
@@ -3815,7 +4010,7 @@ function waitForLog(opts) {
|
|
|
3815
4010
|
if (settled) return;
|
|
3816
4011
|
settled = true;
|
|
3817
4012
|
cleanup();
|
|
3818
|
-
|
|
4013
|
+
resolve9(entry);
|
|
3819
4014
|
};
|
|
3820
4015
|
const fail = (err) => {
|
|
3821
4016
|
if (settled) return;
|
|
@@ -3870,7 +4065,7 @@ function waitForLog(opts) {
|
|
|
3870
4065
|
});
|
|
3871
4066
|
}
|
|
3872
4067
|
function watchLog(opts) {
|
|
3873
|
-
return new Promise((
|
|
4068
|
+
return new Promise((resolve9, reject) => {
|
|
3874
4069
|
const matches = [];
|
|
3875
4070
|
let settled = false;
|
|
3876
4071
|
let child = null;
|
|
@@ -3889,7 +4084,7 @@ function watchLog(opts) {
|
|
|
3889
4084
|
if (settled) return;
|
|
3890
4085
|
settled = true;
|
|
3891
4086
|
cleanup();
|
|
3892
|
-
|
|
4087
|
+
resolve9(opts.dedup === false ? matches : dedupEntries(matches));
|
|
3893
4088
|
};
|
|
3894
4089
|
const onAbort = () => {
|
|
3895
4090
|
if (settled) return;
|
|
@@ -3934,11 +4129,11 @@ async function captureLog(opts) {
|
|
|
3934
4129
|
const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
|
|
3935
4130
|
ensureOk(res, "hdc shell hilog -r");
|
|
3936
4131
|
}
|
|
3937
|
-
const fd =
|
|
4132
|
+
const fd = fs24.openSync(opts.file, "a");
|
|
3938
4133
|
let lines = 0;
|
|
3939
4134
|
let reconnects = 0;
|
|
3940
4135
|
try {
|
|
3941
|
-
await new Promise((
|
|
4136
|
+
await new Promise((resolve9, reject) => {
|
|
3942
4137
|
const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
|
|
3943
4138
|
let settled = false;
|
|
3944
4139
|
let child = null;
|
|
@@ -3947,7 +4142,7 @@ async function captureLog(opts) {
|
|
|
3947
4142
|
let open = true;
|
|
3948
4143
|
const write = (line) => {
|
|
3949
4144
|
if (!open || opts.pattern && !opts.pattern.test(line)) return;
|
|
3950
|
-
|
|
4145
|
+
fs24.writeSync(fd, `${line}
|
|
3951
4146
|
`);
|
|
3952
4147
|
lines++;
|
|
3953
4148
|
};
|
|
@@ -3968,7 +4163,7 @@ async function captureLog(opts) {
|
|
|
3968
4163
|
if (settled) return;
|
|
3969
4164
|
settled = true;
|
|
3970
4165
|
cleanup();
|
|
3971
|
-
|
|
4166
|
+
resolve9();
|
|
3972
4167
|
}
|
|
3973
4168
|
const fail = (err) => {
|
|
3974
4169
|
if (settled) return;
|
|
@@ -4012,7 +4207,7 @@ async function captureLog(opts) {
|
|
|
4012
4207
|
spawnStream();
|
|
4013
4208
|
});
|
|
4014
4209
|
} finally {
|
|
4015
|
-
|
|
4210
|
+
fs24.closeSync(fd);
|
|
4016
4211
|
}
|
|
4017
4212
|
return { file: opts.file, lines, reconnects };
|
|
4018
4213
|
}
|
|
@@ -4040,17 +4235,17 @@ async function dumpLog(opts) {
|
|
|
4040
4235
|
}
|
|
4041
4236
|
|
|
4042
4237
|
// src/build/runHvigorw.ts
|
|
4043
|
-
var
|
|
4044
|
-
var
|
|
4238
|
+
var fs26 = __toESM(require("fs"), 1);
|
|
4239
|
+
var path26 = __toESM(require("path"), 1);
|
|
4045
4240
|
|
|
4046
4241
|
// src/harmonyos/target.ts
|
|
4047
|
-
var
|
|
4048
|
-
var
|
|
4242
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
4243
|
+
var path25 = __toESM(require("path"), 1);
|
|
4049
4244
|
var import_json54 = __toESM(require("json5"), 1);
|
|
4050
4245
|
function detectRuntimeOs(projectDir, productName) {
|
|
4051
4246
|
let products = [];
|
|
4052
4247
|
try {
|
|
4053
|
-
const parsed = import_json54.default.parse(
|
|
4248
|
+
const parsed = import_json54.default.parse(fs25.readFileSync(path25.join(projectDir, "build-profile.json5"), "utf-8"));
|
|
4054
4249
|
if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
|
|
4055
4250
|
} catch {
|
|
4056
4251
|
return "OpenHarmony";
|
|
@@ -4091,7 +4286,7 @@ function runHvigorw(opts) {
|
|
|
4091
4286
|
const { config } = opts;
|
|
4092
4287
|
const projectDir = toLongPath(opts.projectDir);
|
|
4093
4288
|
const logger = opts.logger ?? noopLogger;
|
|
4094
|
-
if (!
|
|
4289
|
+
if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
|
|
4095
4290
|
throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
|
|
4096
4291
|
}
|
|
4097
4292
|
let hvigorw;
|
|
@@ -4103,24 +4298,24 @@ function runHvigorw(opts) {
|
|
|
4103
4298
|
hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
|
|
4104
4299
|
logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
|
|
4105
4300
|
} else {
|
|
4106
|
-
if (!
|
|
4301
|
+
if (!fs26.existsSync(getCmdToolsPath(config))) {
|
|
4107
4302
|
throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
|
|
4108
4303
|
}
|
|
4109
4304
|
sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
|
|
4110
4305
|
hvigorw = getHvigorwPath(config, projectDir);
|
|
4111
4306
|
}
|
|
4112
|
-
if (!
|
|
4307
|
+
if (!fs26.existsSync(hvigorw)) {
|
|
4113
4308
|
throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
|
|
4114
4309
|
}
|
|
4115
4310
|
const args = buildHvigorwArgs(opts);
|
|
4116
4311
|
if (process.platform !== "win32") {
|
|
4117
4312
|
try {
|
|
4118
|
-
|
|
4313
|
+
fs26.chmodSync(hvigorw, 493);
|
|
4119
4314
|
} catch {
|
|
4120
4315
|
}
|
|
4121
4316
|
}
|
|
4122
4317
|
const env = { ...process.env, ...sdkEnv };
|
|
4123
|
-
return new Promise((
|
|
4318
|
+
return new Promise((resolve9, reject) => {
|
|
4124
4319
|
if (opts.abortSignal?.aborted) {
|
|
4125
4320
|
reject(new CancelledError("hvigorw build cancelled."));
|
|
4126
4321
|
return;
|
|
@@ -4164,15 +4359,15 @@ function runHvigorw(opts) {
|
|
|
4164
4359
|
settled = true;
|
|
4165
4360
|
cleanup();
|
|
4166
4361
|
const exitCode = code ?? 1;
|
|
4167
|
-
if (exitCode === 0)
|
|
4362
|
+
if (exitCode === 0) resolve9({ exitCode });
|
|
4168
4363
|
else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
|
|
4169
4364
|
});
|
|
4170
4365
|
});
|
|
4171
4366
|
}
|
|
4172
4367
|
|
|
4173
4368
|
// src/build/ohpm.ts
|
|
4174
|
-
var
|
|
4175
|
-
var
|
|
4369
|
+
var fs27 = __toESM(require("fs"), 1);
|
|
4370
|
+
var path27 = __toESM(require("path"), 1);
|
|
4176
4371
|
function runOhpm(opts) {
|
|
4177
4372
|
return runProcess({
|
|
4178
4373
|
command: opts.ohpmPath ?? getOhpmPath(opts.config),
|
|
@@ -4185,7 +4380,7 @@ function runOhpm(opts) {
|
|
|
4185
4380
|
});
|
|
4186
4381
|
}
|
|
4187
4382
|
async function ensureOhModules(opts) {
|
|
4188
|
-
if (
|
|
4383
|
+
if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
|
|
4189
4384
|
return { installed: false };
|
|
4190
4385
|
}
|
|
4191
4386
|
const res = await runOhpm({
|
|
@@ -4202,8 +4397,8 @@ async function ensureOhModules(opts) {
|
|
|
4202
4397
|
}
|
|
4203
4398
|
|
|
4204
4399
|
// src/build/codelinter.ts
|
|
4205
|
-
var
|
|
4206
|
-
var
|
|
4400
|
+
var fs28 = __toESM(require("fs"), 1);
|
|
4401
|
+
var path28 = __toESM(require("path"), 1);
|
|
4207
4402
|
var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
|
|
4208
4403
|
var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
|
|
4209
4404
|
var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
|
|
@@ -4293,10 +4488,10 @@ function parseCodelinterFindings(output) {
|
|
|
4293
4488
|
var PROJECT_ROOT_FILES = [
|
|
4294
4489
|
["build-profile.json5"],
|
|
4295
4490
|
["hvigorfile.ts", "hvigorfile.js"],
|
|
4296
|
-
[
|
|
4491
|
+
[path28.join("hvigor", "hvigor-config.json5")]
|
|
4297
4492
|
];
|
|
4298
4493
|
function missingProjectRootFiles(projectDir) {
|
|
4299
|
-
return PROJECT_ROOT_FILES.filter((names) => !names.some((f) =>
|
|
4494
|
+
return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs28.existsSync(path28.join(projectDir, f)))).map(
|
|
4300
4495
|
(names) => names[0]
|
|
4301
4496
|
);
|
|
4302
4497
|
}
|
|
@@ -4378,32 +4573,22 @@ ${hint}`, err);
|
|
|
4378
4573
|
return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
|
|
4379
4574
|
}
|
|
4380
4575
|
|
|
4381
|
-
// src/project/validators.ts
|
|
4382
|
-
function isValidProjectName(name) {
|
|
4383
|
-
if (!name) return false;
|
|
4384
|
-
if (name.includes("/") || name.includes("\\")) return false;
|
|
4385
|
-
return /^[A-Za-z0-9._-]+$/.test(name);
|
|
4386
|
-
}
|
|
4387
|
-
function isValidBundleName(bundleName) {
|
|
4388
|
-
return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
|
|
4389
|
-
}
|
|
4390
|
-
|
|
4391
4576
|
// src/project/templates.ts
|
|
4392
|
-
var
|
|
4393
|
-
var
|
|
4577
|
+
var fs29 = __toESM(require("fs"), 1);
|
|
4578
|
+
var path29 = __toESM(require("path"), 1);
|
|
4394
4579
|
function toHumanTemplateName(folderName) {
|
|
4395
4580
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
4396
4581
|
}
|
|
4397
4582
|
function listTemplates(templateRoot) {
|
|
4398
|
-
if (!
|
|
4399
|
-
const entries =
|
|
4583
|
+
if (!fs29.existsSync(templateRoot)) return [];
|
|
4584
|
+
const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
|
|
4400
4585
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
4401
|
-
const dir =
|
|
4402
|
-
const metaPath =
|
|
4586
|
+
const dir = path29.join(templateRoot, e.name);
|
|
4587
|
+
const metaPath = path29.join(dir, "template.json");
|
|
4403
4588
|
let meta = {};
|
|
4404
|
-
if (
|
|
4589
|
+
if (fs29.existsSync(metaPath)) {
|
|
4405
4590
|
try {
|
|
4406
|
-
meta = JSON.parse(
|
|
4591
|
+
meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
|
|
4407
4592
|
} catch {
|
|
4408
4593
|
}
|
|
4409
4594
|
}
|
|
@@ -4418,57 +4603,57 @@ function listTemplates(templateRoot) {
|
|
|
4418
4603
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
4419
4604
|
const required = [
|
|
4420
4605
|
"build-profile.json5",
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4606
|
+
path29.join("AppScope", "app.json5"),
|
|
4607
|
+
path29.join(defaultModuleName, "src", "main", "module.json5"),
|
|
4608
|
+
path29.join(defaultModuleName, "oh-package.json5"),
|
|
4424
4609
|
"hvigorfile.ts"
|
|
4425
4610
|
];
|
|
4426
|
-
return required.filter((rel) => !
|
|
4611
|
+
return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
|
|
4427
4612
|
}
|
|
4428
4613
|
|
|
4429
4614
|
// src/project/createScaffold.ts
|
|
4430
|
-
var
|
|
4431
|
-
var
|
|
4615
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
4616
|
+
var path30 = __toESM(require("path"), 1);
|
|
4432
4617
|
|
|
4433
4618
|
// src/project/jsonHelpers.ts
|
|
4434
|
-
var
|
|
4619
|
+
var fs30 = __toESM(require("fs"), 1);
|
|
4435
4620
|
var import_json55 = __toESM(require("json5"), 1);
|
|
4436
4621
|
function readJson5File(filePath) {
|
|
4437
|
-
return import_json55.default.parse(
|
|
4622
|
+
return import_json55.default.parse(fs30.readFileSync(filePath, "utf8"));
|
|
4438
4623
|
}
|
|
4439
4624
|
function writeJson5File(filePath, value) {
|
|
4440
|
-
|
|
4625
|
+
fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4441
4626
|
}
|
|
4442
4627
|
function readJsonFile(filePath) {
|
|
4443
|
-
return JSON.parse(
|
|
4628
|
+
return JSON.parse(fs30.readFileSync(filePath, "utf8"));
|
|
4444
4629
|
}
|
|
4445
4630
|
function writeJsonFile(filePath, value) {
|
|
4446
|
-
|
|
4631
|
+
fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
4447
4632
|
}
|
|
4448
4633
|
|
|
4449
4634
|
// src/project/createScaffold.ts
|
|
4450
4635
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
4451
4636
|
async function pathExists(p) {
|
|
4452
4637
|
try {
|
|
4453
|
-
await
|
|
4638
|
+
await fs31.promises.access(p);
|
|
4454
4639
|
return true;
|
|
4455
4640
|
} catch {
|
|
4456
4641
|
return false;
|
|
4457
4642
|
}
|
|
4458
4643
|
}
|
|
4459
4644
|
async function copyDirRecursive(srcDir, destDir) {
|
|
4460
|
-
await
|
|
4461
|
-
const entries = await
|
|
4645
|
+
await fs31.promises.mkdir(destDir, { recursive: true });
|
|
4646
|
+
const entries = await fs31.promises.readdir(srcDir, { withFileTypes: true });
|
|
4462
4647
|
for (const entry of entries) {
|
|
4463
|
-
const src =
|
|
4464
|
-
const dest =
|
|
4648
|
+
const src = path30.join(srcDir, entry.name);
|
|
4649
|
+
const dest = path30.join(destDir, entry.name);
|
|
4465
4650
|
if (entry.isDirectory()) {
|
|
4466
4651
|
await copyDirRecursive(src, dest);
|
|
4467
4652
|
} else if (entry.isSymbolicLink()) {
|
|
4468
4653
|
continue;
|
|
4469
4654
|
} else if (entry.isFile()) {
|
|
4470
4655
|
if (entry.name === "template.json") continue;
|
|
4471
|
-
await
|
|
4656
|
+
await fs31.promises.copyFile(src, dest);
|
|
4472
4657
|
}
|
|
4473
4658
|
}
|
|
4474
4659
|
}
|
|
@@ -4476,9 +4661,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
4476
4661
|
const stack = [projectDir];
|
|
4477
4662
|
while (stack.length > 0) {
|
|
4478
4663
|
const currentDir = stack.pop();
|
|
4479
|
-
const entries = await
|
|
4664
|
+
const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
|
|
4480
4665
|
for (const entry of entries) {
|
|
4481
|
-
const fullPath =
|
|
4666
|
+
const fullPath = path30.join(currentDir, entry.name);
|
|
4482
4667
|
if (entry.isDirectory()) {
|
|
4483
4668
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4484
4669
|
stack.push(fullPath);
|
|
@@ -4500,23 +4685,23 @@ function toJavaPropertiesPath(p) {
|
|
|
4500
4685
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
4501
4686
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
4502
4687
|
`;
|
|
4503
|
-
|
|
4688
|
+
fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
|
|
4504
4689
|
}
|
|
4505
4690
|
function renameIfExists(fromPath, toPath) {
|
|
4506
|
-
if (
|
|
4507
|
-
|
|
4691
|
+
if (fs31.existsSync(fromPath) && fromPath !== toPath) {
|
|
4692
|
+
fs31.renameSync(fromPath, toPath);
|
|
4508
4693
|
}
|
|
4509
4694
|
}
|
|
4510
4695
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
4511
|
-
const appJsonPath =
|
|
4512
|
-
if (
|
|
4696
|
+
const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
|
|
4697
|
+
if (fs31.existsSync(appJsonPath)) {
|
|
4513
4698
|
const appJson = readJson5File(appJsonPath);
|
|
4514
4699
|
appJson.app = appJson.app ?? {};
|
|
4515
4700
|
appJson.app.bundleName = args.bundleName;
|
|
4516
4701
|
writeJson5File(appJsonPath, appJson);
|
|
4517
4702
|
}
|
|
4518
|
-
const stringsPath =
|
|
4519
|
-
if (
|
|
4703
|
+
const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
4704
|
+
if (fs31.existsSync(stringsPath)) {
|
|
4520
4705
|
const strings = readJsonFile(stringsPath);
|
|
4521
4706
|
if (Array.isArray(strings.string)) {
|
|
4522
4707
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -4524,8 +4709,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4524
4709
|
}
|
|
4525
4710
|
writeJsonFile(stringsPath, strings);
|
|
4526
4711
|
}
|
|
4527
|
-
const buildProfilePath =
|
|
4528
|
-
if (
|
|
4712
|
+
const buildProfilePath = path30.join(projectDir, "build-profile.json5");
|
|
4713
|
+
if (fs31.existsSync(buildProfilePath)) {
|
|
4529
4714
|
const buildProfile = readJson5File(buildProfilePath);
|
|
4530
4715
|
buildProfile.app = buildProfile.app ?? {};
|
|
4531
4716
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -4538,23 +4723,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
4538
4723
|
}
|
|
4539
4724
|
writeJson5File(buildProfilePath, buildProfile);
|
|
4540
4725
|
}
|
|
4541
|
-
const moduleJsonPath =
|
|
4542
|
-
if (
|
|
4726
|
+
const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
4727
|
+
if (fs31.existsSync(moduleJsonPath)) {
|
|
4543
4728
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
4544
4729
|
moduleJson.module = moduleJson.module ?? {};
|
|
4545
4730
|
moduleJson.module.name = args.moduleName;
|
|
4546
4731
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
4547
4732
|
}
|
|
4548
|
-
const moduleOhPackagePath =
|
|
4549
|
-
if (
|
|
4733
|
+
const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
|
|
4734
|
+
if (fs31.existsSync(moduleOhPackagePath)) {
|
|
4550
4735
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
4551
4736
|
pkg.name = args.moduleName;
|
|
4552
4737
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
4553
4738
|
}
|
|
4554
|
-
const vscodeDir =
|
|
4555
|
-
|
|
4739
|
+
const vscodeDir = path30.join(projectDir, ".vscode");
|
|
4740
|
+
fs31.mkdirSync(vscodeDir, { recursive: true });
|
|
4556
4741
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
4557
|
-
writeJsonFile(
|
|
4742
|
+
writeJsonFile(path30.join(vscodeDir, "settings.json"), {
|
|
4558
4743
|
"oniro.hapPath": hapPath,
|
|
4559
4744
|
"files.associations": { "*.json5": "jsonc" }
|
|
4560
4745
|
});
|
|
@@ -4571,7 +4756,7 @@ async function createScaffold(opts) {
|
|
|
4571
4756
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
4572
4757
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
4573
4758
|
}
|
|
4574
|
-
const templateDir =
|
|
4759
|
+
const templateDir = path30.join(opts.templateRoot, opts.templateId);
|
|
4575
4760
|
if (!await pathExists(templateDir)) {
|
|
4576
4761
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
4577
4762
|
}
|
|
@@ -4606,17 +4791,17 @@ async function createScaffold(opts) {
|
|
|
4606
4791
|
}
|
|
4607
4792
|
}
|
|
4608
4793
|
}
|
|
4609
|
-
const projectDir =
|
|
4794
|
+
const projectDir = path30.join(opts.location, opts.projectName);
|
|
4610
4795
|
if (await pathExists(projectDir)) {
|
|
4611
4796
|
if (!opts.overwrite) {
|
|
4612
4797
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
4613
4798
|
}
|
|
4614
|
-
await
|
|
4799
|
+
await fs31.promises.rm(projectDir, { recursive: true, force: true });
|
|
4615
4800
|
}
|
|
4616
4801
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
4617
4802
|
await copyDirRecursive(templateDir, projectDir);
|
|
4618
4803
|
if (moduleName !== defaultModuleName) {
|
|
4619
|
-
renameIfExists(
|
|
4804
|
+
renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
|
|
4620
4805
|
}
|
|
4621
4806
|
updateTemplateConfigs(
|
|
4622
4807
|
projectDir,
|
|
@@ -4633,11 +4818,11 @@ async function createScaffold(opts) {
|
|
|
4633
4818
|
}
|
|
4634
4819
|
|
|
4635
4820
|
// src/project/listModules.ts
|
|
4636
|
-
var
|
|
4637
|
-
var
|
|
4821
|
+
var fs32 = __toESM(require("fs"), 1);
|
|
4822
|
+
var path31 = __toESM(require("path"), 1);
|
|
4638
4823
|
function listModules(opts) {
|
|
4639
|
-
const buildProfile =
|
|
4640
|
-
if (!
|
|
4824
|
+
const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
|
|
4825
|
+
if (!fs32.existsSync(buildProfile)) {
|
|
4641
4826
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
4642
4827
|
}
|
|
4643
4828
|
const parsed = readJson5File(buildProfile);
|
|
@@ -4649,12 +4834,12 @@ function listModules(opts) {
|
|
|
4649
4834
|
}
|
|
4650
4835
|
|
|
4651
4836
|
// src/project/abilities.ts
|
|
4652
|
-
var
|
|
4653
|
-
var
|
|
4837
|
+
var fs33 = __toESM(require("fs"), 1);
|
|
4838
|
+
var path32 = __toESM(require("path"), 1);
|
|
4654
4839
|
function listAbilities(opts) {
|
|
4655
4840
|
const moduleName = opts.moduleName ?? "entry";
|
|
4656
|
-
const moduleJsonPath =
|
|
4657
|
-
if (!
|
|
4841
|
+
const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
4842
|
+
if (!fs33.existsSync(moduleJsonPath)) {
|
|
4658
4843
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
4659
4844
|
}
|
|
4660
4845
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -4662,12 +4847,12 @@ function listAbilities(opts) {
|
|
|
4662
4847
|
}
|
|
4663
4848
|
|
|
4664
4849
|
// src/project/prepareSigning.ts
|
|
4665
|
-
var
|
|
4666
|
-
var
|
|
4850
|
+
var fs34 = __toESM(require("fs"), 1);
|
|
4851
|
+
var path33 = __toESM(require("path"), 1);
|
|
4667
4852
|
function hasSigningArtifacts(dir) {
|
|
4668
|
-
if (!
|
|
4669
|
-
const buildProfile =
|
|
4670
|
-
if (!
|
|
4853
|
+
if (!fs34.existsSync(path33.join(dir, "signatures"))) return false;
|
|
4854
|
+
const buildProfile = path33.join(dir, "build-profile.json5");
|
|
4855
|
+
if (!fs34.existsSync(buildProfile)) return false;
|
|
4671
4856
|
try {
|
|
4672
4857
|
const parsed = readJson5File(buildProfile);
|
|
4673
4858
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -4746,7 +4931,6 @@ async function getDeviceInfo(opts) {
|
|
|
4746
4931
|
}
|
|
4747
4932
|
|
|
4748
4933
|
// src/install/diffHapAssets.ts
|
|
4749
|
-
var import_node_stream_zip2 = __toESM(require("node-stream-zip"), 1);
|
|
4750
4934
|
function diffEntryNames(installed, next) {
|
|
4751
4935
|
const installedSet = new Set(installed);
|
|
4752
4936
|
const nextSet = new Set(next);
|
|
@@ -4755,14 +4939,11 @@ function diffEntryNames(installed, next) {
|
|
|
4755
4939
|
removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
|
|
4756
4940
|
};
|
|
4757
4941
|
}
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
} finally {
|
|
4764
|
-
await zip.close();
|
|
4765
|
-
}
|
|
4942
|
+
function listFileEntries(hapPath) {
|
|
4943
|
+
return withHapZip(
|
|
4944
|
+
hapPath,
|
|
4945
|
+
(zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
|
|
4946
|
+
);
|
|
4766
4947
|
}
|
|
4767
4948
|
async function diffHapAssets(opts) {
|
|
4768
4949
|
const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
|
|
@@ -4770,8 +4951,8 @@ async function diffHapAssets(opts) {
|
|
|
4770
4951
|
}
|
|
4771
4952
|
|
|
4772
4953
|
// src/install/apply.ts
|
|
4773
|
-
var
|
|
4774
|
-
var
|
|
4954
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
4955
|
+
var path34 = __toESM(require("path"), 1);
|
|
4775
4956
|
|
|
4776
4957
|
// src/install/hapBundleName.ts
|
|
4777
4958
|
function bundleNameFromHapConfig(text) {
|
|
@@ -4809,7 +4990,7 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
4809
4990
|
async function resolveHap(opts, logger) {
|
|
4810
4991
|
let hap;
|
|
4811
4992
|
if (opts.hapPath) {
|
|
4812
|
-
const hapPath = !
|
|
4993
|
+
const hapPath = !path34.isAbsolute(opts.hapPath) && opts.projectDir ? path34.join(opts.projectDir, opts.hapPath) : opts.hapPath;
|
|
4813
4994
|
hap = existingHapOrUnsigned(hapPath);
|
|
4814
4995
|
if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
4815
4996
|
} else {
|
|
@@ -4818,7 +4999,7 @@ async function resolveHap(opts, logger) {
|
|
|
4818
4999
|
if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
|
|
4819
5000
|
hap = { path: picked.path, fallback: !picked.signed };
|
|
4820
5001
|
}
|
|
4821
|
-
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${
|
|
5002
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path34.basename(hap.path)}.`);
|
|
4822
5003
|
return hap;
|
|
4823
5004
|
}
|
|
4824
5005
|
async function applyChanges(opts) {
|
|
@@ -4834,12 +5015,12 @@ async function applyChanges(opts) {
|
|
|
4834
5015
|
});
|
|
4835
5016
|
if (hapBundle && hapBundle !== bundle) {
|
|
4836
5017
|
throw new OniroError(
|
|
4837
|
-
`${
|
|
5018
|
+
`${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.`
|
|
4838
5019
|
);
|
|
4839
5020
|
}
|
|
4840
5021
|
logger.info(`installing ${hapPath}`);
|
|
4841
5022
|
let assetsChanged = false;
|
|
4842
|
-
if (opts.installedHapPath &&
|
|
5023
|
+
if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
|
|
4843
5024
|
try {
|
|
4844
5025
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
4845
5026
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -4872,7 +5053,7 @@ async function applyChanges(opts) {
|
|
|
4872
5053
|
}
|
|
4873
5054
|
if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
|
|
4874
5055
|
throw new OniroError(
|
|
4875
|
-
`hdc reported ${
|
|
5056
|
+
`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).`
|
|
4876
5057
|
);
|
|
4877
5058
|
}
|
|
4878
5059
|
let postInstallPid = await pidOf(config, bundle, deviceSerial);
|
|
@@ -4949,10 +5130,10 @@ var AUTH_PATHS = {
|
|
|
4949
5130
|
};
|
|
4950
5131
|
|
|
4951
5132
|
// src/harmonyos/auth/tokenStore.ts
|
|
4952
|
-
var
|
|
4953
|
-
var
|
|
4954
|
-
var
|
|
4955
|
-
var
|
|
5133
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
5134
|
+
var os12 = __toESM(require("os"), 1);
|
|
5135
|
+
var path35 = __toESM(require("path"), 1);
|
|
5136
|
+
var crypto4 = __toESM(require("crypto"), 1);
|
|
4956
5137
|
var ALGORITHM = "aes-256-gcm";
|
|
4957
5138
|
var KEY_LENGTH = 32;
|
|
4958
5139
|
var IV_LENGTH = 12;
|
|
@@ -4961,8 +5142,8 @@ var KEY_FILE = "kek.bin";
|
|
|
4961
5142
|
var DIR_MODE = 448;
|
|
4962
5143
|
var FILE_MODE = 384;
|
|
4963
5144
|
function encrypt2(key, plaintext) {
|
|
4964
|
-
const iv =
|
|
4965
|
-
const cipher =
|
|
5145
|
+
const iv = crypto4.randomBytes(IV_LENGTH);
|
|
5146
|
+
const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
|
|
4966
5147
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
4967
5148
|
return {
|
|
4968
5149
|
version: 1,
|
|
@@ -4973,7 +5154,7 @@ function encrypt2(key, plaintext) {
|
|
|
4973
5154
|
};
|
|
4974
5155
|
}
|
|
4975
5156
|
function decrypt2(key, blob) {
|
|
4976
|
-
const decipher =
|
|
5157
|
+
const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
|
|
4977
5158
|
decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
|
|
4978
5159
|
return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
|
|
4979
5160
|
}
|
|
@@ -4987,7 +5168,7 @@ function permissionHint(dir) {
|
|
|
4987
5168
|
}
|
|
4988
5169
|
function ensureDir(dir) {
|
|
4989
5170
|
try {
|
|
4990
|
-
|
|
5171
|
+
fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
4991
5172
|
} catch (err) {
|
|
4992
5173
|
if (err.code === "EACCES") {
|
|
4993
5174
|
throw new OniroError(permissionHint(dir), err);
|
|
@@ -4998,17 +5179,17 @@ function ensureDir(dir) {
|
|
|
4998
5179
|
function createTokenStore(opts) {
|
|
4999
5180
|
const logger = opts.logger ?? noopLogger;
|
|
5000
5181
|
const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
|
|
5001
|
-
const tokenPath =
|
|
5002
|
-
const keyPath =
|
|
5182
|
+
const tokenPath = path35.join(authDir, TOKEN_FILE);
|
|
5183
|
+
const keyPath = path35.join(os12.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
|
|
5003
5184
|
function loadOrCreateKey() {
|
|
5004
|
-
if (
|
|
5005
|
-
const existing =
|
|
5185
|
+
if (fs36.existsSync(keyPath)) {
|
|
5186
|
+
const existing = fs36.readFileSync(keyPath);
|
|
5006
5187
|
if (existing.length === KEY_LENGTH) return existing;
|
|
5007
5188
|
logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
|
|
5008
5189
|
}
|
|
5009
|
-
ensureDir(
|
|
5010
|
-
const key =
|
|
5011
|
-
|
|
5190
|
+
ensureDir(path35.dirname(keyPath));
|
|
5191
|
+
const key = crypto4.randomBytes(KEY_LENGTH);
|
|
5192
|
+
fs36.writeFileSync(keyPath, key, { mode: FILE_MODE });
|
|
5012
5193
|
return key;
|
|
5013
5194
|
}
|
|
5014
5195
|
return {
|
|
@@ -5018,12 +5199,12 @@ function createTokenStore(opts) {
|
|
|
5018
5199
|
const payload = Buffer.from(JSON.stringify(session), "utf8");
|
|
5019
5200
|
const blob = encrypt2(loadOrCreateKey(), payload);
|
|
5020
5201
|
ensureDir(authDir);
|
|
5021
|
-
|
|
5202
|
+
fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
|
|
5022
5203
|
},
|
|
5023
5204
|
load() {
|
|
5024
|
-
if (!
|
|
5205
|
+
if (!fs36.existsSync(tokenPath)) return null;
|
|
5025
5206
|
try {
|
|
5026
|
-
const blob = JSON.parse(
|
|
5207
|
+
const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
|
|
5027
5208
|
if (!isEncryptedBlob(blob)) return null;
|
|
5028
5209
|
const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
|
|
5029
5210
|
return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
|
|
@@ -5035,7 +5216,7 @@ function createTokenStore(opts) {
|
|
|
5035
5216
|
}
|
|
5036
5217
|
logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
|
|
5037
5218
|
try {
|
|
5038
|
-
|
|
5219
|
+
fs36.rmSync(tokenPath, { force: true });
|
|
5039
5220
|
} catch {
|
|
5040
5221
|
}
|
|
5041
5222
|
return null;
|
|
@@ -5043,7 +5224,7 @@ function createTokenStore(opts) {
|
|
|
5043
5224
|
},
|
|
5044
5225
|
clear() {
|
|
5045
5226
|
try {
|
|
5046
|
-
|
|
5227
|
+
fs36.rmSync(tokenPath, { force: true });
|
|
5047
5228
|
} catch (err) {
|
|
5048
5229
|
throw new OniroError(`Failed to remove ${tokenPath}.`, err);
|
|
5049
5230
|
}
|
|
@@ -5052,7 +5233,7 @@ function createTokenStore(opts) {
|
|
|
5052
5233
|
}
|
|
5053
5234
|
|
|
5054
5235
|
// src/harmonyos/auth/session.ts
|
|
5055
|
-
var
|
|
5236
|
+
var crypto6 = __toESM(require("crypto"), 1);
|
|
5056
5237
|
|
|
5057
5238
|
// src/harmonyos/http.ts
|
|
5058
5239
|
var import_undici = require("undici");
|
|
@@ -5143,13 +5324,13 @@ function createHttpClient(opts = {}) {
|
|
|
5143
5324
|
|
|
5144
5325
|
// src/harmonyos/auth/callbackServer.ts
|
|
5145
5326
|
var http2 = __toESM(require("http"), 1);
|
|
5146
|
-
var
|
|
5327
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
5147
5328
|
var CALLBACK_PATH = "/callback";
|
|
5148
5329
|
var MAX_BODY_BYTES = 65536;
|
|
5149
5330
|
function codeMatches(code, secret) {
|
|
5150
5331
|
const a = Buffer.from(code ?? "", "utf8");
|
|
5151
5332
|
const b = Buffer.from(secret, "utf8");
|
|
5152
|
-
return a.length === b.length &&
|
|
5333
|
+
return a.length === b.length && crypto5.timingSafeEqual(a, b);
|
|
5153
5334
|
}
|
|
5154
5335
|
function isQuit(quit) {
|
|
5155
5336
|
return quit === "true" || quit === "access_denied" || quit === "quit";
|
|
@@ -5157,8 +5338,8 @@ function isQuit(quit) {
|
|
|
5157
5338
|
async function startCallbackServer(opts) {
|
|
5158
5339
|
let resolveCallback;
|
|
5159
5340
|
let rejectCallback;
|
|
5160
|
-
const callback = new Promise((
|
|
5161
|
-
resolveCallback =
|
|
5341
|
+
const callback = new Promise((resolve9, reject) => {
|
|
5342
|
+
resolveCallback = resolve9;
|
|
5162
5343
|
rejectCallback = reject;
|
|
5163
5344
|
});
|
|
5164
5345
|
callback.catch(() => {
|
|
@@ -5217,9 +5398,9 @@ async function startCallbackServer(opts) {
|
|
|
5217
5398
|
}
|
|
5218
5399
|
});
|
|
5219
5400
|
server.keepAliveTimeout = 1;
|
|
5220
|
-
await new Promise((
|
|
5401
|
+
await new Promise((resolve9, reject) => {
|
|
5221
5402
|
server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
|
|
5222
|
-
server.listen(0, "127.0.0.1",
|
|
5403
|
+
server.listen(0, "127.0.0.1", resolve9);
|
|
5223
5404
|
});
|
|
5224
5405
|
const port = server.address().port;
|
|
5225
5406
|
return {
|
|
@@ -5235,12 +5416,12 @@ async function startCallbackServer(opts) {
|
|
|
5235
5416
|
return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
|
|
5236
5417
|
},
|
|
5237
5418
|
stop() {
|
|
5238
|
-
return new Promise((
|
|
5419
|
+
return new Promise((resolve9) => {
|
|
5239
5420
|
server.closeAllConnections?.();
|
|
5240
|
-
const timer = setTimeout(
|
|
5421
|
+
const timer = setTimeout(resolve9, 100);
|
|
5241
5422
|
server.close(() => {
|
|
5242
5423
|
clearTimeout(timer);
|
|
5243
|
-
|
|
5424
|
+
resolve9();
|
|
5244
5425
|
});
|
|
5245
5426
|
});
|
|
5246
5427
|
}
|
|
@@ -5248,7 +5429,7 @@ async function startCallbackServer(opts) {
|
|
|
5248
5429
|
}
|
|
5249
5430
|
|
|
5250
5431
|
// src/harmonyos/auth/browser.ts
|
|
5251
|
-
var
|
|
5432
|
+
var import_node_child_process6 = require("child_process");
|
|
5252
5433
|
function isSafeUrl(url) {
|
|
5253
5434
|
try {
|
|
5254
5435
|
const parsed = new URL(url);
|
|
@@ -5265,18 +5446,18 @@ function openBrowser(url) {
|
|
|
5265
5446
|
return Promise.reject(new OniroError(`Refusing to open unsafe URL: ${JSON.stringify(url)}`));
|
|
5266
5447
|
}
|
|
5267
5448
|
const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", '""', escapeForCmd(url)]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
|
|
5268
|
-
const child = (0,
|
|
5449
|
+
const child = (0, import_node_child_process6.spawn)(command, args, {
|
|
5269
5450
|
stdio: "ignore",
|
|
5270
5451
|
shell: false,
|
|
5271
5452
|
detached: true,
|
|
5272
5453
|
windowsHide: true
|
|
5273
5454
|
});
|
|
5274
5455
|
child.unref();
|
|
5275
|
-
return new Promise((
|
|
5456
|
+
return new Promise((resolve9, reject) => {
|
|
5276
5457
|
child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
|
|
5277
5458
|
child.on(
|
|
5278
5459
|
"exit",
|
|
5279
|
-
(code) => code === 0 ?
|
|
5460
|
+
(code) => code === 0 ? resolve9() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
|
|
5280
5461
|
);
|
|
5281
5462
|
});
|
|
5282
5463
|
}
|
|
@@ -5358,7 +5539,7 @@ function createHarmonyOsSession(opts) {
|
|
|
5358
5539
|
getDeveloperAgreement,
|
|
5359
5540
|
async login(o = {}) {
|
|
5360
5541
|
const start = HUAWEI_SITES.CN.loginUrl;
|
|
5361
|
-
const clientSecret =
|
|
5542
|
+
const clientSecret = crypto6.randomUUID().replace(/-/g, "");
|
|
5362
5543
|
const server = await startCallbackServer({
|
|
5363
5544
|
clientSecret,
|
|
5364
5545
|
baseUrl: start,
|
|
@@ -5479,7 +5660,7 @@ var SIGNING_ERRORS = {
|
|
|
5479
5660
|
DEVICE_ADD: "AppGallery Connect did not register the device.",
|
|
5480
5661
|
DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
|
|
5481
5662
|
DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
|
|
5482
|
-
DEVICE_NONE: "
|
|
5663
|
+
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.",
|
|
5483
5664
|
PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
|
|
5484
5665
|
PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
|
|
5485
5666
|
PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
|
|
@@ -5524,7 +5705,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
|
|
|
5524
5705
|
}
|
|
5525
5706
|
|
|
5526
5707
|
// src/harmonyos/signing/profile.ts
|
|
5527
|
-
var
|
|
5708
|
+
var fs37 = __toESM(require("fs"), 1);
|
|
5528
5709
|
var import_node_crypto3 = require("crypto");
|
|
5529
5710
|
var import_node_forge = __toESM(require("node-forge"), 1);
|
|
5530
5711
|
function extractProfileJson(buffer) {
|
|
@@ -5556,7 +5737,7 @@ function extractProfileJson(buffer) {
|
|
|
5556
5737
|
function readProvisionProfile(profilePath) {
|
|
5557
5738
|
let raw;
|
|
5558
5739
|
try {
|
|
5559
|
-
const json = extractProfileJson(
|
|
5740
|
+
const json = extractProfileJson(fs37.readFileSync(profilePath));
|
|
5560
5741
|
if (!json) return null;
|
|
5561
5742
|
raw = JSON.parse(json);
|
|
5562
5743
|
} catch {
|
|
@@ -5585,7 +5766,7 @@ function parseCertificate(pem) {
|
|
|
5585
5766
|
}
|
|
5586
5767
|
function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
5587
5768
|
try {
|
|
5588
|
-
const der = import_node_forge.default.util.createBuffer(
|
|
5769
|
+
const der = import_node_forge.default.util.createBuffer(fs37.readFileSync(p12Path).toString("binary"));
|
|
5589
5770
|
const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
|
|
5590
5771
|
const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
|
|
5591
5772
|
const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
|
|
@@ -5601,7 +5782,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
|
5601
5782
|
}
|
|
5602
5783
|
}
|
|
5603
5784
|
function verifySigningMaterial(opts) {
|
|
5604
|
-
const pems =
|
|
5785
|
+
const pems = fs37.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
5605
5786
|
const chain = (pems ?? []).map(parseCertificate);
|
|
5606
5787
|
const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
|
|
5607
5788
|
if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
|
|
@@ -5619,15 +5800,15 @@ function verifySigningMaterial(opts) {
|
|
|
5619
5800
|
}
|
|
5620
5801
|
|
|
5621
5802
|
// src/harmonyos/signing/autoSign.ts
|
|
5622
|
-
var
|
|
5623
|
-
var
|
|
5624
|
-
var
|
|
5803
|
+
var crypto10 = __toESM(require("crypto"), 1);
|
|
5804
|
+
var fs42 = __toESM(require("fs"), 1);
|
|
5805
|
+
var path41 = __toESM(require("path"), 1);
|
|
5625
5806
|
var import_json57 = __toESM(require("json5"), 1);
|
|
5626
5807
|
|
|
5627
5808
|
// src/harmonyos/signing/agc.ts
|
|
5628
|
-
var
|
|
5629
|
-
var
|
|
5630
|
-
var
|
|
5809
|
+
var crypto7 = __toESM(require("crypto"), 1);
|
|
5810
|
+
var fs38 = __toESM(require("fs"), 1);
|
|
5811
|
+
var path36 = __toESM(require("path"), 1);
|
|
5631
5812
|
var ENDPOINTS = {
|
|
5632
5813
|
CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
|
|
5633
5814
|
CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
|
|
@@ -5675,7 +5856,7 @@ function createAgcClient(http3, auth) {
|
|
|
5675
5856
|
}
|
|
5676
5857
|
},
|
|
5677
5858
|
async addDevice(udid, deviceKind) {
|
|
5678
|
-
const deviceName = `oniro_app_device_${
|
|
5859
|
+
const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
|
|
5679
5860
|
await call("device", "post", ENDPOINTS.DEVICE_ADD, {
|
|
5680
5861
|
body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
|
|
5681
5862
|
});
|
|
@@ -5709,12 +5890,12 @@ function createAgcClient(http3, auth) {
|
|
|
5709
5890
|
const target = urlsInfo?.[0];
|
|
5710
5891
|
if (!target) throw mapAgcError(operation, 200, "", "");
|
|
5711
5892
|
const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
|
|
5712
|
-
const actual =
|
|
5893
|
+
const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
|
|
5713
5894
|
if (target.sha256 && actual !== target.sha256.toLowerCase()) {
|
|
5714
5895
|
throw new ChecksumMismatchError(target.sha256, actual);
|
|
5715
5896
|
}
|
|
5716
|
-
|
|
5717
|
-
|
|
5897
|
+
fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
|
|
5898
|
+
fs38.writeFileSync(dest, buffer, { mode: 384 });
|
|
5718
5899
|
}
|
|
5719
5900
|
};
|
|
5720
5901
|
}
|
|
@@ -5728,21 +5909,31 @@ function parseDeviceKind(stdout) {
|
|
|
5728
5909
|
}
|
|
5729
5910
|
async function collectLocalDevices(config, logger) {
|
|
5730
5911
|
const devices = [];
|
|
5912
|
+
const skipped = [];
|
|
5731
5913
|
for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
|
|
5732
|
-
|
|
5914
|
+
const skip = (why) => {
|
|
5915
|
+
logger.warn(`[harmonyos] ${target.serial} ${why}`);
|
|
5916
|
+
skipped.push(target.serial);
|
|
5917
|
+
};
|
|
5918
|
+
if (!isUsableDevice(target)) {
|
|
5919
|
+
skip(
|
|
5920
|
+
target.status === "Unauthorized" ? "is unauthorized; accept the debugging prompt on the device to register it." : `is ${target.status.toLowerCase()}; skipping it.`
|
|
5921
|
+
);
|
|
5922
|
+
continue;
|
|
5923
|
+
}
|
|
5733
5924
|
const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
|
|
5734
5925
|
try {
|
|
5735
5926
|
const udid = parseUdid(await shell2("bm", "get", "-u"));
|
|
5736
5927
|
if (!udid) {
|
|
5737
|
-
|
|
5928
|
+
skip("did not report a UDID (`bm get -u`), so it cannot be registered.");
|
|
5738
5929
|
continue;
|
|
5739
5930
|
}
|
|
5740
5931
|
devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
|
|
5741
5932
|
} catch (err) {
|
|
5742
|
-
|
|
5933
|
+
skip(`could not be read: ${err.message}`);
|
|
5743
5934
|
}
|
|
5744
5935
|
}
|
|
5745
|
-
return devices;
|
|
5936
|
+
return { devices, skipped };
|
|
5746
5937
|
}
|
|
5747
5938
|
async function registerDevices(agc, connected, logger) {
|
|
5748
5939
|
let devices = await agc.listDevices();
|
|
@@ -5755,32 +5946,27 @@ async function registerDevices(agc, connected, logger) {
|
|
|
5755
5946
|
if (unregistered.length > 0) devices = await agc.listDevices();
|
|
5756
5947
|
const deviceIds = devices.map((d) => d.id);
|
|
5757
5948
|
if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
|
|
5758
|
-
if (connected.length === 0) {
|
|
5759
|
-
logger.warn(
|
|
5760
|
-
`[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
|
|
5761
|
-
);
|
|
5762
|
-
}
|
|
5763
5949
|
return deviceIds;
|
|
5764
5950
|
}
|
|
5765
5951
|
|
|
5766
5952
|
// src/harmonyos/signing/keystore.ts
|
|
5767
|
-
var
|
|
5768
|
-
var
|
|
5769
|
-
var
|
|
5953
|
+
var crypto8 = __toESM(require("crypto"), 1);
|
|
5954
|
+
var fs40 = __toESM(require("fs"), 1);
|
|
5955
|
+
var path38 = __toESM(require("path"), 1);
|
|
5770
5956
|
|
|
5771
5957
|
// src/harmonyos/java.ts
|
|
5772
|
-
var
|
|
5773
|
-
var
|
|
5958
|
+
var fs39 = __toESM(require("fs"), 1);
|
|
5959
|
+
var path37 = __toESM(require("path"), 1);
|
|
5774
5960
|
var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
|
|
5775
5961
|
function findJava(sdk, env = process.env) {
|
|
5776
5962
|
const candidates = [
|
|
5777
|
-
env.JAVA_HOME &&
|
|
5778
|
-
sdk &&
|
|
5963
|
+
env.JAVA_HOME && path37.join(env.JAVA_HOME, "bin", JAVA_BIN),
|
|
5964
|
+
sdk && path37.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
|
|
5779
5965
|
// macOS: the JBR is itself a bundle inside Studio's Contents.
|
|
5780
|
-
sdk &&
|
|
5781
|
-
...(env.PATH ?? "").split(
|
|
5966
|
+
sdk && path37.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
|
|
5967
|
+
...(env.PATH ?? "").split(path37.delimiter).map((dir) => dir && path37.join(dir, JAVA_BIN))
|
|
5782
5968
|
];
|
|
5783
|
-
const found = candidates.find((c) => !!c &&
|
|
5969
|
+
const found = candidates.find((c) => !!c && fs39.existsSync(c));
|
|
5784
5970
|
if (found) return found;
|
|
5785
5971
|
throw new OniroError(
|
|
5786
5972
|
"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."
|
|
@@ -5793,49 +5979,77 @@ var SIGN_ALG = "SHA256withECDSA";
|
|
|
5793
5979
|
function redactArgs(args) {
|
|
5794
5980
|
return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
|
|
5795
5981
|
}
|
|
5796
|
-
|
|
5797
|
-
const jar =
|
|
5798
|
-
if (!
|
|
5982
|
+
function hapSignToolJar(sdk) {
|
|
5983
|
+
const jar = path38.join(sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
|
|
5984
|
+
if (!fs40.existsSync(jar)) {
|
|
5799
5985
|
throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
|
|
5800
5986
|
}
|
|
5987
|
+
return jar;
|
|
5988
|
+
}
|
|
5989
|
+
async function runHapSignTool(opts) {
|
|
5990
|
+
const jar = hapSignToolJar(opts.sdk);
|
|
5801
5991
|
const java = findJava(opts.sdk);
|
|
5802
|
-
const
|
|
5803
|
-
|
|
5804
|
-
const
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
if (result.code !== 0) {
|
|
5809
|
-
const detail = (result.stderr || result.stdout || "").trim();
|
|
5810
|
-
throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
|
|
5992
|
+
const argv = ["-jar", jar, ...opts.args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", opts.keyPwd, "-keyPwd", opts.keyPwd];
|
|
5993
|
+
opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
|
|
5994
|
+
const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
|
|
5995
|
+
if (result.code !== 0) {
|
|
5996
|
+
const detail = (result.stderr || result.stdout || "").trim();
|
|
5997
|
+
throw new OniroError(`hap-sign-tool failed to ${opts.what} (exit ${result.code}).${detail ? `
|
|
5811
5998
|
${detail}` : ""}`);
|
|
5812
|
-
|
|
5813
|
-
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
async function generateKeystoreAndCsr(opts) {
|
|
6002
|
+
hapSignToolJar(opts.sdk);
|
|
6003
|
+
findJava(opts.sdk);
|
|
6004
|
+
const keyPwd = crypto8.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
|
|
6005
|
+
fs40.mkdirSync(path38.dirname(opts.p12Path), { recursive: true, mode: 448 });
|
|
6006
|
+
const run = (what, args) => runHapSignTool({ ...opts, what, args, keyPwd });
|
|
5814
6007
|
opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
|
|
5815
6008
|
await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
|
|
5816
6009
|
await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
|
|
5817
|
-
return { keyPwd, csr:
|
|
6010
|
+
return { keyPwd, csr: fs40.readFileSync(opts.csrPath, "utf8") };
|
|
6011
|
+
}
|
|
6012
|
+
async function signHapFile(opts) {
|
|
6013
|
+
await runHapSignTool({
|
|
6014
|
+
...opts,
|
|
6015
|
+
what: `sign ${opts.inFile}`,
|
|
6016
|
+
args: [
|
|
6017
|
+
"sign-app",
|
|
6018
|
+
"-mode",
|
|
6019
|
+
"localSign",
|
|
6020
|
+
"-signAlg",
|
|
6021
|
+
SIGN_ALG,
|
|
6022
|
+
"-appCertFile",
|
|
6023
|
+
opts.cerPath,
|
|
6024
|
+
"-profileFile",
|
|
6025
|
+
opts.profilePath,
|
|
6026
|
+
"-inFile",
|
|
6027
|
+
opts.inFile,
|
|
6028
|
+
"-outFile",
|
|
6029
|
+
opts.outFile
|
|
6030
|
+
]
|
|
6031
|
+
});
|
|
5818
6032
|
}
|
|
5819
6033
|
|
|
5820
6034
|
// src/harmonyos/signing/paths.ts
|
|
5821
|
-
var
|
|
5822
|
-
var
|
|
6035
|
+
var crypto9 = __toESM(require("crypto"), 1);
|
|
6036
|
+
var path39 = __toESM(require("path"), 1);
|
|
5823
6037
|
function sanitizeNamePart(raw) {
|
|
5824
6038
|
const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
|
|
5825
6039
|
return safe.slice(0, 64) || "default";
|
|
5826
6040
|
}
|
|
5827
6041
|
function resolveSigningMaterialPaths(config, productName, projectRoot) {
|
|
5828
|
-
const hash =
|
|
5829
|
-
const base =
|
|
6042
|
+
const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
|
|
6043
|
+
const base = path39.join(
|
|
5830
6044
|
config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
|
|
5831
|
-
`${sanitizeNamePart(productName)}_${
|
|
6045
|
+
`${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
|
|
5832
6046
|
);
|
|
5833
6047
|
return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
|
|
5834
6048
|
}
|
|
5835
6049
|
|
|
5836
6050
|
// src/harmonyos/signing/project.ts
|
|
5837
|
-
var
|
|
5838
|
-
var
|
|
6051
|
+
var fs41 = __toESM(require("fs"), 1);
|
|
6052
|
+
var path40 = __toESM(require("path"), 1);
|
|
5839
6053
|
var import_json56 = __toESM(require("json5"), 1);
|
|
5840
6054
|
|
|
5841
6055
|
// src/harmonyos/signing/aclPermissionList.ts
|
|
@@ -5903,9 +6117,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
|
|
|
5903
6117
|
|
|
5904
6118
|
// src/harmonyos/signing/project.ts
|
|
5905
6119
|
function readRequestedPermissions(moduleConfigPath) {
|
|
5906
|
-
if (!
|
|
6120
|
+
if (!fs41.existsSync(moduleConfigPath)) return [];
|
|
5907
6121
|
try {
|
|
5908
|
-
const parsed = import_json56.default.parse(
|
|
6122
|
+
const parsed = import_json56.default.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
|
|
5909
6123
|
return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
5910
6124
|
} catch {
|
|
5911
6125
|
return [];
|
|
@@ -5914,32 +6128,38 @@ function readRequestedPermissions(moduleConfigPath) {
|
|
|
5914
6128
|
function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
5915
6129
|
const requested = /* @__PURE__ */ new Set();
|
|
5916
6130
|
for (const module2 of listModules({ projectDir })) {
|
|
5917
|
-
const moduleRoot =
|
|
6131
|
+
const moduleRoot = path40.resolve(projectDir, module2.srcPath);
|
|
5918
6132
|
for (const sourceSet of ["main", "ohosTest"]) {
|
|
5919
6133
|
for (const name of readRequestedPermissions(
|
|
5920
|
-
|
|
6134
|
+
path40.join(moduleRoot, "src", sourceSet, "module.json5")
|
|
5921
6135
|
)) {
|
|
5922
6136
|
requested.add(name);
|
|
5923
6137
|
}
|
|
5924
6138
|
}
|
|
5925
6139
|
}
|
|
5926
|
-
const acls =
|
|
6140
|
+
const acls = aclPermissionsAmong(requested);
|
|
5927
6141
|
if (acls.length > 0) {
|
|
5928
6142
|
logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
|
|
5929
6143
|
}
|
|
5930
6144
|
return acls;
|
|
5931
6145
|
}
|
|
6146
|
+
function aclPermissionsAmong(names) {
|
|
6147
|
+
return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
|
|
6148
|
+
}
|
|
5932
6149
|
|
|
5933
6150
|
// src/harmonyos/signing/autoSign.ts
|
|
5934
6151
|
function shouldRegenerate(input) {
|
|
5935
6152
|
if (input.force) return "--force was passed";
|
|
5936
|
-
const missing = Object.values(input.paths).find((file) => !
|
|
6153
|
+
const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
|
|
5937
6154
|
if (missing) return `${missing} is missing`;
|
|
5938
6155
|
const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
|
|
5939
6156
|
if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
|
|
5940
6157
|
return "build-profile.json5 has no signingConfig for this material";
|
|
5941
6158
|
}
|
|
5942
|
-
|
|
6159
|
+
return profileProblem({ ...input, profilePath: input.paths.profilePath });
|
|
6160
|
+
}
|
|
6161
|
+
function profileProblem(input) {
|
|
6162
|
+
const profile = readProvisionProfile(input.profilePath);
|
|
5943
6163
|
if (!profile) return "the provisioning profile could not be read";
|
|
5944
6164
|
if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
|
|
5945
6165
|
if (profile.developerId && profile.developerId !== input.teamId) {
|
|
@@ -5972,31 +6192,29 @@ function writeSigningConfig(opts) {
|
|
|
5972
6192
|
for (const product of app.products ?? []) {
|
|
5973
6193
|
if (product.name === opts.productName) product.signingConfig = opts.productName;
|
|
5974
6194
|
}
|
|
5975
|
-
|
|
6195
|
+
fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
|
|
5976
6196
|
}
|
|
5977
6197
|
async function harmonyOsAutoSign(opts) {
|
|
5978
6198
|
const logger = opts.logger ?? noopLogger;
|
|
5979
6199
|
const productName = opts.productName ?? "default";
|
|
5980
|
-
const projectDir =
|
|
5981
|
-
const buildProfilePath =
|
|
6200
|
+
const projectDir = path41.resolve(opts.projectDir);
|
|
6201
|
+
const buildProfilePath = path41.join(projectDir, "build-profile.json5");
|
|
5982
6202
|
let buildProfile;
|
|
5983
6203
|
try {
|
|
5984
|
-
buildProfile = import_json57.default.parse(
|
|
6204
|
+
buildProfile = import_json57.default.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
|
|
5985
6205
|
} catch (err) {
|
|
5986
6206
|
throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
|
|
5987
6207
|
}
|
|
5988
6208
|
const product = buildProfile.app?.products?.find((p) => p.name === productName);
|
|
5989
6209
|
if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
|
|
5990
6210
|
const bundleName = product.bundleName || getBundleName(projectDir);
|
|
5991
|
-
|
|
5992
|
-
throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
|
|
5993
|
-
}
|
|
6211
|
+
assertAgcBundleName(bundleName);
|
|
5994
6212
|
const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
|
|
5995
6213
|
const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
|
|
5996
6214
|
const aclPermissions = collectAclPermissions(projectDir, logger);
|
|
5997
6215
|
const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
|
|
5998
6216
|
logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
|
|
5999
|
-
const
|
|
6217
|
+
const local = await collectLocalDevices(opts.config, logger);
|
|
6000
6218
|
const result = (regenerated) => ({
|
|
6001
6219
|
bundleName,
|
|
6002
6220
|
teamId: auth.teamId,
|
|
@@ -6010,7 +6228,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
6010
6228
|
productName,
|
|
6011
6229
|
bundleName,
|
|
6012
6230
|
teamId: auth.teamId,
|
|
6013
|
-
connectedUdids:
|
|
6231
|
+
connectedUdids: local.devices.map((d) => d.udid),
|
|
6014
6232
|
aclPermissions,
|
|
6015
6233
|
force: opts.force
|
|
6016
6234
|
});
|
|
@@ -6019,10 +6237,47 @@ async function harmonyOsAutoSign(opts) {
|
|
|
6019
6237
|
return result(false);
|
|
6020
6238
|
}
|
|
6021
6239
|
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
6022
|
-
const
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6240
|
+
const keyPwd = await issueSigningMaterial({
|
|
6241
|
+
agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
|
|
6242
|
+
local,
|
|
6243
|
+
sdk,
|
|
6244
|
+
paths,
|
|
6245
|
+
bundleName,
|
|
6246
|
+
aclPermissions,
|
|
6247
|
+
teamId: auth.teamId,
|
|
6248
|
+
logger
|
|
6249
|
+
});
|
|
6250
|
+
const encryptedPassword = encryptPwd(keyPwd, sharedMaterialDir(paths));
|
|
6251
|
+
writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword });
|
|
6252
|
+
logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
|
|
6253
|
+
return result(true);
|
|
6254
|
+
}
|
|
6255
|
+
function assertAgcBundleName(bundleName) {
|
|
6256
|
+
if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
|
|
6257
|
+
throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
function materialDirFor(paths) {
|
|
6261
|
+
return path41.join(path41.dirname(paths.p12Path), "material");
|
|
6262
|
+
}
|
|
6263
|
+
function sharedMaterialDir(paths) {
|
|
6264
|
+
const materialDir = materialDirFor(paths);
|
|
6265
|
+
try {
|
|
6266
|
+
getKey(materialDir);
|
|
6267
|
+
} catch {
|
|
6268
|
+
createMaterial(materialDir);
|
|
6269
|
+
}
|
|
6270
|
+
return materialDir;
|
|
6271
|
+
}
|
|
6272
|
+
async function issueSigningMaterial(opts) {
|
|
6273
|
+
const { agc, local, sdk, paths, logger } = opts;
|
|
6274
|
+
const deviceIds = await registerDevices(agc, local.devices, logger);
|
|
6275
|
+
if (local.devices.length === 0) {
|
|
6276
|
+
const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
|
|
6277
|
+
logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
|
|
6278
|
+
}
|
|
6279
|
+
for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
|
|
6280
|
+
const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
|
|
6026
6281
|
const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
|
|
6027
6282
|
if (stale) {
|
|
6028
6283
|
logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
|
|
@@ -6036,11 +6291,11 @@ async function harmonyOsAutoSign(opts) {
|
|
|
6036
6291
|
await agc.download("cert", cert.certObjectId, paths.cerPath);
|
|
6037
6292
|
logger.info("[harmonyos] Issuing the debug provisioning profile...");
|
|
6038
6293
|
const profile = await agc.addDebugProfile({
|
|
6039
|
-
provisionName: `oniro_${
|
|
6040
|
-
bundleName,
|
|
6294
|
+
provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
|
|
6295
|
+
bundleName: opts.bundleName,
|
|
6041
6296
|
certId: cert.id,
|
|
6042
6297
|
deviceIds,
|
|
6043
|
-
aclPermissions
|
|
6298
|
+
aclPermissions: opts.aclPermissions
|
|
6044
6299
|
});
|
|
6045
6300
|
try {
|
|
6046
6301
|
await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
|
|
@@ -6052,22 +6307,86 @@ async function harmonyOsAutoSign(opts) {
|
|
|
6052
6307
|
try {
|
|
6053
6308
|
verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
|
|
6054
6309
|
} catch (err) {
|
|
6055
|
-
|
|
6310
|
+
fs42.rmSync(paths.profilePath, { force: true });
|
|
6056
6311
|
throw err;
|
|
6057
6312
|
}
|
|
6058
|
-
|
|
6313
|
+
return keyPwd;
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
// src/harmonyos/signing/signHap.ts
|
|
6317
|
+
var fs43 = __toESM(require("fs"), 1);
|
|
6318
|
+
var path42 = __toESM(require("path"), 1);
|
|
6319
|
+
function resolveHapSigningMaterialPaths(config, bundleName) {
|
|
6320
|
+
const paths = resolveSigningMaterialPaths(config, "hap", bundleName);
|
|
6321
|
+
return { ...paths, pwdPath: paths.p12Path.replace(/\.p12$/, ".pwd") };
|
|
6322
|
+
}
|
|
6323
|
+
function readStoredPassword(paths) {
|
|
6059
6324
|
try {
|
|
6060
|
-
|
|
6325
|
+
return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
|
|
6061
6326
|
} catch {
|
|
6062
|
-
|
|
6327
|
+
return null;
|
|
6063
6328
|
}
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6329
|
+
}
|
|
6330
|
+
async function harmonyOsSignHap(opts) {
|
|
6331
|
+
const logger = opts.logger ?? noopLogger;
|
|
6332
|
+
const hapPath = path42.resolve(opts.hapPath);
|
|
6333
|
+
const outPath = path42.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
|
|
6334
|
+
const info = await readHapSigningInfo(hapPath);
|
|
6335
|
+
const { bundleName } = info;
|
|
6336
|
+
if (info.compileSdkType === "OpenHarmony") {
|
|
6337
|
+
throw new OniroError(
|
|
6338
|
+
`${hapPath} was built for OpenHarmony, so a HarmonyOS device would not run it; sign it without --harmonyos.`
|
|
6339
|
+
);
|
|
6340
|
+
}
|
|
6341
|
+
assertAgcBundleName(bundleName);
|
|
6342
|
+
const aclPermissions = aclPermissionsAmong(info.requestedPermissions);
|
|
6343
|
+
if (aclPermissions.length > 0) {
|
|
6344
|
+
logger.info(`[harmonyos] HAP requests ${aclPermissions.length} ACL permission(s): ${aclPermissions.join(", ")}`);
|
|
6345
|
+
}
|
|
6346
|
+
const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
|
|
6347
|
+
const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
|
|
6348
|
+
const paths = resolveHapSigningMaterialPaths(opts.config, bundleName);
|
|
6349
|
+
const { pwdPath, ...material } = paths;
|
|
6350
|
+
logger.info(`[harmonyos] Signing ${path42.basename(hapPath)} as ${bundleName} (team ${auth.teamId}).`);
|
|
6351
|
+
const local = await collectLocalDevices(opts.config, logger);
|
|
6352
|
+
let keyPwd = opts.force ? null : readStoredPassword(paths);
|
|
6353
|
+
const missing = Object.values(material).find((file) => !fs43.existsSync(file));
|
|
6354
|
+
const reason = opts.force ? "--force was passed" : missing ? `${missing} is missing` : keyPwd === null ? "the stored keystore password could not be read" : profileProblem({
|
|
6355
|
+
profilePath: material.profilePath,
|
|
6356
|
+
bundleName,
|
|
6357
|
+
teamId: auth.teamId,
|
|
6358
|
+
connectedUdids: local.devices.map((d) => d.udid),
|
|
6359
|
+
aclPermissions
|
|
6360
|
+
});
|
|
6361
|
+
if (!reason && keyPwd !== null) {
|
|
6362
|
+
logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
|
|
6363
|
+
} else {
|
|
6364
|
+
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
6365
|
+
fs43.rmSync(pwdPath, { force: true });
|
|
6366
|
+
keyPwd = await issueSigningMaterial({
|
|
6367
|
+
agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
|
|
6368
|
+
local,
|
|
6369
|
+
sdk,
|
|
6370
|
+
paths: material,
|
|
6371
|
+
bundleName,
|
|
6372
|
+
aclPermissions,
|
|
6373
|
+
teamId: auth.teamId,
|
|
6374
|
+
logger
|
|
6375
|
+
});
|
|
6376
|
+
fs43.writeFileSync(pwdPath, encryptPwd(keyPwd, sharedMaterialDir(material)), { mode: 384 });
|
|
6377
|
+
}
|
|
6378
|
+
const password = keyPwd;
|
|
6379
|
+
await writeHapAtomically(
|
|
6380
|
+
outPath,
|
|
6381
|
+
(tmpPath) => signHapFile({ sdk, inFile: hapPath, outFile: tmpPath, ...material, keyPwd: password, logger })
|
|
6382
|
+
);
|
|
6383
|
+
const deviceIds = readProvisionProfile(material.profilePath)?.deviceIds ?? [];
|
|
6384
|
+
logger.info(`[harmonyos] Wrote ${outPath}.`);
|
|
6385
|
+
return { bundleName, teamId: auth.teamId, paths: material, regenerated: !!reason, deviceIds, outPath };
|
|
6067
6386
|
}
|
|
6068
6387
|
|
|
6069
6388
|
// src/index.ts
|
|
6070
|
-
var VERSION = "0.
|
|
6389
|
+
var VERSION = "0.16.0";
|
|
6071
6390
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6072
6391
|
0 && (module.exports = {
|
|
6073
6392
|
ALL_SDKS,
|
|
@@ -6085,6 +6404,7 @@ var VERSION = "0.15.0";
|
|
|
6085
6404
|
OHOS_DAILY_BUILDS_URL,
|
|
6086
6405
|
OHOS_URL_BASE,
|
|
6087
6406
|
OniroError,
|
|
6407
|
+
SIGNED_HAP_CHUNK_SIZE,
|
|
6088
6408
|
SIGNING_ERRORS,
|
|
6089
6409
|
SdkNotInstalledError,
|
|
6090
6410
|
TMP_DIR_HINT,
|
|
@@ -6120,6 +6440,7 @@ var VERSION = "0.15.0";
|
|
|
6120
6440
|
dedupEntries,
|
|
6121
6441
|
defaultContext,
|
|
6122
6442
|
defaultPaths,
|
|
6443
|
+
defaultSignedHapPath,
|
|
6123
6444
|
describeLayoutNode,
|
|
6124
6445
|
detectAaStartFailure,
|
|
6125
6446
|
detectCmdToolsFlavour,
|
|
@@ -6186,6 +6507,7 @@ var VERSION = "0.15.0";
|
|
|
6186
6507
|
getSupportedSdksForUi,
|
|
6187
6508
|
harmonyOsAutoSign,
|
|
6188
6509
|
harmonyOsSdkLabelForApi,
|
|
6510
|
+
harmonyOsSignHap,
|
|
6189
6511
|
hdcClientFailure,
|
|
6190
6512
|
hdcExec,
|
|
6191
6513
|
hdcTargetArgs,
|
|
@@ -6203,6 +6525,7 @@ var VERSION = "0.15.0";
|
|
|
6203
6525
|
isOutOfSpaceError,
|
|
6204
6526
|
isRamBackedPath,
|
|
6205
6527
|
isUnsignedHap,
|
|
6528
|
+
isUsableDevice,
|
|
6206
6529
|
isValidBundleName,
|
|
6207
6530
|
isValidProjectName,
|
|
6208
6531
|
killProcessTree,
|
|
@@ -6238,6 +6561,7 @@ var VERSION = "0.15.0";
|
|
|
6238
6561
|
prepareSigning,
|
|
6239
6562
|
pruneLayout,
|
|
6240
6563
|
readHapBundleName,
|
|
6564
|
+
readHapSigningInfo,
|
|
6241
6565
|
readJson5File,
|
|
6242
6566
|
readJsonFile,
|
|
6243
6567
|
readProvisionProfile,
|
|
@@ -6265,6 +6589,7 @@ var VERSION = "0.15.0";
|
|
|
6265
6589
|
setHilogLevel,
|
|
6266
6590
|
shell,
|
|
6267
6591
|
shellChecked,
|
|
6592
|
+
signOpenHarmonyHap,
|
|
6268
6593
|
spawnCompat,
|
|
6269
6594
|
startEmulator,
|
|
6270
6595
|
staticConfig,
|
|
@@ -6284,6 +6609,7 @@ var VERSION = "0.15.0";
|
|
|
6284
6609
|
waitForTarget,
|
|
6285
6610
|
watchLog,
|
|
6286
6611
|
waypointsToRawEvents,
|
|
6612
|
+
withHapZip,
|
|
6287
6613
|
withoutWindow,
|
|
6288
6614
|
writeJson5File,
|
|
6289
6615
|
writeJsonFile
|