CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / package-release (pull_request) Failing after 16s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 44s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
Produces the exact public package tarballs of a platform release, validates them, and proves a standalone module consumes those artifacts end to end. Nothing is published and no credential is configured. - scripts/package-release.ts (pnpm packages:release) is the canonical release builder: from a clean commit it rebuilds the nine public packages from clean output, packs each, inspects every tarball, checks the release set and coordinated versions, and only then writes ekklesia-packages-<version>/ with the tarballs, SHA256SUMS, and a provider-neutral manifest (ekklesia-package-train.json: version, revision, and each package's file, size, SHA-256, npm integrity). - Packing uses a pack-only pnpm beforePacking hook (passed with --config.pnpmfile, never the workspace's install settings). pnpm inserts rewritten workspace: dependencies in async completion order, so two checkouts of one commit packed module-pack's package.json with keys in a different order; the hook restores the source key order (and the inspection enforces it). It also drops devDependencies, which named private packages outside the train. - scripts/test-package-release.sh (pnpm test:package-release, the CI package-release gate) builds the train twice, the second time from a separate worktree, requires byte-identical release directories, then runs the standalone consumer proof on the release: manifest checked, offline install from the tarballs only, lockfile integrity equal to the manifest's, frozen-lockfile reinstall, no Drizzle, one React, contracts only transitive, every @ekklesia package and module-pack at the release version, skipLibCheck: false, UI build, pack, and Core verification. - docs/development/releases.md documents the release order, the train, the artifacts, and the reproducibility findings; registry publication remains unimplemented. - ADR 0053 is Accepted: everything it decides that does not need a registry is now proven, and registry provisioning is on its own Deferred list. Co-Authored-By: Claude Opus 5.5 <[email protected]>
57 lines
2.1 KiB
JavaScript
57 lines
2.1 KiB
JavaScript
// pnpm hooks for packing the public package train, and nothing else: passed to `pnpm pack` with
|
|
// `--pnpmfile` by scripts/package-release.ts, never installed as the workspace's .pnpmfile.cjs, so
|
|
// installs and the lockfile are unaffected.
|
|
//
|
|
// `beforePacking` runs after pnpm has rewritten `workspace:` ranges into exact versions, and does two
|
|
// things to the packed package.json only:
|
|
//
|
|
// - It drops `devDependencies`. A consumer never installs them, and a tooling package's test-only
|
|
// development dependencies name private workspace packages (Core, the module template) that are
|
|
// not part of the public package train.
|
|
// - It puts every remaining dependency map back in the source package.json's own key order. pnpm
|
|
// rewrites `workspace:` ranges concurrently and inserts each entry as its lookup resolves, so two
|
|
// packs of the same commit could order those keys differently, the one difference that made
|
|
// otherwise byte-identical tarballs differ. No name or range changes.
|
|
"use strict";
|
|
|
|
const { readFileSync } = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const DEPENDENCY_FIELDS = [
|
|
"dependencies",
|
|
"optionalDependencies",
|
|
"peerDependencies",
|
|
"peerDependenciesMeta",
|
|
];
|
|
|
|
function inSourceOrder(packed, source) {
|
|
if (
|
|
packed === null ||
|
|
typeof packed !== "object" ||
|
|
source === null ||
|
|
typeof source !== "object"
|
|
) {
|
|
return packed;
|
|
}
|
|
const ordered = {};
|
|
for (const key of Object.keys(source)) {
|
|
if (Object.hasOwn(packed, key)) ordered[key] = packed[key];
|
|
}
|
|
for (const key of Object.keys(packed)) {
|
|
if (!Object.hasOwn(ordered, key)) ordered[key] = packed[key];
|
|
}
|
|
return ordered;
|
|
}
|
|
|
|
function beforePacking(manifest, directory) {
|
|
const source = JSON.parse(readFileSync(path.join(directory, "package.json"), "utf8"));
|
|
const packed = { ...manifest };
|
|
delete packed.devDependencies;
|
|
for (const field of DEPENDENCY_FIELDS) {
|
|
if (packed[field] !== undefined) packed[field] = inSourceOrder(packed[field], source[field]);
|
|
}
|
|
return packed;
|
|
}
|
|
|
|
module.exports = { hooks: { beforePacking }, inSourceOrder };
|