diff --git a/server-tools/lake-lamp-authz/architecture-provision-broker.js b/server-tools/lake-lamp-authz/architecture-provision-broker.js index 660d93a..fae77f0 100644 --- a/server-tools/lake-lamp-authz/architecture-provision-broker.js +++ b/server-tools/lake-lamp-authz/architecture-provision-broker.js @@ -259,8 +259,14 @@ function backupFile(source, backup, records) { const stat = fs.lstatSync(source); if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("backup_source_not_regular_file"); fs.mkdirSync(path.dirname(backup), { recursive: true, mode: 0o700 }); - fs.copyFileSync(source, backup, fs.constants.COPYFILE_EXCL); - fs.chmodSync(backup, stat.mode & 0o777); + if (fs.existsSync(backup)) { + const backupStat = fs.lstatSync(backup); + if (!backupStat.isFile() || backupStat.isSymbolicLink()) throw new Error("backup_conflict"); + if (!fs.readFileSync(source).equals(fs.readFileSync(backup))) throw new Error("backup_conflict"); + } else { + fs.copyFileSync(source, backup, fs.constants.COPYFILE_EXCL); + fs.chmodSync(backup, stat.mode & 0o777); + } records.push({ source, backup, existed: true, mode: stat.mode & 0o777 }); } diff --git a/server-tools/lake-lamp-authz/architecture-provision-broker.test.js b/server-tools/lake-lamp-authz/architecture-provision-broker.test.js index 0ecd918..8feb29d 100644 --- a/server-tools/lake-lamp-authz/architecture-provision-broker.test.js +++ b/server-tools/lake-lamp-authz/architecture-provision-broker.test.js @@ -203,5 +203,15 @@ test("existing service update restores every changed file when acceptance fails" assert.equal(fs.readFileSync(path.join(installRoot, "server.js"), "utf8"), "old server\n"); assert.equal(fs.existsSync(path.join(installRoot, "alias.json")), false); assert.equal(fs.readFileSync(path.join(unitDir, "guanghu-ai-discovery.service"), "utf8"), "old unit\n"); + const retry = await provision({ target: "JD-FD-PRIMARY", action: "provision-approved-architecture", resource: `${updateRequestId}@${commit}` }, { + repoDir, releasesDir, unitDir, receiptsDir, installRootOverride: installRoot, + run: async (file, args) => ({ stdout: args.includes("rev-parse") ? `${commit}\n` : "" }), + getJson: async () => ({ ok: true }), + healthAttempts: 1, + healthDelayMs: 0, + }); + assert.equal(retry.ok, true); + assert.equal(fs.readFileSync(path.join(installRoot, "server.js"), "utf8"), "new server\n"); + assert.equal(fs.existsSync(path.join(installRoot, "alias.json")), true); } finally { fs.rmSync(root, { recursive: true, force: true }); } });