@applift/core 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1160,13 +1160,111 @@ function assertBetaVersionSupported(ctx) {
1160
1160
  * 产物版本读取(只读不写——版本号变更属开发提交,发版工具只负责读版本防回退)。
1161
1161
  * .hap/.app:pack.info 是纯 JSON,零依赖直接读;
1162
1162
  * .apk:aapt2/aapt dump badging(ANDROID_HOME/SDK 探测,同 oppo 渠道逻辑);
1163
- * .ipa:二进制 plist 解析成本高,不支持自动读(返回 undefined,doctor 提示用 --version/配置)。
1163
+ * .hap/.app 的 pushy 构建时间戳在包内 resources/rawfile/meta.json(pushy hvigor 插件写入),
1164
+ * 由 readArtifactBuildTime 读取——基线 buildTime 必须与设备上报精确一致(不匹配服务端封锁 6h)。
1164
1165
  */
1165
1166
  function readArtifactVersion(artifactPath) {
1166
1167
  const ext = path.extname(artifactPath).toLowerCase();
1167
1168
  if (ext === ".hap" || ext === ".app") return fromPackInfo(artifactPath);
1168
1169
  if (ext === ".apk") return fromAapt(artifactPath);
1169
1170
  }
1171
+ /** 产物 pushy 构建时间戳读取(buildTime 与设备上报必须精确一致,读不出返回 undefined,
1172
+ * 由调用方决定拒绝上传而非猜测)。
1173
+ * .hap/.app:包内(.app 可能嵌套 hap)resources/rawfile/meta.json 的 pushy_build_time;
1174
+ * .apk:AndroidManifest meta-data pushy_build_time(aapt2/aapt xmltree);
1175
+ * .ipa:Payload/*.app/pushy_build_time.txt(官方 Xcode 构建 phase 产物)。 */
1176
+ function readArtifactBuildTime(artifactPath) {
1177
+ const ext = path.extname(artifactPath).toLowerCase();
1178
+ if (ext === ".hap" || ext === ".app") return harmonyBuildTime(artifactPath);
1179
+ if (ext === ".apk") return apkBuildTime(artifactPath);
1180
+ if (ext === ".ipa") return ipaBuildTime(artifactPath);
1181
+ }
1182
+ /** unzip 子进程 stdout(失败/超时返回 undefined,调用方按读不出处理) */
1183
+ function unzipStdout(args) {
1184
+ const r = spawnSync("unzip", args, {
1185
+ timeout: 2e4,
1186
+ encoding: "utf8",
1187
+ maxBuffer: 67108864
1188
+ });
1189
+ return !r.error && r.status === 0 ? r.stdout : void 0;
1190
+ }
1191
+ function metaJsonBuildTime(text) {
1192
+ if (!text) return void 0;
1193
+ let meta;
1194
+ try {
1195
+ meta = JSON.parse(text);
1196
+ } catch {
1197
+ return;
1198
+ }
1199
+ if (meta && typeof meta === "object" && "pushy_build_time" in meta) {
1200
+ const v = meta.pushy_build_time;
1201
+ if (typeof v === "string" || typeof v === "number") return String(v);
1202
+ }
1203
+ }
1204
+ /** 鸿蒙:先按顶层 resources/rawfile/meta.json 读(hap 或直排 hap 内容的 .app);
1205
+ * .app 整包的 hap 嵌套在 zip 里,取出后再读(对照官方 CLI getAppInfo 的嵌套提取)。 */
1206
+ function harmonyBuildTime(artifactPath) {
1207
+ const direct = metaJsonBuildTime(unzipStdout([
1208
+ "-p",
1209
+ artifactPath,
1210
+ "resources/rawfile/meta.json"
1211
+ ]));
1212
+ if (direct) return direct;
1213
+ const hapEntry = unzipStdout(["-Z1", artifactPath])?.split("\n").map((s) => s.trim()).find((s) => s.endsWith(".hap"));
1214
+ if (!hapEntry) return void 0;
1215
+ const tmp = path.join(os.tmpdir(), `applift-meta-hap-${process.pid}-${Date.now()}`);
1216
+ try {
1217
+ const ex = spawnSync("unzip", [
1218
+ "-p",
1219
+ artifactPath,
1220
+ hapEntry
1221
+ ], {
1222
+ timeout: 6e4,
1223
+ maxBuffer: 536870912
1224
+ });
1225
+ if (ex.error || ex.status !== 0 || !ex.stdout) return void 0;
1226
+ fs.writeFileSync(tmp, ex.stdout);
1227
+ return metaJsonBuildTime(unzipStdout([
1228
+ "-p",
1229
+ tmp,
1230
+ "resources/rawfile/meta.json"
1231
+ ]));
1232
+ } finally {
1233
+ fs.rmSync(tmp, { force: true });
1234
+ }
1235
+ }
1236
+ /** APK:AndroidManifest meta-data pushy_build_time(对照官方 CLI getApkInfo 的 meta 读取) */
1237
+ function apkBuildTime(artifactPath) {
1238
+ for (const tool of aaptTools()) {
1239
+ const args = path.basename(tool).startsWith("aapt2") ? [
1240
+ "dump",
1241
+ "xmltree",
1242
+ "--file",
1243
+ "AndroidManifest.xml",
1244
+ artifactPath
1245
+ ] : [
1246
+ "dump",
1247
+ "xmltree",
1248
+ artifactPath,
1249
+ "AndroidManifest.xml"
1250
+ ];
1251
+ const r = spawnSync(tool, args, {
1252
+ timeout: 1e4,
1253
+ encoding: "utf8"
1254
+ });
1255
+ if (r.error || r.status !== 0) continue;
1256
+ const m = /pushy_build_time"[\s\S]{0,400}?="(\d+)"/.exec(r.stdout ?? "");
1257
+ if (m?.[1]) return m[1];
1258
+ }
1259
+ }
1260
+ /** iOS:Payload/*.app/pushy_build_time.txt(官方 Xcode 构建 phase 产物) */
1261
+ function ipaBuildTime(artifactPath) {
1262
+ return unzipStdout([
1263
+ "-p",
1264
+ artifactPath,
1265
+ "Payload/*.app/pushy_build_time.txt"
1266
+ ])?.trim() || void 0;
1267
+ }
1170
1268
  /** 鸿蒙包版本读取:优先包内 pack.info(zip 容器内的权威数据),回退同目录 pack.info(标注可能过期) */
1171
1269
  function fromPackInfo(artifactPath) {
1172
1270
  const r = spawnSync("unzip", [
@@ -1202,8 +1300,8 @@ function parsePackInfo(text, source) {
1202
1300
  };
1203
1301
  } catch {}
1204
1302
  }
1205
- /** APK:aapt2/aapt dump badging(versionCode/versionName) */
1206
- function fromAapt(artifactPath) {
1303
+ /** aapt 工具探测(ANDROID_HOME/SDK build-tools 下新版优先,aapt2 先于 aapt) */
1304
+ function aaptTools() {
1207
1305
  const tools = [];
1208
1306
  const sdkDir = process.env.ANDROID_HOME ?? path.join(os.homedir(), "Library/Android/sdk");
1209
1307
  const btDir = path.join(sdkDir, "build-tools");
@@ -1211,6 +1309,11 @@ function fromAapt(artifactPath) {
1211
1309
  const p = path.join(btDir, bt, tool);
1212
1310
  if (fs.existsSync(p)) tools.push(p);
1213
1311
  }
1312
+ return tools;
1313
+ }
1314
+ /** APK:aapt2/aapt dump badging(versionCode/versionName) */
1315
+ function fromAapt(artifactPath) {
1316
+ const tools = aaptTools();
1214
1317
  for (const tool of tools) {
1215
1318
  const r = spawnSync(tool, [
1216
1319
  "dump",
@@ -1231,4 +1334,4 @@ function fromAapt(artifactPath) {
1231
1334
  }
1232
1335
  }
1233
1336
  //#endregion
1234
- export { resolveFramework as A, statusShort as B, suggestFramework as C, detectBundleIds as D, deps as E, git as F, isGitRepo as I, latestSemverTag as L, collectReleaseDiff as M, routeDelivery as N, findArtifacts as O, commitsSince as P, latestVersionAnchor as R, rnFramework as S, tauriFramework as T, tagReleaseVersion as V, nativeHarmonyAdapter as _, betaVersionName as a, nativeIosFramework as b, writeProjectVersions as c, electronFramework as d, flutterAdapter as f, nativeAndroidFramework as g, nativeAndroidAdapter as h, assertBetaVersionSupported as i, subdirs as j, readJson as k, redactText as l, knownFrameworkAdapters as m, DEFAULT_BETA_VERSION_CONFIG as n, checkVersionNameShape as o, flutterFramework as p, PLATFORM_VERSION_CAPS as r, readProjectVersions as s, readArtifactVersion as t, electronAdapter as u, nativeHarmonyFramework as v, tauriAdapter as w, rnAdapter as x, nativeIosAdapter as y, looseSemver as z };
1337
+ export { readJson as A, looseSemver as B, rnFramework as C, deps as D, tauriFramework as E, commitsSince as F, tagReleaseVersion as H, git as I, isGitRepo as L, subdirs as M, collectReleaseDiff as N, detectBundleIds as O, routeDelivery as P, latestSemverTag as R, rnAdapter as S, tauriAdapter as T, statusShort as V, nativeAndroidFramework as _, assertBetaVersionSupported as a, nativeIosAdapter as b, readProjectVersions as c, electronAdapter as d, electronFramework as f, nativeAndroidAdapter as g, knownFrameworkAdapters as h, PLATFORM_VERSION_CAPS as i, resolveFramework as j, findArtifacts as k, writeProjectVersions as l, flutterFramework as m, readArtifactVersion as n, betaVersionName as o, flutterAdapter as p, DEFAULT_BETA_VERSION_CONFIG as r, checkVersionNameShape as s, readArtifactBuildTime as t, redactText as u, nativeHarmonyAdapter as v, suggestFramework as w, nativeIosFramework as x, nativeHarmonyFramework as y, latestVersionAnchor as z };
package/dist/index.d.mts CHANGED
@@ -766,5 +766,11 @@ export interface ArtifactVersion {
766
766
  source: string;
767
767
  }
768
768
  export declare function readArtifactVersion(artifactPath: string): ArtifactVersion | undefined;
769
+ /** 产物 pushy 构建时间戳读取(buildTime 与设备上报必须精确一致,读不出返回 undefined,
770
+ * 由调用方决定拒绝上传而非猜测)。
771
+ * .hap/.app:包内(.app 可能嵌套 hap)resources/rawfile/meta.json 的 pushy_build_time;
772
+ * .apk:AndroidManifest meta-data pushy_build_time(aapt2/aapt xmltree);
773
+ * .ipa:Payload/*.app/pushy_build_time.txt(官方 Xcode 构建 phase 产物)。 */
774
+ export declare function readArtifactBuildTime(artifactPath: string): string | undefined;
769
775
  //#endregion
770
776
  export { AppliftPlugin, AppliftUserConfig, BetaConfig, BetaConfigSchema, BetaVersionConfig, BetaVersionConfigSchema, BundleIds, Channel, ChannelCapabilities, ChannelContext, ChannelEvent, ChannelRegistry, CredentialsFile, CredentialsFileSchema, CredentialsProfile, CredentialsProfileSchema, DeliveryJudgement, DeliveryRoute, FrameworkAdapter, FrameworkContext, GlobalConfig, GlobalConfigSchema, HISTORY_PATH, HistoryEntry, HistoryEvent, HistoryQuery, HookContext, PipelineInput, PipelineSummary, PipelineSummaryEntry, Platform, PluginHookName, PluginHost, ProjectConfig, ProjectConfigSchema, PublishContext, ReleasePlanEntry, ReviewStatus, StageResult, StoreConfig, StoreConfigSchema, Stores, StoresSchema, SubmitResult, UpdateProvider, VersionCodeConfig, VersionCodeConfigSchema, VersionConfig, VersionConfigSchema, appendEntry, collectReleaseDiff, defineConfig, deps, detectBundleIds, electronAdapter, electronFramework, findArtifacts, flutterAdapter, flutterFramework, knownFrameworkAdapters, nativeAndroidAdapter, nativeAndroidFramework, nativeHarmonyAdapter, nativeHarmonyFramework, nativeIosAdapter, nativeIosFramework, readEntries, readJson, resolveFramework, resolvePlugins, rnAdapter, rnFramework, routeDelivery, runStage, runSubmit, subdirs, suggestFramework, tauriAdapter, tauriFramework };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as resolveFramework, B as statusShort, C as suggestFramework, D as detectBundleIds, E as deps, F as git, I as isGitRepo, L as latestSemverTag, M as collectReleaseDiff, N as routeDelivery, O as findArtifacts, P as commitsSince, R as latestVersionAnchor, S as rnFramework, T as tauriFramework, V as tagReleaseVersion, _ as nativeHarmonyAdapter, a as betaVersionName, b as nativeIosFramework, c as writeProjectVersions, d as electronFramework, f as flutterAdapter, g as nativeAndroidFramework, h as nativeAndroidAdapter, i as assertBetaVersionSupported, j as subdirs, k as readJson, l as redactText, m as knownFrameworkAdapters, n as DEFAULT_BETA_VERSION_CONFIG, o as checkVersionNameShape, p as flutterFramework, r as PLATFORM_VERSION_CAPS, s as readProjectVersions, t as readArtifactVersion, u as electronAdapter, v as nativeHarmonyFramework, w as tauriAdapter, x as rnAdapter, y as nativeIosAdapter, z as looseSemver } from "./artifact-DbTqPUbk.mjs";
1
+ import { A as readJson, B as looseSemver, C as rnFramework, D as deps, E as tauriFramework, F as commitsSince, H as tagReleaseVersion, I as git, L as isGitRepo, M as subdirs, N as collectReleaseDiff, O as detectBundleIds, P as routeDelivery, R as latestSemverTag, S as rnAdapter, T as tauriAdapter, V as statusShort, _ as nativeAndroidFramework, a as assertBetaVersionSupported, b as nativeIosAdapter, c as readProjectVersions, d as electronAdapter, f as electronFramework, g as nativeAndroidAdapter, h as knownFrameworkAdapters, i as PLATFORM_VERSION_CAPS, j as resolveFramework, k as findArtifacts, l as writeProjectVersions, m as flutterFramework, n as readArtifactVersion, o as betaVersionName, p as flutterAdapter, r as DEFAULT_BETA_VERSION_CONFIG, s as checkVersionNameShape, t as readArtifactBuildTime, u as redactText, v as nativeHarmonyAdapter, w as suggestFramework, x as nativeIosFramework, y as nativeHarmonyFramework, z as latestVersionAnchor } from "./artifact-D3BSnmqY.mjs";
2
2
  import { z } from "zod";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
@@ -4777,4 +4777,4 @@ function applyBump(cwd, plan, framework, opts) {
4777
4777
  }
4778
4778
  }
4779
4779
  //#endregion
4780
- export { APPLIFT_HOME, AgcHarmonyChannel, AscChannel, BetaConfigSchema, BetaVersionConfigSchema, CREDENTIALS_PATH, CredentialsFileSchema, CredentialsProfileSchema, DEFAULT_BETA_VERSION_CONFIG, DefaultChannelRegistry, GLOBAL_CONFIG_PATH, GlobalConfigSchema, GooglePlayChannel, HISTORY_PATH, HonorChannel, HuaweiAndroidChannel, OppoChannel, PLATFORM_VERSION_CAPS, PROJECTS_MAP_PATH, PgyerChannel, PluginHost, ProjectConfigSchema, StoreConfigSchema, StoresSchema, VersionCodeConfigSchema, VersionConfigSchema, VivoChannel, WAIT_BEFORE_SUBMIT_MS, XiaomiChannel, YybChannel, appendEntry, applyBump, assertBetaVersionSupported, assertListingFiles, betaVersionName, checkVersionNameShape, collectReleaseDiff, commitsSince, compareDeclared, decideNextVersion, defineConfig, deps, detectBundleIds, electronAdapter, electronFramework, evalVersionCodeFormula, findArtifacts, findUp, flutterAdapter, flutterFramework, formPost, getAgcToken, getHarmonyAppId, git, hmacSign, humanizeError, inferBumpLevel, isGitRepo, knownFrameworkAdapters, latestSemverTag, latestVersionAnchor, loadCredentials, loadCredentialsLayered, loadGlobalConfig, loadListingFile, loadProjectConfig, loadProjectProfileMap, looseSemver, md5File, md5String, nativeAndroidAdapter, nativeAndroidFramework, nativeHarmonyAdapter, nativeHarmonyFramework, nativeIosAdapter, nativeIosFramework, nextVersionCode, planBump, projectCredentialsPath, readArtifactVersion, readEntries, readJson, readProjectVersions, redactText, registry, renderChangelogSection, resolveFramework, resolveListing, resolvePlugins, resolveProfile, resolveProfileFor, resolveProfileSource, rnAdapter, rnFramework, routeDelivery, runStage, runSubmit, saveCredentials, saveProjectProfileMap, setProjectProfile, sha256File, sortedQueryString, statusShort, storeVersionName, subdirs, suggestFramework, tagReleaseVersion, tauriAdapter, tauriFramework, toChangelogEntries, tsMillis, tsSeconds, upsertChangelog, writeProjectVersions };
4780
+ export { APPLIFT_HOME, AgcHarmonyChannel, AscChannel, BetaConfigSchema, BetaVersionConfigSchema, CREDENTIALS_PATH, CredentialsFileSchema, CredentialsProfileSchema, DEFAULT_BETA_VERSION_CONFIG, DefaultChannelRegistry, GLOBAL_CONFIG_PATH, GlobalConfigSchema, GooglePlayChannel, HISTORY_PATH, HonorChannel, HuaweiAndroidChannel, OppoChannel, PLATFORM_VERSION_CAPS, PROJECTS_MAP_PATH, PgyerChannel, PluginHost, ProjectConfigSchema, StoreConfigSchema, StoresSchema, VersionCodeConfigSchema, VersionConfigSchema, VivoChannel, WAIT_BEFORE_SUBMIT_MS, XiaomiChannel, YybChannel, appendEntry, applyBump, assertBetaVersionSupported, assertListingFiles, betaVersionName, checkVersionNameShape, collectReleaseDiff, commitsSince, compareDeclared, decideNextVersion, defineConfig, deps, detectBundleIds, electronAdapter, electronFramework, evalVersionCodeFormula, findArtifacts, findUp, flutterAdapter, flutterFramework, formPost, getAgcToken, getHarmonyAppId, git, hmacSign, humanizeError, inferBumpLevel, isGitRepo, knownFrameworkAdapters, latestSemverTag, latestVersionAnchor, loadCredentials, loadCredentialsLayered, loadGlobalConfig, loadListingFile, loadProjectConfig, loadProjectProfileMap, looseSemver, md5File, md5String, nativeAndroidAdapter, nativeAndroidFramework, nativeHarmonyAdapter, nativeHarmonyFramework, nativeIosAdapter, nativeIosFramework, nextVersionCode, planBump, projectCredentialsPath, readArtifactBuildTime, readArtifactVersion, readEntries, readJson, readProjectVersions, redactText, registry, renderChangelogSection, resolveFramework, resolveListing, resolvePlugins, resolveProfile, resolveProfileFor, resolveProfileSource, rnAdapter, rnFramework, routeDelivery, runStage, runSubmit, saveCredentials, saveProjectProfileMap, setProjectProfile, sha256File, sortedQueryString, statusShort, storeVersionName, subdirs, suggestFramework, tagReleaseVersion, tauriAdapter, tauriFramework, toChangelogEntries, tsMillis, tsSeconds, upsertChangelog, writeProjectVersions };
@@ -32,8 +32,9 @@ interface PushyCli {
32
32
  }): Promise<{
33
33
  versionId: number;
34
34
  }>;
35
- /** 上传原生基线(versionName 由调用方从产物读取后显式传入;deps 同 publish) */
36
- uploadApp(appId: number, appFile: string, versionName: string, deps: Record<string, string>): Promise<{
35
+ /** 上传原生基线(versionName 与 buildTime 均由调用方从产物内读取后显式传入;
36
+ * buildTime 必须与设备上报的编译时间戳精确一致,读不出时调用方应拒绝上传。deps 同 publish) */
37
+ uploadApp(appId: number, appFile: string, versionName: string, deps: Record<string, string>, buildTime: string): Promise<{
37
38
  packageId: number;
38
39
  version: string;
39
40
  }>;
@@ -1,4 +1,4 @@
1
- import { C as suggestFramework, M as collectReleaseDiff, O as findArtifacts, S as rnFramework, T as tauriFramework, _ as nativeHarmonyAdapter, a as betaVersionName, b as nativeIosFramework, d as electronFramework, f as flutterAdapter, g as nativeAndroidFramework, h as nativeAndroidAdapter, i as assertBetaVersionSupported, l as redactText, m as knownFrameworkAdapters, n as DEFAULT_BETA_VERSION_CONFIG, p as flutterFramework, s as readProjectVersions, t as readArtifactVersion, u as electronAdapter, v as nativeHarmonyFramework, w as tauriAdapter, x as rnAdapter, y as nativeIosAdapter } from "../artifact-DbTqPUbk.mjs";
1
+ import { C as rnFramework, E as tauriFramework, N as collectReleaseDiff, S as rnAdapter, T as tauriAdapter, _ as nativeAndroidFramework, a as assertBetaVersionSupported, b as nativeIosAdapter, c as readProjectVersions, d as electronAdapter, f as electronFramework, g as nativeAndroidAdapter, h as knownFrameworkAdapters, k as findArtifacts, m as flutterFramework, n as readArtifactVersion, o as betaVersionName, p as flutterAdapter, r as DEFAULT_BETA_VERSION_CONFIG, t as readArtifactBuildTime, u as redactText, v as nativeHarmonyAdapter, w as suggestFramework, x as nativeIosFramework, y as nativeHarmonyFramework } from "../artifact-D3BSnmqY.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { createHmac } from "node:crypto";
@@ -62,9 +62,11 @@ function dingtalkNotify(options) {
62
62
  * cresc/自建服务差异仅在 baseUrl(同一套服务端代码),serverUrl 由 rnUpdate 配置注入。
63
63
  * uploadApp 相对官方 CLI 的有意降级(记录在案):传原始产物而非 slim 包、不带 bundleHash/
64
64
  * bytecodeVersion/bundleLocation(服务端无法将该基线用作 hermes-base 或 package-origin diff)、
65
- * buildTime 为上传时刻而非包内提取、不校验包内 appId 与目标 app 一致(配错 appId 会传到别的 app——
66
- * 由配置审查保证)、不做 maxSize 预检(超限撞对象存储错误)。需要 hermes-base 能力时在此补齐。
67
- */
65
+ * 不校验包内 appId 与目标 app 一致(配错 appId 会传到别的 app——由配置审查保证)、
66
+ * 不做 maxSize 预检(超限撞对象存储错误)。需要 hermes-base 能力时在此补齐。
67
+ * buildTime 已对齐官方语义:由调用方从产物内提取真实构建时间戳传入(2026-10-26 qy-driver
68
+ * 实证:服务端按 buildTime 精确匹配设备上报,写入上传时刻会让该基线下的设备全部
69
+ * checkUpdate 吃「编译时间戳不匹配」+ 封锁 6h,热更永远发不下去)。 */
68
70
  /** credentials.json 中的 pushy token 键名 */
69
71
  const PUSHY_TOKEN_KEY = "PUSHY_API_TOKEN";
70
72
  /** pushy 官方 API 根(官方 CLI 默认端点簇的主端点) */
@@ -245,14 +247,14 @@ function createPushyApi(opts) {
245
247
  await bind(q, input.appId, id, input.packageVersion, input.rollout);
246
248
  return { versionId: id };
247
249
  },
248
- async uploadApp(appId, appFile, versionName, deps) {
250
+ async uploadApp(appId, appFile, versionName, deps, buildTime) {
249
251
  const hash = await uploadFile(f, baseUrl, opts.token, appId, appFile, opts.httpUpload);
250
252
  const { id } = await q(`/app/${appId}/package/create`, {
251
253
  method: "POST",
252
254
  body: JSON.stringify({
253
255
  name: versionName,
254
256
  hash,
255
- buildTime: String(Date.now()),
257
+ buildTime,
256
258
  deps
257
259
  })
258
260
  });
@@ -743,13 +745,15 @@ function rnUpdate(opts = {}, cli) {
743
745
  try {
744
746
  const versionName = readArtifactVersion(result.artifactPath)?.versionName;
745
747
  if (!versionName) throw new Error(`未能从产物读出 versionName:${result.artifactPath}(readArtifactVersion 不支持该格式?)`);
748
+ const buildTime = readArtifactBuildTime(result.artifactPath);
749
+ if (!buildTime) throw new Error(`未能从产物读出 pushy 构建时间戳:${result.artifactPath}(包内缺 meta.json / pushy_build_time?请人工用 pushy CLI 补传基线)`);
746
750
  const appId = platformAppId(opts, platform, result.target ?? baselineChannel(versionName, platform, hookCtx.cwd));
747
751
  const key = `${platform}:${versionName}`;
748
752
  if (uploadedBaselines.has(key)) return;
749
753
  uploadedBaselines.add(key);
750
754
  const pushy = resolvePushyClient(opts, cli, hookCtx.credentials);
751
755
  try {
752
- await pushy.uploadApp(appId, result.artifactPath, versionName, readDepVersions(hookCtx.cwd));
756
+ await pushy.uploadApp(appId, result.artifactPath, versionName, readDepVersions(hookCtx.cwd), buildTime);
753
757
  } catch (e) {
754
758
  if (e.status !== 409) throw e;
755
759
  const sameName = (await pushy.packages(appId)).filter((p) => p.name === versionName);
@@ -760,7 +764,7 @@ function rnUpdate(opts = {}, cli) {
760
764
  if (vers.length > 0 && vers.every((v) => v.packageVersion === void 0)) throw new Error(`服务端未回显热更绑定关系,无法确认 ${versionName} 基线可安全替换——请人工核对后删除重传`);
761
765
  for (const p of sameName) await pushy.deletePackage(appId, p.id);
762
766
  process.stderr.write(`⚠ pushy 基线 ${versionName} 内容已变(同版本名重建),自动删除旧基线 ${sameName.map((p) => `#${p.id}(${p.hash})`).join(",")} 并重传\n`);
763
- await pushy.uploadApp(appId, result.artifactPath, versionName, readDepVersions(hookCtx.cwd));
767
+ await pushy.uploadApp(appId, result.artifactPath, versionName, readDepVersions(hookCtx.cwd), buildTime);
764
768
  }
765
769
  } catch (e) {
766
770
  uploadedBaselines.delete(`${platform}:${readArtifactVersion(result.artifactPath)?.versionName ?? ""}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applift/core",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/hcg1023/applift.git",