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
574 lines
16 KiB
JavaScript
574 lines
16 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
buildTypeScriptTestShards,
|
|
classifyChangedFiles,
|
|
createPackageManifestPackRelevance,
|
|
createRootManifestTestRelevance
|
|
} from "./change-classification.mjs";
|
|
import { createIsolatedGitEnvironment } from "./git-environment.mjs";
|
|
import { selectRepositoryChecks } from "./repository-checks.mjs";
|
|
|
|
const releasePackages = [
|
|
{ name: "@tutti-os/agent-gui", root: "packages/agent/gui" },
|
|
{ name: "@tutti-os/ui-system", root: "packages/ui/system" }
|
|
];
|
|
const workspacePackages = [
|
|
{
|
|
hasTests: true,
|
|
name: "@tutti-os/agent-gui",
|
|
root: "packages/agent/gui"
|
|
},
|
|
{
|
|
hasTests: true,
|
|
name: "@tutti-os/desktop",
|
|
root: "apps/desktop",
|
|
workspaceDependencies: ["@tutti-os/agent-gui"]
|
|
},
|
|
{
|
|
hasTests: false,
|
|
name: "@tutti-os/no-tests",
|
|
root: "packages/example/no-tests"
|
|
}
|
|
];
|
|
|
|
test("builds no more TypeScript test shards than selected packages", () => {
|
|
assert.deepEqual(buildTypeScriptTestShards([]), ["1/1"]);
|
|
assert.deepEqual(buildTypeScriptTestShards(["one"]), ["1/1"]);
|
|
assert.deepEqual(buildTypeScriptTestShards(["one", "two"]), ["1/2", "2/2"]);
|
|
assert.deepEqual(buildTypeScriptTestShards(["one", "two", "three", "four"]), [
|
|
"1/3",
|
|
"2/3",
|
|
"3/3"
|
|
]);
|
|
});
|
|
|
|
test("Go-only changes do not select TypeScript validation", () => {
|
|
const classification = classifyChangedFiles(
|
|
["services/tuttid/service/workspace/apps.go"],
|
|
{ releasePackages }
|
|
);
|
|
|
|
assert.equal(classification.runGo, true);
|
|
assert.equal(classification.runTs, false);
|
|
assert.equal(classification.runPack, false);
|
|
assert.equal(classification.runBoundaries, true);
|
|
});
|
|
|
|
test("generated source files select generated contracts before outputs change", () => {
|
|
for (const file of [
|
|
"config/tutti.defaults.json",
|
|
"services/tuttid/api/openapi/tuttid.v1.yaml",
|
|
"packages/events/protocol/definitions/agent/activity.updated.event.json",
|
|
"packages/workbench/snapshot/src/schema.json"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], {
|
|
releasePackages
|
|
});
|
|
assert.equal(classification.runGenerated, true, file);
|
|
}
|
|
});
|
|
|
|
test("workflow and hook changes select repository tool contracts", () => {
|
|
for (const file of [
|
|
".github/workflows/desktop-release.yml",
|
|
".github/workflows/publish-tutti-app-release.yml",
|
|
".husky/pre-push"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], {
|
|
releasePackages
|
|
});
|
|
assert.equal(classification.runContracts, true, file);
|
|
}
|
|
});
|
|
|
|
test("Go CI and shared selector changes use tool contracts instead of all Go lanes", () => {
|
|
for (const file of [
|
|
".github/workflows/pr-checks.yml",
|
|
"tools/scripts/run-changed-go-validation.mjs",
|
|
"tools/scripts/run-check-changed-targets.mjs"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], {
|
|
releasePackages
|
|
});
|
|
assert.equal(classification.runGo, false, file);
|
|
assert.equal(classification.runContracts, true, file);
|
|
}
|
|
});
|
|
|
|
test("Agent Session Replay changes select the current-build closed loop", () => {
|
|
for (const file of [
|
|
"packages/agent/session-replay/cassette.go",
|
|
"packages/agent/daemon/runtime/process_transport_session_replay.go",
|
|
"services/tuttid/agent_session_recording.go",
|
|
"services/tuttid/data/workspace/migrations_agent_session_replay.go",
|
|
"apps/desktop/src/main/agentSessionReplayProcessManager.ts",
|
|
"apps/desktop/src/renderer/src/features/agent-session-replay/services/agentSessionReplayService.ts",
|
|
"tools/scripts/run-agent-session-replay.mjs"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], {
|
|
releasePackages
|
|
});
|
|
assert.equal(classification.runAgentSessionReplay, true, file);
|
|
}
|
|
});
|
|
|
|
test("unrelated UI changes do not select the Agent Session Replay closed loop", () => {
|
|
const classification = classifyChangedFiles(
|
|
["apps/desktop/src/renderer/src/features/settings/Settings.tsx"],
|
|
{ releasePackages }
|
|
);
|
|
|
|
assert.equal(classification.runAgentSessionReplay, false);
|
|
});
|
|
|
|
test("ordinary Desktop source changes skip the Windows installer", () => {
|
|
for (const file of [
|
|
"apps/desktop/src/main/index.ts",
|
|
"apps/desktop/src/preload/index.ts",
|
|
"apps/desktop/src/renderer/src/main.tsx",
|
|
"apps/desktop/test/register-asset-stub.mjs",
|
|
"apps/desktop/electron.vite.config.ts",
|
|
"apps/desktop/tsconfig.json"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], { releasePackages });
|
|
|
|
assert.equal(classification.buildWindowsInstaller, false, file);
|
|
}
|
|
});
|
|
|
|
test("Windows packaging inputs select the Windows installer", () => {
|
|
for (const file of [
|
|
"package.json",
|
|
"pnpm-lock.yaml",
|
|
"config/tutti.app-runtime.lock.json",
|
|
"apps/desktop/package.json",
|
|
"apps/desktop/build/icon.png",
|
|
"apps/desktop/scripts/vendor-managed-posix-shell.mjs",
|
|
"services/tuttid/builtin-apps/tutti-onboarding/src/main.ts"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], { releasePackages });
|
|
|
|
assert.equal(classification.buildWindowsInstaller, true, file);
|
|
}
|
|
});
|
|
|
|
test("published CSS and assets select package packing and UI boundaries", () => {
|
|
for (const file of [
|
|
"packages/agent/gui/app/renderer/agentactivity.css",
|
|
"packages/ui/system/src/icons/recent-lined.svg"
|
|
]) {
|
|
const classification = classifyChangedFiles([file], {
|
|
releasePackages
|
|
});
|
|
assert.equal(classification.runPack, true, file);
|
|
assert.equal(classification.runBoundaries, true, file);
|
|
assert.equal(classification.runTs, false, file);
|
|
}
|
|
});
|
|
|
|
test("deleted package manifests still select package packing", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/example/removed/package.json"],
|
|
{ releasePackages }
|
|
);
|
|
|
|
assert.equal(classification.runPack, true);
|
|
assert.equal(classification.packAll, true);
|
|
assert.deepEqual(classification.packPackages, [
|
|
"@tutti-os/agent-gui",
|
|
"@tutti-os/ui-system"
|
|
]);
|
|
});
|
|
|
|
test("test files and Vitest support files do not select package packing", () => {
|
|
const classification = classifyChangedFiles(
|
|
[
|
|
"packages/agent/gui/agent-gui/controller.spec.ts",
|
|
"packages/agent/gui/vitest.config.ts",
|
|
"packages/agent/gui/vitest.shared.setup.ts"
|
|
],
|
|
{ releasePackages }
|
|
);
|
|
|
|
assert.equal(classification.runPack, false);
|
|
assert.deepEqual(classification.packPackages, []);
|
|
});
|
|
|
|
test("test-only package manifest changes do not select package packing", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/agent/gui/package.json"],
|
|
{
|
|
isPackageManifestPackRelevant: () => false,
|
|
releasePackages
|
|
}
|
|
);
|
|
|
|
assert.equal(classification.runPack, false);
|
|
});
|
|
|
|
test("publish-relevant package manifest changes select every package", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/agent/gui/package.json"],
|
|
{ releasePackages }
|
|
);
|
|
|
|
assert.equal(classification.packAll, true);
|
|
assert.deepEqual(classification.packPackages, [
|
|
"@tutti-os/agent-gui",
|
|
"@tutti-os/ui-system"
|
|
]);
|
|
});
|
|
|
|
test("package manifest comparison ignores only test scripts", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "package-manifest-pack-"));
|
|
const packageRoot = join(root, "packages/agent/gui");
|
|
const manifestPath = join(packageRoot, "package.json");
|
|
const gitEnv = createIsolatedGitEnvironment(root);
|
|
const runGit = (args) =>
|
|
execFileSync("git", args, { cwd: root, env: gitEnv });
|
|
mkdirSync(packageRoot, { recursive: true });
|
|
|
|
try {
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
name: "@tutti-os/agent-gui",
|
|
scripts: { build: "tsup", test: "vitest run --old" }
|
|
})}\n`
|
|
);
|
|
runGit(["init", "--quiet"]);
|
|
runGit(["add", "."]);
|
|
runGit([
|
|
"-c",
|
|
"user.name=Test",
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"commit",
|
|
"--quiet",
|
|
"-m",
|
|
"init"
|
|
]);
|
|
const isRelevant = createPackageManifestPackRelevance({
|
|
baseRef: "HEAD",
|
|
root
|
|
});
|
|
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
name: "@tutti-os/agent-gui",
|
|
scripts: { build: "tsup", test: "vitest run" }
|
|
})}\n`
|
|
);
|
|
assert.equal(isRelevant("packages/agent/gui/package.json"), false);
|
|
|
|
const buildChangeIsRelevant = createPackageManifestPackRelevance({
|
|
baseRef: "HEAD",
|
|
root
|
|
});
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
name: "@tutti-os/agent-gui",
|
|
scripts: { build: "tsup --config tsup.config.ts", test: "vitest run" }
|
|
})}\n`
|
|
);
|
|
assert.equal(
|
|
buildChangeIsRelevant("packages/agent/gui/package.json"),
|
|
true
|
|
);
|
|
|
|
runGit(["add", "."]);
|
|
runGit([
|
|
"-c",
|
|
"user.name=Test",
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"commit",
|
|
"--quiet",
|
|
"-m",
|
|
"change build"
|
|
]);
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
name: "@tutti-os/agent-gui",
|
|
scripts: {
|
|
build: "tsup --config tsup.config.ts",
|
|
test: "vitest run --working-copy"
|
|
}
|
|
})}\n`
|
|
);
|
|
const committedBuildChangeIsRelevant = createPackageManifestPackRelevance({
|
|
baseRef: "HEAD^",
|
|
root
|
|
});
|
|
assert.equal(
|
|
committedBuildChangeIsRelevant("packages/agent/gui/package.json"),
|
|
true
|
|
);
|
|
|
|
const headRef = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
env: gitEnv
|
|
}).trim();
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
name: "@tutti-os/agent-gui",
|
|
scripts: {
|
|
build: "tsup --working-copy",
|
|
test: "vitest run --working-copy"
|
|
}
|
|
})}\n`
|
|
);
|
|
assert.equal(
|
|
createPackageManifestPackRelevance({
|
|
baseRef: "HEAD",
|
|
headRef,
|
|
root
|
|
})("packages/agent/gui/package.json"),
|
|
false
|
|
);
|
|
assert.equal(
|
|
createPackageManifestPackRelevance({
|
|
baseRef: "HEAD",
|
|
root
|
|
})("packages/agent/gui/package.json"),
|
|
true
|
|
);
|
|
} finally {
|
|
rmSync(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
test("published package changes select only their owning package", () => {
|
|
const classification = classifyChangedFiles(["packages/agent/gui/index.ts"], {
|
|
releasePackages
|
|
});
|
|
|
|
assert.equal(classification.runPack, true);
|
|
assert.equal(classification.packAll, false);
|
|
assert.deepEqual(classification.packPackages, ["@tutti-os/agent-gui"]);
|
|
});
|
|
|
|
test("global release inputs select every published package", () => {
|
|
const classification = classifyChangedFiles(["pnpm-lock.yaml"], {
|
|
releasePackages
|
|
});
|
|
|
|
assert.equal(classification.runPack, true);
|
|
assert.equal(classification.packAll, true);
|
|
assert.deepEqual(classification.packPackages, [
|
|
"@tutti-os/agent-gui",
|
|
"@tutti-os/ui-system"
|
|
]);
|
|
});
|
|
|
|
test("tool-only changes do not select workspace package tests", () => {
|
|
const classification = classifyChangedFiles(
|
|
["tools/scripts/check-package-packs.mjs"],
|
|
{
|
|
isRootManifestTestRelevant: () => false,
|
|
releasePackages,
|
|
workspacePackages
|
|
}
|
|
);
|
|
|
|
assert.equal(classification.runTs, true);
|
|
assert.equal(classification.runTsTests, false);
|
|
assert.deepEqual(classification.testPackages, []);
|
|
});
|
|
|
|
test("package changes select owning and dependent package tests", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/agent/gui/controller.spec.ts"],
|
|
{ releasePackages, workspacePackages }
|
|
);
|
|
|
|
assert.equal(classification.runTsTests, true);
|
|
assert.equal(classification.testAll, false);
|
|
assert.deepEqual(classification.testPackages, [
|
|
"@tutti-os/agent-gui",
|
|
"@tutti-os/desktop"
|
|
]);
|
|
});
|
|
|
|
test("packages without tests do not select workspace package tests", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/example/no-tests/index.ts"],
|
|
{ releasePackages, workspacePackages }
|
|
);
|
|
|
|
assert.equal(classification.runTsTests, false);
|
|
});
|
|
|
|
test("global test inputs select every workspace test package", () => {
|
|
const classification = classifyChangedFiles(["pnpm-lock.yaml"], {
|
|
releasePackages,
|
|
workspacePackages
|
|
});
|
|
|
|
assert.equal(classification.runTsTests, true);
|
|
assert.equal(classification.testAll, true);
|
|
assert.deepEqual(classification.testPackages, [
|
|
"@tutti-os/agent-gui",
|
|
"@tutti-os/desktop"
|
|
]);
|
|
});
|
|
|
|
test("deleted package manifests select every workspace test package", () => {
|
|
const classification = classifyChangedFiles(
|
|
["packages/example/deleted/package.json"],
|
|
{ releasePackages, workspacePackages }
|
|
);
|
|
|
|
assert.equal(classification.testAll, true);
|
|
});
|
|
|
|
test("root manifest comparison ignores unrelated scripts", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "root-manifest-tests-"));
|
|
const manifestPath = join(root, "package.json");
|
|
const gitEnv = createIsolatedGitEnvironment(root);
|
|
const runGit = (args) =>
|
|
execFileSync("git", args, { cwd: root, env: gitEnv });
|
|
|
|
try {
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
packageManager: "pnpm@10.11.0",
|
|
scripts: {
|
|
"release:pack:check": "old",
|
|
"test:ts": "node test.mjs"
|
|
}
|
|
})}\n`
|
|
);
|
|
runGit(["init", "--quiet"]);
|
|
runGit(["add", "."]);
|
|
runGit([
|
|
"-c",
|
|
"user.name=Test",
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"commit",
|
|
"--quiet",
|
|
"-m",
|
|
"init"
|
|
]);
|
|
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
packageManager: "pnpm@10.11.0",
|
|
scripts: {
|
|
"release:pack:check": "new",
|
|
"test:ts": "node test.mjs"
|
|
}
|
|
})}\n`
|
|
);
|
|
assert.equal(
|
|
createRootManifestTestRelevance({
|
|
baseRef: "HEAD",
|
|
root
|
|
})(),
|
|
false
|
|
);
|
|
|
|
writeFileSync(
|
|
manifestPath,
|
|
`${JSON.stringify({
|
|
packageManager: "pnpm@10.11.0",
|
|
scripts: {
|
|
"release:pack:check": "new",
|
|
"test:ts": "node changed-test.mjs"
|
|
}
|
|
})}\n`
|
|
);
|
|
assert.equal(
|
|
createRootManifestTestRelevance({
|
|
baseRef: "HEAD",
|
|
root
|
|
})(),
|
|
true
|
|
);
|
|
} finally {
|
|
rmSync(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
test("repository check registry selects only relevant generated checks", () => {
|
|
const checks = selectRepositoryChecks(
|
|
["packages/events/protocol/schemas/core/event-envelope.schema.json"],
|
|
{ group: "generated" }
|
|
);
|
|
|
|
assert.deepEqual(
|
|
checks.map((check) => check.key),
|
|
["generated:event-protocol"]
|
|
);
|
|
});
|
|
|
|
test("provider source changes select catalog and strategy checks", () => {
|
|
const checks = selectRepositoryChecks([
|
|
"packages/agent/daemon/providerregistry/providers.go"
|
|
]);
|
|
const keys = checks.map((check) => check.key);
|
|
|
|
assert.ok(keys.includes("generated:agent-provider-catalog"));
|
|
assert.ok(keys.includes("boundary:agent-provider-strategy"));
|
|
});
|
|
|
|
test("every DeviceLink package change selects the Android contract", () => {
|
|
for (const file of [
|
|
"packages/device-link/mobile/mobile.go",
|
|
"packages/device-link/Makefile",
|
|
"packages/device-link/mobile/androidprobe/AndroidManifest.xml"
|
|
]) {
|
|
const checks = selectRepositoryChecks([file]);
|
|
assert.ok(
|
|
checks.some((check) => check.key === "contracts:device-link-android"),
|
|
`${file} should select the DeviceLink Android contract`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("stylesheet and HTML changes select the backdrop-filter authoring policy", () => {
|
|
for (const file of [
|
|
"packages/workbench/launchpad/src/styles/workbench-launchpad.css",
|
|
"apps/desktop/index.html"
|
|
]) {
|
|
const checks = selectRepositoryChecks([file]);
|
|
|
|
assert.ok(
|
|
checks.some((check) => check.key === "policy:backdrop-filter-authoring"),
|
|
`${file} should select the backdrop-filter policy`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("stylesheet changes select the CSS :has() performance policy", () => {
|
|
const checks = selectRepositoryChecks([
|
|
"packages/agent/gui/app/renderer/agentactivity.css"
|
|
]);
|
|
|
|
assert.ok(checks.some((check) => check.key === "policy:css-has-performance"));
|
|
});
|
|
|
|
test("bounded runtime image changes select the image budget policy", () => {
|
|
const checks = selectRepositoryChecks([
|
|
"apps/desktop/src/renderer/src/assets/workspace-canvas/dock/default/codex.png"
|
|
]);
|
|
|
|
assert.ok(
|
|
checks.some((check) => check.key === "policy:runtime-image-budgets")
|
|
);
|
|
});
|