Advance M7 workflows and release operations
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-15 17:43:53 -04:00
parent 17ab961485
commit 7c16b279ae
103 changed files with 8064 additions and 429 deletions

View File

@@ -0,0 +1,136 @@
import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const archive = path.join(repoRoot, "release/blender-web-offline.tar.gz");
const reportRoot = path.join(repoRoot, "release/operations-reports");
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "blender-rollback-runbook-"));
const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex");
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function writeMetadata(release, metadata) {
fs.writeFileSync(path.join(release, "release-metadata.json"), `${JSON.stringify(metadata, null, 2)}\n`);
}
function switchCurrent(installRoot, releaseName) {
const pending = path.join(installRoot, ".current.next");
fs.rmSync(pending, { force: true });
fs.symlinkSync(path.join("releases", releaseName), pending);
fs.renameSync(pending, path.join(installRoot, "current"));
}
function rollbackCompatibility(openedStorage, target) {
if (target.storage.indexedDbSchemaVersion < openedStorage.indexedDbSchemaVersion) {
return { status: "BLOCKED", reason: "INDEXEDDB_SCHEMA_DOWNGRADE_UNSUPPORTED" };
}
if (target.storage.opfsProjectManifestSchemaVersion !== openedStorage.opfsProjectManifestSchemaVersion) {
return { status: "BLOCKED", reason: "OPFS_REVERSE_MIGRATION_UNDECLARED" };
}
return { status: "READY", reason: null };
}
try {
const installRoot = path.join(workspace, "install");
const releases = path.join(installRoot, "releases");
const originStorage = path.join(workspace, "origin-storage", "projects", "project-a");
fs.mkdirSync(releases, { recursive: true });
fs.mkdirSync(originStorage, { recursive: true });
const extracted = path.join(workspace, "extracted");
fs.mkdirSync(extracted);
execFileSync("tar", ["--no-same-owner", "--no-same-permissions", "-xzf", archive, "-C", extracted]);
const currentRelease = path.join(releases, "current-release");
fs.renameSync(path.join(extracted, "blender-web-offline"), currentRelease);
const currentMetadata = readJson(path.join(currentRelease, "release-metadata.json"));
const compatibleOld = path.join(releases, "compatible-old-release");
fs.cpSync(currentRelease, compatibleOld, { recursive: true });
const compatibleMetadata = structuredClone(currentMetadata);
compatibleMetadata.productVersion = "0.0.9-compatible";
compatibleMetadata.engineReleaseId = `${currentMetadata.engineReleaseId}-compatible-old`;
writeMetadata(compatibleOld, compatibleMetadata);
const incompatibleOld = path.join(releases, "incompatible-old-release");
fs.cpSync(currentRelease, incompatibleOld, { recursive: true });
const incompatibleMetadata = structuredClone(currentMetadata);
incompatibleMetadata.productVersion = "0.0.8-incompatible";
incompatibleMetadata.engineReleaseId = `${currentMetadata.engineReleaseId}-incompatible-old`;
incompatibleMetadata.storage.indexedDbSchemaVersion = Math.max(1, currentMetadata.storage.indexedDbSchemaVersion - 1);
writeMetadata(incompatibleOld, incompatibleMetadata);
switchCurrent(installRoot, path.basename(currentRelease));
const blend = Buffer.from("M6-17C persistent project content");
const project = {
schemaVersion: currentMetadata.storage.opfsProjectManifestSchemaVersion,
revision: 17,
bytes: blend.byteLength,
sha256: sha256(blend),
};
fs.writeFileSync(path.join(originStorage, "scene.blend"), blend);
fs.writeFileSync(path.join(originStorage, "scene.blend.meta.json"), `${JSON.stringify(project)}\n`);
const projectBytesBefore = fs.readFileSync(path.join(originStorage, "scene.blend"));
const manifestBefore = fs.readFileSync(path.join(originStorage, "scene.blend.meta.json"));
const currentBefore = fs.realpathSync(path.join(installRoot, "current"));
const openedStorage = {
indexedDbSchemaVersion: currentMetadata.storage.indexedDbSchemaVersion,
opfsProjectManifestSchemaVersion: currentMetadata.storage.opfsProjectManifestSchemaVersion,
};
const blocked = rollbackCompatibility(openedStorage, incompatibleMetadata);
assert.deepEqual(blocked, { status: "BLOCKED", reason: "INDEXEDDB_SCHEMA_DOWNGRADE_UNSUPPORTED" });
assert.equal(fs.realpathSync(path.join(installRoot, "current")), currentBefore, "blocked rollback changed current");
assert.deepEqual(fs.readFileSync(path.join(originStorage, "scene.blend")), projectBytesBefore);
assert.deepEqual(fs.readFileSync(path.join(originStorage, "scene.blend.meta.json")), manifestBefore);
const compatible = rollbackCompatibility(openedStorage, compatibleMetadata);
assert.deepEqual(compatible, { status: "READY", reason: null });
switchCurrent(installRoot, path.basename(compatibleOld));
assert.equal(fs.realpathSync(path.join(installRoot, "current")), compatibleOld);
assert.equal(readJson(path.join(installRoot, "current", "release-metadata.json")).engineReleaseId, compatibleMetadata.engineReleaseId);
assert.deepEqual(fs.readFileSync(path.join(originStorage, "scene.blend")), projectBytesBefore);
assert.deepEqual(fs.readFileSync(path.join(originStorage, "scene.blend.meta.json")), manifestBefore);
const reopened = readJson(path.join(originStorage, "scene.blend.meta.json"));
assert.equal(reopened.revision, project.revision);
assert.equal(reopened.sha256, sha256(fs.readFileSync(path.join(originStorage, "scene.blend"))));
assert.ok(fs.statSync(currentRelease).isDirectory(), "newer release was removed during rollback");
const opfsBlockedTarget = structuredClone(compatibleMetadata);
opfsBlockedTarget.storage.opfsProjectManifestSchemaVersion += 1;
assert.deepEqual(rollbackCompatibility(openedStorage, opfsBlockedTarget), {
status: "BLOCKED",
reason: "OPFS_REVERSE_MIGRATION_UNDECLARED",
});
const report = {
schemaVersion: 1,
task: "M6-17C",
status: "READY",
activeBefore: currentMetadata.engineReleaseId,
activeAfter: compatibleMetadata.engineReleaseId,
compatibleRollback: compatible,
blockedOldReader: blocked,
blockedOpfsMismatch: "OPFS_REVERSE_MIGRATION_UNDECLARED",
project: {
revisionBefore: project.revision,
revisionAfter: reopened.revision,
sha256Before: project.sha256,
sha256After: reopened.sha256,
preserved: true,
},
releases: { newerRetained: true, targetSwitchedAtomically: true },
};
fs.mkdirSync(reportRoot, { recursive: true });
fs.writeFileSync(path.join(reportRoot, "rollback.json"), `${JSON.stringify(report, null, 2)}\n`);
process.stdout.write(`rollback-runbook-ok compatible=ready idb-downgrade=blocked opfs-mismatch=blocked revision=${project.revision} project=${project.sha256}\n`);
}
finally {
fs.rmSync(workspace, { recursive: true, force: true });
}