everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
145 lines (143 loc) • 6.56 kB
JavaScript
const require_image = require('./image.cjs');
const require_catalog = require('../workspace/catalog.cjs');
const require_config_slots = require('../config-slots.cjs');
const require_semver = require('../../utils/semver.cjs');
const require_services_deploy_integrity = require('./integrity.cjs');
const require_manifest = require('../platform/manifest.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
//#region src/services/deploy/platform-manifest.ts
/**
* The deploy-side half of the platform release manifest (ADR 0034 pt 1/2):
* wires the framework repo's release inputs into the pure composer and shapes
* the registry entry that rides the train's atomic FastKV write. Framework-
* rooted deploys only — a child deploy publishes no release manifest.
*/
/** The universal image (ADR 0020/0021) — the release manifest's image ref. */
const PLATFORM_IMAGE_REF = "ghcr.io/nearbuilders/everything-dev";
/**
* The release's pinned slots: every config slot (dotted config path, keyed by
* the same path shape the release-slot stamping writes back — `app.auth`,
* `app.auth.ui`, `plugins.votes.ui`, …) with a production URL and an
* immutable version-manifest pin in the release's publish payload. The
* release leg records them in the release manifest so pinned children can
* re-point inherited slots at these bytes.
*/
function collectReleaseSlotPins(config) {
const slots = [];
require_config_slots.walkConfigSlots(config, ({ slot, entry }) => {
if (typeof entry !== "object" || entry === null) return;
const base = typeof entry.production === "string" ? entry.production : void 0;
const pin = entry.pin;
const manifest = typeof pin?.manifest === "string" ? pin.manifest : void 0;
const integrity = typeof pin?.integrity === "string" ? pin.integrity : void 0;
if (base && manifest && integrity) slots.push({
slot,
base,
pin: {
manifest,
integrity
}
});
});
return slots;
}
/** The `Record<slot, ReleaseSlotRecord>` shape the manifest schema stores. */
function releaseSlotMap(config) {
return Object.fromEntries(collectReleaseSlotPins(config).map(({ slot, base, pin }) => [slot, {
base,
pin
}]));
}
function readWorkspacePackageVersion(configDir, name) {
try {
const raw = (0, node_fs.readFileSync)((0, node_path.join)(configDir, "packages", name, "package.json"), "utf-8");
const pkg = JSON.parse(raw);
return typeof pkg.version === "string" ? pkg.version : void 0;
} catch {
return;
}
}
function readEveryPluginVersion(configDir) {
return readWorkspacePackageVersion(configDir, "every-plugin");
}
function composePlatformManifestPublishEntry(opts) {
const frameworkVersion = require_image.readFrameworkVersion(opts.configDir);
const everyPluginVersion = readEveryPluginVersion(opts.configDir);
if (!frameworkVersion || !everyPluginVersion) return null;
const manifest = require_manifest.composePlatformReleaseManifest({
frameworkVersion,
everyPluginVersion,
...opts.gitSha ? { gitSha: opts.gitSha } : {},
imageRef: opts.imageRef ?? "ghcr.io/nearbuilders/everything-dev",
catalog: require_catalog.readWorkspaceCatalog(opts.configDir),
templateInventory: require_manifest.computeTemplateInventory(opts.configDir),
compatMinChild: opts.previousFrameworkVersion ?? frameworkVersion,
...opts.publishPayload ? { slots: releaseSlotMap(opts.publishPayload) } : {}
});
const value = require_manifest.serializePlatformReleaseManifest(manifest);
return {
key: require_manifest.getPlatformManifestKey(opts.account, opts.gateway),
value,
integrity: require_services_deploy_integrity.computeSriHash(value),
manifest
};
}
/**
* The truth gate (#546): every uploaded workspace's version manifest declares
* the shared-singleton versions its build embedded; the release manifest pins
* the same names from the release catalog. A version that appears in both but
* disagrees means the workspace's install drifted from the catalog — the
* manifest would lie about the uploaded bytes and pinned children would boot
* a skewed composition. Pin shapes mirror `checkPlatformCompat`'s semantics
* (via `satisfiesConstraint`): an exact pin must match exactly, a range pin
* must be satisfied by the built version — the catalog pinning every-plugin
* as `^X` while bundles record the exact resolved version is not a violation.
* Returns human-readable violations; empty = clean.
*/
function platformTruthViolations(manifest, sharedByWorkspace) {
const violations = [];
for (const [workspace, shared] of sharedByWorkspace) {
if (!shared) continue;
for (const [name, version] of Object.entries(shared)) {
const pin = manifest.sharedDeps[name];
if (pin && !require_semver.satisfiesConstraint(version, pin.version)) violations.push(`${workspace} was built against ${name}@${version}, the release manifest pins ${pin.version} — a stale install publishes a manifest that lies about the uploaded bytes; run pnpm install and redeploy`);
}
}
return violations;
}
/**
* The derived-pin seam for image refs (ADR 0034 pt 2): when the platform
* manifest resolves, a runtime's image ref derives as `ref@v<version>` —
* never hand-pinned. Digest resolution rides the registry and arrives as
* `digest` when the caller can fetch it; the deploy preflight consumer is
* `resolveChildImagePin` (`services/deploy/legs.ts`), which resolves the
* tag's digest and repoints the Railway service source.
*/
async function deriveImageRefFromPlatformManifest(opts) {
const resolved = await require_manifest.resolvePlatformManifest({
configDir: opts.configDir,
localConfig: opts.localConfig,
fetchManifest: opts.fetchManifest,
...opts.fetchSlot ? { fetchSlot: opts.fetchSlot } : {},
...opts.fetchParent ? { fetchParent: opts.fetchParent } : {}
});
if (!resolved) return null;
const tag = resolved.manifest.image.tags[0];
if (!tag) return null;
return {
image: resolved.manifest.image.ref,
tag
};
}
//#endregion
exports.PLATFORM_IMAGE_REF = PLATFORM_IMAGE_REF;
exports.collectReleaseSlotPins = collectReleaseSlotPins;
exports.composePlatformManifestPublishEntry = composePlatformManifestPublishEntry;
exports.deriveImageRefFromPlatformManifest = deriveImageRefFromPlatformManifest;
exports.getPlatformManifestKey = require_manifest.getPlatformManifestKey;
exports.getPlatformVersionSlotKey = require_manifest.getPlatformVersionSlotKey;
exports.platformTruthViolations = platformTruthViolations;
exports.readEveryPluginVersion = readEveryPluginVersion;
exports.releaseSlotMap = releaseSlotMap;
//# sourceMappingURL=platform-manifest.cjs.map