Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
161 lines
4.8 KiB
JavaScript
161 lines
4.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import {
|
|
mkdir,
|
|
mkdtemp,
|
|
readFile,
|
|
readdir,
|
|
rm,
|
|
writeFile
|
|
} from "node:fs/promises";
|
|
import { createRequire } from "node:module";
|
|
import test from "node:test";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import YAML from "yaml";
|
|
|
|
import {
|
|
mergeMacosReleaseArtifacts,
|
|
resolveUpdaterFileName
|
|
} from "../../apps/desktop/scripts/merge-macos-release-artifacts.mjs";
|
|
|
|
const desktopRequire = createRequire(
|
|
new URL("../../apps/desktop/package.json", import.meta.url)
|
|
);
|
|
const { findFile } = desktopRequire("electron-updater/out/providers/Provider");
|
|
|
|
async function createArchitectureArtifacts(root, version, architecture) {
|
|
const artifactDir = path.join(
|
|
root,
|
|
`tutti-desktop-release-assets-macos-${architecture}`
|
|
);
|
|
await mkdir(artifactDir, { recursive: true });
|
|
const stem = `Tutti-${version}-mac-${architecture}`;
|
|
await Promise.all([
|
|
writeFile(path.join(artifactDir, `${stem}.dmg`), `dmg-${architecture}`),
|
|
writeFile(path.join(artifactDir, `${stem}.zip`), `zip-${architecture}`),
|
|
writeFile(
|
|
path.join(artifactDir, `${stem}.zip.blockmap`),
|
|
`blockmap-${architecture}`
|
|
)
|
|
]);
|
|
}
|
|
|
|
function withProcessArchitecture(architecture, callback) {
|
|
const descriptor = Object.getOwnPropertyDescriptor(process, "arch");
|
|
Object.defineProperty(process, "arch", {
|
|
configurable: true,
|
|
enumerable: true,
|
|
value: architecture
|
|
});
|
|
try {
|
|
return callback();
|
|
} finally {
|
|
Object.defineProperty(process, "arch", descriptor);
|
|
}
|
|
}
|
|
|
|
function selectMacUpdaterZip(files, architecture) {
|
|
return withProcessArchitecture(architecture, () => {
|
|
let resolvedFiles = files.map((info) => ({
|
|
info,
|
|
url: new URL(info.url, "https://updates.example.test/")
|
|
}));
|
|
const isArm64 = (file) =>
|
|
file.url.pathname.includes("arm64") || file.info.url.includes("arm64");
|
|
if (architecture === "arm64" && resolvedFiles.some(isArm64)) {
|
|
resolvedFiles = resolvedFiles.filter(isArm64);
|
|
} else {
|
|
resolvedFiles = resolvedFiles.filter((file) => !isArm64(file));
|
|
}
|
|
return findFile(resolvedFiles, "zip", ["pkg", "dmg"]);
|
|
});
|
|
}
|
|
|
|
test("macOS matrix artifacts merge into one architecture-aware updater manifest", async () => {
|
|
const tempRoot = await mkdtemp(
|
|
path.join(os.tmpdir(), "tutti-macos-release-merge-")
|
|
);
|
|
const inputDirectory = path.join(tempRoot, "input");
|
|
const outputDirectory = path.join(tempRoot, "output");
|
|
const version = "1.2.3-rc.4";
|
|
|
|
try {
|
|
await Promise.all(
|
|
["x64", "arm64", "universal"].map((architecture) =>
|
|
createArchitectureArtifacts(inputDirectory, version, architecture)
|
|
)
|
|
);
|
|
|
|
const result = await mergeMacosReleaseArtifacts({
|
|
inputDirectory,
|
|
outputDirectory,
|
|
releaseChannel: "rc",
|
|
releaseDate: "2026-07-16T00:00:00.000Z",
|
|
releaseVersion: version
|
|
});
|
|
const metadata = YAML.parse(
|
|
await readFile(path.join(outputDirectory, "rc-mac.yml"), "utf8")
|
|
);
|
|
const outputNames = await readdir(outputDirectory);
|
|
|
|
assert.equal(result.updaterFileName, "rc-mac.yml");
|
|
assert.equal(outputNames.length, 10);
|
|
assert.deepEqual(
|
|
metadata.files.map((file) => file.url),
|
|
[
|
|
`Tutti-${version}-mac-x64.zip`,
|
|
`Tutti-${version}-mac-arm64.zip`,
|
|
`Tutti-${version}-mac-universal.zip`
|
|
]
|
|
);
|
|
assert.equal(metadata.path, `Tutti-${version}-mac-x64.zip`);
|
|
assert.equal(metadata.sha512, metadata.files[0].sha512);
|
|
assert.equal(metadata.releaseDate, "2026-07-16T00:00:00.000Z");
|
|
for (const file of metadata.files) {
|
|
assert.equal(typeof file.sha512, "string");
|
|
assert.ok(file.sha512.length > 40);
|
|
assert.equal(typeof file.size, "number");
|
|
}
|
|
|
|
assert.match(
|
|
selectMacUpdaterZip(metadata.files, "x64").url.pathname,
|
|
/-mac-x64\.zip$/
|
|
);
|
|
assert.match(
|
|
selectMacUpdaterZip(metadata.files, "arm64").url.pathname,
|
|
/-mac-arm64\.zip$/
|
|
);
|
|
} finally {
|
|
await rm(tempRoot, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
test("electron-updater accepts universal ZIP metadata on both macOS architectures", () => {
|
|
const universalFiles = [
|
|
{
|
|
sha512: "universal-sha512",
|
|
size: 123,
|
|
url: "Tutti-1.2.3-mac-universal.zip"
|
|
}
|
|
];
|
|
|
|
assert.match(
|
|
selectMacUpdaterZip(universalFiles, "x64").url.pathname,
|
|
/-mac-universal\.zip$/
|
|
);
|
|
assert.match(
|
|
selectMacUpdaterZip(universalFiles, "arm64").url.pathname,
|
|
/-mac-universal\.zip$/
|
|
);
|
|
});
|
|
|
|
test("macOS updater channel names stay compatible with electron-updater", () => {
|
|
assert.equal(resolveUpdaterFileName("stable"), "latest-mac.yml");
|
|
assert.equal(resolveUpdaterFileName("rc"), "rc-mac.yml");
|
|
assert.equal(resolveUpdaterFileName("beta"), "beta-mac.yml");
|
|
assert.throws(
|
|
() => resolveUpdaterFileName("nightly"),
|
|
/Unsupported desktop release channel/
|
|
);
|
|
});
|