Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf7945abb6 | |||
| 74b6ff2ace | |||
| d109410617 | |||
| 5861e91298 | |||
| 8b2a224698 | |||
| 80b65b6f0c | |||
| 32652d5279 | |||
| 7e4fb60155 | |||
| b6cd2f3cd7 | |||
| 2f66f25153 | |||
| 2ee30c177c | |||
| 7177130c03 | |||
| 1965741625 | |||
| 220aaaa08c | |||
| 56bbcd22d1 | |||
| bb57488dbc | |||
| 2413926ad8 | |||
| 1f0c39b015 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git",
|
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
|
||||||
"sourceCommit": "398fd9e0d723c3fe7a511c2c35702874fd7342d0",
|
"sourceCommit": "7fd5e15105cef21d2b1c3da860c53c5033f66256",
|
||||||
"sourcePath": "quixos-instance/quixos-nix-helpers",
|
"sourcePath": "quixos-instance/quixos-nix-helpers",
|
||||||
"exportName": "quixos-nix-helpers",
|
"exportName": "quixos-nix-helpers",
|
||||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
const [schemaPath, packageRevisionId, bindingOutput, generatorPath, output] = process.argv.slice(2);
|
||||||
|
if (!schemaPath || !packageRevisionId || !bindingOutput || !generatorPath || !output) throw new Error("Missing candidate check receipt inputs");
|
||||||
|
const canonical = (value) => Array.isArray(value) ? value.map(canonical) : value && typeof value === "object"
|
||||||
|
? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, canonical(entry)])) : value;
|
||||||
|
const hash = (value) => `sha256:${crypto.createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`;
|
||||||
|
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
||||||
|
if (!schema.packages.some((entry) => entry.revisionId === packageRevisionId)) throw new Error("Checked binding schema lacks the package");
|
||||||
|
const generated = fs.readFileSync(bindingOutput, "utf8");
|
||||||
|
if (generated !== fs.readFileSync(".qx-checked-bindings", "utf8")) throw new Error("Build replaced candidate-generated bindings; its check is not evidence for this candidate");
|
||||||
|
const receipt = {
|
||||||
|
schemaVersion: 1, packageRevisionId, success: true, bindingSchema: schema,
|
||||||
|
bindingSchemaDigest: hash(schema), generatedDigest: hash(generated),
|
||||||
|
checkerDigest: hash({ generatorPath, compiler: JSON.parse(fs.readFileSync("node_modules/typescript/package.json", "utf8")), lock: fs.readFileSync("yarn.lock", "utf8") }),
|
||||||
|
};
|
||||||
|
fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { flag: "wx" });
|
||||||
+92
-14
@@ -510,6 +510,20 @@ EOF
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Language-neutral, offline schema compilation. Snapshot directories must be
|
||||||
|
# fixed Nix inputs matching the resource lock's complete dependency closure.
|
||||||
|
mkQxBindingSchema = { pkgs, protocol, src, repository, commit, resources ? [ ] }:
|
||||||
|
let
|
||||||
|
snapshots = pkgs.writeText "qx-binding-snapshots.json" (builtins.toJSON { inherit resources; });
|
||||||
|
in pkgs.runCommand "qx-binding-schema.json" { } ''
|
||||||
|
${protocol}/bin/quixos-resource-compile \
|
||||||
|
--root ${src} --kind package \
|
||||||
|
--repository ${pkgs.lib.escapeShellArg repository} \
|
||||||
|
--commit ${pkgs.lib.escapeShellArg commit} \
|
||||||
|
--checkout-root "$TMPDIR/checkouts" \
|
||||||
|
--snapshot-map ${snapshots} --snapshot-only true --schema-out "$out" > /dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
mkCaminoTsYarnNixifyFlake =
|
mkCaminoTsYarnNixifyFlake =
|
||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
@@ -521,8 +535,14 @@ EOF
|
|||||||
promptName ? null,
|
promptName ? null,
|
||||||
nodejsAttr ? "nodejs_24",
|
nodejsAttr ? "nodejs_24",
|
||||||
buildCommand ? "yarn build",
|
buildCommand ? "yarn build",
|
||||||
sourcePortals ? { },
|
|
||||||
buildEnv ? { },
|
buildEnv ? { },
|
||||||
|
# An exact, compiler-produced BindingSchema JSON artifact and a backend.
|
||||||
|
# Other language helpers can consume the same schema with their own generator/runtime.
|
||||||
|
bindings ? null,
|
||||||
|
bindingOptions ? { },
|
||||||
|
# A dedicated entrypoint calling SDK serveMigration; never start the
|
||||||
|
# normal package server in the isolated migration execution boundary.
|
||||||
|
migrationEntrypoint ? null,
|
||||||
nativeBuildInputs ? [ ],
|
nativeBuildInputs ? [ ],
|
||||||
devShellPackages ? [ ],
|
devShellPackages ? [ ],
|
||||||
devShellHook ? "",
|
devShellHook ? "",
|
||||||
@@ -532,6 +552,8 @@ EOF
|
|||||||
flake-utils.lib.eachDefaultSystem (
|
flake-utils.lib.eachDefaultSystem (
|
||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
|
outputsFor = candidateBindings:
|
||||||
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
lib = pkgs.lib;
|
lib = pkgs.lib;
|
||||||
nodejs = pkgs.${nodejsAttr};
|
nodejs = pkgs.${nodejsAttr};
|
||||||
@@ -561,12 +583,29 @@ EOF
|
|||||||
attrs
|
attrs
|
||||||
);
|
);
|
||||||
|
|
||||||
portalLinksFor = attrs:
|
bindingConfig = if candidateBindings != null then candidateBindings
|
||||||
lib.concatStringsSep "\n" (
|
else if bindings == null then null else callOption bindings;
|
||||||
lib.mapAttrsToList
|
bindingSchema = if bindingConfig == null then null else bindingConfig.schema or (mkQxBindingSchema {
|
||||||
(target: source: "ln -sfn ${source} ${lib.escapeShellArg target}")
|
inherit pkgs;
|
||||||
attrs
|
protocol = bindingConfig.generator;
|
||||||
);
|
src = packageRoot;
|
||||||
|
inherit (bindingConfig) repository commit;
|
||||||
|
resources = bindingConfig.resources or [ ];
|
||||||
|
});
|
||||||
|
bindingOutput = if bindingConfig == null then "src/gen/qx.ts" else bindingConfig.output or "src/gen/qx.ts";
|
||||||
|
bindingOptionsFile = if bindingConfig == null then null else
|
||||||
|
pkgs.writeText "qx-typescript-options.json" (builtins.toJSON (bindingConfig.options or bindingOptions));
|
||||||
|
bindingCommand = if bindingConfig == null then "" else ''
|
||||||
|
mkdir -p ${lib.escapeShellArg (dirOf bindingOutput)}
|
||||||
|
${bindingConfig.generator}/bin/quixos-codegen-ts \
|
||||||
|
${lib.escapeShellArg (toString bindingSchema)} \
|
||||||
|
${lib.escapeShellArg bindingConfig.packageRevisionId} \
|
||||||
|
${lib.escapeShellArg bindingOutput} ${bindingOptionsFile}
|
||||||
|
'';
|
||||||
|
generateBindings = pkgs.writeShellApplication {
|
||||||
|
name = "qx-generate-bindings";
|
||||||
|
text = bindingCommand;
|
||||||
|
};
|
||||||
|
|
||||||
bundleConfig = if bundle == null then { } else bundle;
|
bundleConfig = if bundle == null then { } else bundle;
|
||||||
bundleOutfile = bundleConfig.outfile or "server.mjs";
|
bundleOutfile = bundleConfig.outfile or "server.mjs";
|
||||||
@@ -574,6 +613,14 @@ EOF
|
|||||||
bundleTarget = bundleConfig.target or "node24";
|
bundleTarget = bundleConfig.target or "node24";
|
||||||
bundleFormat = bundleConfig.format or "esm";
|
bundleFormat = bundleConfig.format or "esm";
|
||||||
bundleBanner = bundleConfig.banner or nodeRequireBanner;
|
bundleBanner = bundleConfig.banner or nodeRequireBanner;
|
||||||
|
bundleAliases = bundleConfig.aliases or {
|
||||||
|
"@automerge/automerge" = "./node_modules/@automerge/automerge/dist/mjs/entrypoints/fullfat_base64.js";
|
||||||
|
};
|
||||||
|
bundleAliasArgs = lib.concatStringsSep " " (
|
||||||
|
lib.mapAttrsToList
|
||||||
|
(from: to: "--alias:${from}=${lib.escapeShellArg to}")
|
||||||
|
bundleAliases
|
||||||
|
);
|
||||||
nodeRequireBanner = "import { createRequire } from 'module';const require = createRequire(import.meta.url);";
|
nodeRequireBanner = "import { createRequire } from 'module';const require = createRequire(import.meta.url);";
|
||||||
bundleCommand =
|
bundleCommand =
|
||||||
if bundle == null
|
if bundle == null
|
||||||
@@ -584,6 +631,7 @@ EOF
|
|||||||
--platform=${bundlePlatform} \
|
--platform=${bundlePlatform} \
|
||||||
--target=${bundleTarget} \
|
--target=${bundleTarget} \
|
||||||
--format=${bundleFormat} \
|
--format=${bundleFormat} \
|
||||||
|
${bundleAliasArgs} \
|
||||||
${lib.optionalString (bundleConfig.preserveSymlinks or true) "--preserve-symlinks \\"}
|
${lib.optionalString (bundleConfig.preserveSymlinks or true) "--preserve-symlinks \\"}
|
||||||
--banner:js=${lib.escapeShellArg bundleBanner} \
|
--banner:js=${lib.escapeShellArg bundleBanner} \
|
||||||
--outfile=${lib.escapeShellArg bundleOutfile}
|
--outfile=${lib.escapeShellArg bundleOutfile}
|
||||||
@@ -620,6 +668,17 @@ EOF
|
|||||||
${lib.optionalString (installConfig ? descriptorPath && descriptorPath != null) ''
|
${lib.optionalString (installConfig ? descriptorPath && descriptorPath != null) ''
|
||||||
cp ${lib.escapeShellArg descriptorPath} "$out/${descriptorPath}"
|
cp ${lib.escapeShellArg descriptorPath} "$out/${descriptorPath}"
|
||||||
''}
|
''}
|
||||||
|
${lib.optionalString (bindingConfig != null) ''
|
||||||
|
install -m 0444 quixos-check.json "$out/quixos-check.json"
|
||||||
|
''}
|
||||||
|
${lib.optionalString (migrationEntrypoint != null) ''
|
||||||
|
install -Dm444 migration.mjs "$out/libexec/${serverLibexecName}/migration.mjs"
|
||||||
|
cat > "$out/bin/migrate" <<EOF
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
exec ${nodejs}/bin/node --max-old-space-size=256 "$out/libexec/${serverLibexecName}/migration.mjs" "\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "$out/bin/migrate"
|
||||||
|
''}
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@@ -628,16 +687,26 @@ EOF
|
|||||||
overrideAttrs = old: {
|
overrideAttrs = old: {
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
(old.nativeBuildInputs or [ ])
|
(old.nativeBuildInputs or [ ])
|
||||||
++ lib.optional (bundle != null) pkgs.esbuild
|
++ lib.optional (bundle != null || migrationEntrypoint != null) pkgs.esbuild
|
||||||
++ callOption nativeBuildInputs;
|
++ callOption nativeBuildInputs;
|
||||||
preConfigure = (old.preConfigure or "") + ''
|
|
||||||
${portalLinksFor (callOption sourcePortals)}
|
|
||||||
'';
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
${exportsFor (callOption buildEnv)}
|
${exportsFor (callOption buildEnv)}
|
||||||
|
${bindingCommand}
|
||||||
|
${lib.optionalString (bindingConfig != null) "yarn exec tsc --noEmit"}
|
||||||
|
${lib.optionalString (bindingConfig != null) "cp ${lib.escapeShellArg bindingOutput} .qx-checked-bindings"}
|
||||||
${buildCommand}
|
${buildCommand}
|
||||||
|
${lib.optionalString (bindingConfig != null) ''
|
||||||
|
yarn exec tsc --noEmit
|
||||||
|
node ${./check-receipt.mjs} ${lib.escapeShellArg (toString bindingSchema)} \
|
||||||
|
${lib.escapeShellArg bindingConfig.packageRevisionId} ${lib.escapeShellArg bindingOutput} \
|
||||||
|
${lib.escapeShellArg (toString bindingConfig.generator)} quixos-check.json
|
||||||
|
''}
|
||||||
${bundleCommand}
|
${bundleCommand}
|
||||||
|
${lib.optionalString (migrationEntrypoint != null) ''
|
||||||
|
esbuild ${lib.escapeShellArg migrationEntrypoint} --bundle --platform=node --target=node24 --format=esm \
|
||||||
|
${bundleAliasArgs} --preserve-symlinks --banner:js=${lib.escapeShellArg nodeRequireBanner} --outfile=migration.mjs
|
||||||
|
''}
|
||||||
runHook postBuild
|
runHook postBuild
|
||||||
'';
|
'';
|
||||||
} // lib.optionalAttrs (installServerPhase != null) {
|
} // lib.optionalAttrs (installServerPhase != null) {
|
||||||
@@ -672,11 +741,19 @@ EOF
|
|||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
packages = [
|
packages = [
|
||||||
nodejs
|
nodejs
|
||||||
pkgs.yarn-berry_4
|
project.yarn-freestanding
|
||||||
] ++ callOption devShellPackages;
|
] ++ lib.optional (bindingConfig != null) generateBindings ++ callOption devShellPackages;
|
||||||
|
# Explicit command keeps shell entry free of source mutations.
|
||||||
shellHook = devShellHookBase + callOption devShellHook;
|
shellHook = devShellHookBase + callOption devShellHook;
|
||||||
};
|
};
|
||||||
} // maybeServerOutputs
|
} // maybeServerOutputs;
|
||||||
|
in (outputsFor null) // {
|
||||||
|
# The workspace supplies a compiler-produced schema for the exact
|
||||||
|
# candidate graph. Standalone builds may use checked-in authoring types,
|
||||||
|
# but only this build path regenerates and witnesses candidate contracts.
|
||||||
|
quixosPackages.checkedServer = { schema, generator, packageRevisionId }:
|
||||||
|
(outputsFor { inherit schema generator packageRevisionId; }).packages.server;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -685,5 +762,6 @@ in
|
|||||||
mkTsPackageServer
|
mkTsPackageServer
|
||||||
mkSchemaSupport
|
mkSchemaSupport
|
||||||
mkCaminoSourcePackage
|
mkCaminoSourcePackage
|
||||||
|
mkQxBindingSchema
|
||||||
mkCaminoTsYarnNixifyFlake;
|
mkCaminoTsYarnNixifyFlake;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user