Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions sfmc/pack-update-policy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ describe("version-policy", () => {
assert.equal(d.shouldBumpRp, false);
});

it("同版本未更高时默认不 bump;treatAsUpdate 强制同步仍可 bump RP", () => {
const policy = {
authority: "behavior_pack",
onUpdateOverwriteBoth: true,
rpBumpWhenSameMajor: true,
rpBumpComponent: "patch",
majorHigherSkipRpBump: true,
};
const plain = decideVersionPolicy([1, 0, 0], [1, 0, 0], policy);
assert.equal(plain.remoteNewer, false);
assert.equal(plain.shouldBumpRp, false);

const forced = decideVersionPolicy([1, 0, 0], [1, 0, 0], policy, { treatAsUpdate: true });
assert.equal(forced.remoteNewer, false);
assert.equal(forced.shouldBumpRp, true);
});

it("远程更旧但 treatAsUpdate 时仍按同 major 规则 bump RP", () => {
const d = decideVersionPolicy([1, 2, 0], [1, 1, 0], {
authority: "behavior_pack",
onUpdateOverwriteBoth: true,
rpBumpWhenSameMajor: true,
rpBumpComponent: "patch",
majorHigherSkipRpBump: true,
}, { treatAsUpdate: true });
assert.equal(d.remoteNewer, false);
assert.equal(d.majorHigher, false);
assert.equal(d.shouldBumpRp, true);
});

it("nextVersionGreaterThan", () => {
assert.deepEqual(nextVersionGreaterThan([1, 0, 5], [1, 0, 8], "patch"), [1, 0, 9]);
});
Expand Down
72 changes: 29 additions & 43 deletions sfmc/src/pack-update/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ function touchBinding(bpUuid: string, binding: PackSourceBinding, patch?: Partia
setBinding(bpUuid, binding);
}

/** 该 fileId 是否已成功写入世界目录(DRY:prepareCheck 多处勿手写 null/!==) */
function isFileIdApplied(binding: PackSourceBinding, fileId: number): boolean {
return binding.lastAppliedFileId != null && binding.lastAppliedFileId === fileId;
}

/** 多查询搜索 + 综合打分排序(probe / searchRemote 共用) */
async function searchAndRankHits(
provider: PackSourceProvider,
Expand Down Expand Up @@ -411,8 +416,8 @@ async function prepareCheck(
);
}

/* 已成功应用(或确认无需覆盖)同一 fileId:不再下载 */
if (binding.lastAppliedFileId != null && binding.lastAppliedFileId === file.fileId) {
/* 已成功应用同一 fileId:不再下载 */
if (isFileIdApplied(binding, file.fileId)) {
touchBinding(bpUuid, binding, { lastFileId: file.fileId });
return withLocalBp(
baseCheckFields(bpUuid, name, localVer, binding, {
Expand All @@ -424,15 +429,12 @@ async function prepareCheck(
}

if (!downloadArchive) {
/* 仅文件 id 比较的轻量检查 */
const updateAvailable = binding.lastAppliedFileId == null || binding.lastAppliedFileId !== file.fileId;
/* 仅文件 id 比较的轻量检查(未 apply 即视为有更新) */
return withLocalBp(
baseCheckFields(bpUuid, name, localVer, binding, {
updateAvailable,
updateAvailable: true,
fileId: file.fileId,
message: updateAvailable
? t("packUpdate.fileNewer", { file: file.fileName, id: String(file.fileId) })
: t("packUpdate.upToDate"),
message: t("packUpdate.fileNewer", { file: file.fileName, id: String(file.fileId) }),
}),
localBp
);
Expand Down Expand Up @@ -461,54 +463,38 @@ async function prepareCheck(
);
}

const decision = decideVersionPolicy(localVer, remoteBpInfo.version, cfg.versionPolicy);

/*
* 是否需要写入世界目录:
* - 远程 BP 版本更高 → 要覆盖
* - 或同一 fileId 从未成功 apply 过 → 也要覆盖
* (汉化包等可能与 CF 同版本号但内容不同;旧逻辑在「版本未更高」时直接记 lastApplied 会跳过安装)
* 注意:绝不能在未 install 成功时写入 lastAppliedFileId。
* 能走到此处说明该 fileId 尚未成功 apply(上方已 early-return)。
* - 远程 BP 版本更高 → 覆盖
* - 版本未更高 → 仍覆盖(汉化包等同版本号不同内容;勿在未 install 时写 lastAppliedFileId)
* RP bump 规则集中在 decideVersionPolicy(OCP/DRY:勿在编排层复刻 policy 字段)。
*/
const fileNotApplied =
binding.lastAppliedFileId == null || binding.lastAppliedFileId !== file.fileId;
const updateAvailable = decision.remoteNewer || fileNotApplied;
const shouldBumpRp = decision.remoteNewer
? decision.shouldBumpRp
: Boolean(
fileNotApplied &&
cfg.versionPolicy.onUpdateOverwriteBoth &&
cfg.versionPolicy.rpBumpWhenSameMajor &&
!decision.majorHigher
);
const decision = decideVersionPolicy(localVer, remoteBpInfo.version, cfg.versionPolicy, {
treatAsUpdate: true,
});

touchBinding(bpUuid, binding, { lastFileId: file.fileId });

let message: string;
if (decision.remoteNewer) {
message = t("packUpdate.bpNewer", {
local: fmtVer(localVer),
remote: fmtVer(remoteBpInfo.version),
});
} else if (fileNotApplied) {
message = t("packUpdate.needSync", {
local: fmtVer(localVer),
remote: fmtVer(remoteBpInfo.version),
id: String(file.fileId),
});
} else {
message = t("packUpdate.upToDate");
}
const message = decision.remoteNewer
? t("packUpdate.bpNewer", {
local: fmtVer(localVer),
remote: fmtVer(remoteBpInfo.version),
})
: t("packUpdate.needSync", {
local: fmtVer(localVer),
remote: fmtVer(remoteBpInfo.version),
id: String(file.fileId),
});

return withLocalBp(
{
bpUuid,
name,
localVer,
remoteVer: remoteBpInfo.version,
updateAvailable,
updateAvailable: true,
majorHigher: decision.majorHigher,
shouldBumpRp,
shouldBumpRp: decision.shouldBumpRp,
fileId: file.fileId,
message,
binding,
Expand Down
11 changes: 8 additions & 3 deletions sfmc/src/pack-update/version-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@ export function isNewer(remote: SemVer3, local: SemVer3): boolean {
return compareSemVer3(remote, local) > 0;
}

/** 根据本地/远程 BP 版本与策略,决定是否更新以及是否额外 bump RP */
/**
* 根据本地/远程 BP 版本与策略,决定是否更新以及是否额外 bump RP。
* `treatAsUpdate`:同版本/未更高但需强制同步写入时(如 fileId 尚未 apply)也走更新侧 RP bump 规则(OCP:策略集中于此,勿在编排层复刻)。
*/
export function decideVersionPolicy(
localBp: SemVer3,
remoteBp: SemVer3,
policy: VersionPolicyConfig
policy: VersionPolicyConfig,
opts?: { treatAsUpdate?: boolean }
): VersionCompareResult {
const remoteNewer = isNewer(remoteBp, localBp);
const majorHigher = remoteBp[0] > localBp[0];
const asUpdate = remoteNewer || Boolean(opts?.treatAsUpdate);
let shouldBumpRp = false;
if (remoteNewer && policy.onUpdateOverwriteBoth) {
if (asUpdate && policy.onUpdateOverwriteBoth) {
if (majorHigher && policy.majorHigherSkipRpBump) {
shouldBumpRp = false;
} else if (policy.rpBumpWhenSameMajor) {
Expand Down