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
6 changes: 5 additions & 1 deletion .github/workflows/check-skills.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ on:
jobs:
validate:
name: Validate intent skills
if: github.event_name == 'pull_request'
# Changeset release PRs bump every package version at once, so `stale`
# structurally reports version drift there — the library_version markers
# mean "reviewed against this published version" and are reconciled by the
# review job below (or its review PR) after the release actually publishes.
if: github.event_name == 'pull_request' && github.head_ref != 'changeset-release/main'
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,13 @@ jobs:
name: Runtime image smoke
runs-on: ubuntu-latest
needs: [detect-changes, lint-and-typecheck]
if: needs.detect-changes.outputs.image-smoke == 'true'
# The smoke boots identity-driven against the published config, so it is a
# deploy-parity check: an image built on a release PR (framework train
# bumped) cannot match the bundles the last deploy published. The deploy
# train re-aligns config + image post-merge and gates itself on
# `bos mf check`; the image's self-contained boot path is covered by the
# regression browser start:* modes.
if: needs.detect-changes.outputs.image-smoke == 'true' && github.head_ref != 'changeset-release/main'
timeout-minutes: 25
env:
CI: true
Expand Down
7 changes: 5 additions & 2 deletions host/tests/unit/plugins-shared-registration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ describe("host shared registration entries", () => {
it("derives normalized registration entries from config-declared shared deps", () => {
const entries = buildSharedRegistrationEntries(
mergeSharedMaps(
{ "every-plugin": { version: "2.10.1", singleton: true } },
// Neutral literal — the helper normalizes whatever version it is
// given; pinning the live framework train here would break on every
// release PR.
{ "every-plugin": { version: "9.9.9", singleton: true } },
{ react: { version: "19.2.4", singleton: true, eager: true, shareScope: "default" } },
),
);
expect(entries["every-plugin"]).toEqual({
version: "2.10.1",
version: "9.9.9",
shareScope: "default",
shareConfig: {
singleton: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/every-plugin/tests/unit/shared-deps-spec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ describe("ticket 12 pins (no runtime debug)", () => {

describe("host registration policy helpers", () => {
it("normalizes config-declared entries with the host defaults", () => {
expect(toHostSharedEntry("every-plugin", { version: "2.10.1", singleton: true })).toEqual({
version: "2.10.1",
expect(toHostSharedEntry("every-plugin", { version: "9.9.9", singleton: true })).toEqual({
version: "9.9.9",
shareScope: "default",
shareConfig: {
singleton: true,
Expand Down
57 changes: 57 additions & 0 deletions packages/everything-dev/src/federation-train.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";

/**
* The auth app-slot's local workspace, derived from the resolved config:
* `app.auth.development: "local:<dir>"` → `<dir>`, null otherwise. Shared by
* the container-build orchestrator's build and staging phases — deriving it
* per-site duplicated the expression and scoped it away from callers.
*/
export function resolveAuthWorkspace(
bosConfig: { app?: { auth?: { development?: unknown } } } | undefined | null,
): string | null {
const development = bosConfig?.app?.auth?.development;
return typeof development === "string" && development.startsWith("local:")
? development.slice("local:".length)
: null;
}

export interface MfManifest {
shared?: Array<{ name?: string; version?: string; requiredVersion?: string | null }>;
}

/** The `every-plugin` share version a built remote stamps in its mf-manifest. */
export function everyPluginStamp(manifest: MfManifest): string | null {
const entry = manifest.shared?.find((dep) => dep.name === "every-plugin");
return entry?.version ?? entry?.requiredVersion ?? null;
}

export type FederationTrainCheck =
| { ok: true; stamp: string }
| {
ok: false;
reason: "missing-manifest" | "no-stamp" | "mismatch";
stamp: string | null;
};

/**
* A built remote must stamp the same `every-plugin` share version as the
* framework package on this checkout — a stale train surfaces at boot as a
* strict-singleton mismatch ("Version X from host … needs Y") and every
* /api/* route 503s. `distDir` is the workspace's built dist root.
*/
export function checkFederationTrain(distDir: string, expectedTrain: string): FederationTrainCheck {
const manifestPath = join(distDir, "mf-manifest.json");
if (!existsSync(manifestPath)) {
return { ok: false, reason: "missing-manifest", stamp: null };
}
let manifest: MfManifest;
try {
manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as MfManifest;
} catch {
return { ok: false, reason: "missing-manifest", stamp: null };
}
const stamp = everyPluginStamp(manifest);
if (!stamp) return { ok: false, reason: "no-stamp", stamp: null };
return stamp === expectedTrain ? { ok: true, stamp } : { ok: false, reason: "mismatch", stamp };
}
42 changes: 25 additions & 17 deletions packages/everything-dev/tests/integration/framework-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { globSync } from "glob";
import { readWorkspaceCatalog } from "../../src/workspace-catalog";

type FrameworkTarballs = {
"better-near-auth": string;
"every-plugin": string;
"everything-dev": string;
};
Expand All @@ -21,35 +22,42 @@ export function getFrameworkTarballs(repoRoot: string): Promise<FrameworkTarball
async function buildFrameworkTarballs(repoRoot: string): Promise<FrameworkTarballs> {
const tarballDir = mkdtempSync(join(tmpdir(), "everything-dev-framework-packages-"));

await execa("pnpm", ["--dir", "packages/every-plugin", "run", "build"], { cwd: repoRoot });
await execa("pnpm", ["--dir", "packages/everything-dev", "run", "build"], { cwd: repoRoot });
for (const packageName of ["better-near-auth", "every-plugin", "everything-dev"] as const) {
await execa("pnpm", ["--dir", `packages/${packageName}`, "run", "build"], { cwd: repoRoot });
}

const rootCatalog = readWorkspaceCatalog(repoRoot);

const everyPluginTarball = await stageAndPackFrameworkPackage({
repoRoot,
packageName: "every-plugin",
tarballDir,
rootCatalog,
});

// Bun fails to resolve a tarball when it is both a direct dependency and a
// transitive dependency (through another tarball) and both use the same
// file path. We create a copy with a different name for the transitive
// reference inside everything-dev so the paths differ.
const everyPluginTarballCopy = join(tarballDir, "every-plugin-2.7.0-copy.tgz");
cpSync(everyPluginTarball, everyPluginTarballCopy);
// The everything-dev tarball depends on the other framework packages and the
// scaffolded child depends on them directly — both references must use
// distinct tarball paths, so stage copies for the transitive side. These
// packages must pack before everything-dev, which consumes the copies.
const transitiveTarballs: Record<string, string> = {};
const directTarballs: Record<string, string> = {};
for (const packageName of ["better-near-auth", "every-plugin"] as const) {
const tarball = await stageAndPackFrameworkPackage({
repoRoot,
packageName,
tarballDir,
rootCatalog,
});
const copy = join(tarballDir, `${packageName}-transitive-copy.tgz`);
cpSync(tarball, copy);
directTarballs[packageName] = tarball;
transitiveTarballs[packageName] = copy;
}

const everythingDevTarball = await stageAndPackFrameworkPackage({
repoRoot,
packageName: "everything-dev",
tarballDir,
rootCatalog,
otherFrameworkTarballs: { "every-plugin": everyPluginTarballCopy },
otherFrameworkTarballs: transitiveTarballs,
});

return {
"every-plugin": everyPluginTarball,
"better-near-auth": directTarballs["better-near-auth"],
"every-plugin": directTarballs["every-plugin"],
"everything-dev": everythingDevTarball,
};
}
Expand Down
109 changes: 109 additions & 0 deletions packages/everything-dev/tests/unit/federation-train.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
checkFederationTrain,
everyPluginStamp,
resolveAuthWorkspace,
} from "../../src/federation-train";

describe("resolveAuthWorkspace", () => {
it("resolves a local auth app-slot to its workspace directory", () => {
expect(resolveAuthWorkspace({ app: { auth: { development: "local:plugins/auth" } } })).toBe(
"plugins/auth",
);
});

it("returns null when auth is absent", () => {
expect(resolveAuthWorkspace(undefined)).toBeNull();
expect(resolveAuthWorkspace(null)).toBeNull();
expect(resolveAuthWorkspace({})).toBeNull();
expect(resolveAuthWorkspace({ app: {} })).toBeNull();
expect(resolveAuthWorkspace({ app: { auth: {} } })).toBeNull();
});

it("returns null for malformed or non-local development values", () => {
expect(resolveAuthWorkspace({ app: { auth: { development: 42 } } })).toBeNull();
expect(
resolveAuthWorkspace({ app: { auth: { development: "https://auth.example.com" } } }),
).toBeNull();
});
});

describe("everyPluginStamp", () => {
it("reads the version from the every-plugin shared entry", () => {
expect(
everyPluginStamp({
shared: [
{ name: "zod", version: "4.6.5" },
{ name: "every-plugin", version: "2.10.1" },
],
}),
).toBe("2.10.1");
});

it("falls back to requiredVersion when version is absent", () => {
expect(
everyPluginStamp({ shared: [{ name: "every-plugin", requiredVersion: "3.0.0-rc.0" }] }),
).toBe("3.0.0-rc.0");
});

it("returns null when every-plugin is not shared", () => {
expect(everyPluginStamp({ shared: [{ name: "zod", version: "4.6.5" }] })).toBeNull();
expect(everyPluginStamp({})).toBeNull();
expect(everyPluginStamp({ shared: [] })).toBeNull();
});
});

describe("checkFederationTrain", () => {
const tempDirs: string[] = [];

afterEach(() => {
while (tempDirs.length > 0) {
const dir = tempDirs.pop();
if (dir) rmSync(dir, { recursive: true, force: true });
}
});

const distWithManifest = (manifest: unknown): string => {
const dir = mkdtempSync(join(tmpdir(), "federation-train-"));
tempDirs.push(dir);
writeFileSync(join(dir, "mf-manifest.json"), `${JSON.stringify(manifest)}\n`);
return dir;
};

it("passes when the stamp matches the expected train", () => {
const check = checkFederationTrain(
distWithManifest({ shared: [{ name: "every-plugin", version: "2.10.1" }] }),
"2.10.1",
);
expect(check).toEqual({ ok: true, stamp: "2.10.1" });
});

it("fails on a stale train and reports the stamped version", () => {
const check = checkFederationTrain(
distWithManifest({ shared: [{ name: "every-plugin", version: "2.10.1" }] }),
"3.0.0-rc.0",
);
expect(check).toEqual({ ok: false, reason: "mismatch", stamp: "2.10.1" });
});

it("fails when the dist predates federation metadata", () => {
const dir = mkdtempSync(join(tmpdir(), "federation-train-"));
tempDirs.push(dir);
expect(checkFederationTrain(dir, "2.10.1")).toEqual({
ok: false,
reason: "missing-manifest",
stamp: null,
});
});

it("fails when the manifest shares no every-plugin entry", () => {
const check = checkFederationTrain(
distWithManifest({ shared: [{ name: "zod", version: "4.6.5" }] }),
"2.10.1",
);
expect(check).toEqual({ ok: false, reason: "no-stamp", stamp: null });
});
});
Loading
Loading