Commit Graph
2 Commits
Author SHA1 Message Date
ekklesiaandClaude Opus 5.5 1caffc5f26 fix: hand npm the release artifact's absolute path, so publishing finds it
CI / verify (pull_request) Successful in 1m5s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / package-release (pull_request) Successful in 34s
CI / image-build (pull_request) Successful in 24s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
CI / package-publication (pull_request) Successful in 49s
The first real 0.2.0 publish failed on its first package with ENOENT:
"Publishing ekklesia-contracts-0.2.0.tgz failed: npm error code ENOENT".

The publish workflow passes a relative release path,
--release "$(find package-train …)", which is
package-train/ekklesia-packages-0.2.0. The tool validated the artifacts
relative to the job's working directory, but ran npm in its own
temporary home (cwd /tmp/ekklesia-publish-XXXXXX) with the same relative
path, package-train/ekklesia-packages-0.2.0/ekklesia-contracts-0.2.0.tgz,
which there names nothing. The disposable-registry suite always passed
an absolute path, so it never saw this.

- loadReleaseTrain resolves the release directory once; every artifact
  path is absolute from validation on, and npm is given only that exact
  file's absolute path (anything else is refused at the npm boundary).
- Just before each upload the file is checked against the validated
  bytes once more; the tool logs the absolute path it publishes, and a
  failure names it with npm's path/syscall lines (npm's temporary home
  masked, the token never shown).
- Regression tests: unit tests with a recording npm (relative nested
  release from another cwd; npm's argv, cwd, and the file's existence; a
  changed artifact refused before npm runs), which fail on the previous
  tool; and a disposable-Gitea test that runs the CLI exactly as the
  workflow does, from the Actions artifact layout, round-tripping all
  nine packages byte for byte.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:51:22 -05:00
ekklesiaandClaude Opus 5.5 d3c62e63bf feat: publish the exact @ekklesia package train to the npm registry (ADR 0053)
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Failing after 43s
CI / package-publication (pull_request) Skipped
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (split) (pull_request) Canceled after 0s
CI / deploy-smoke (same-origin) (pull_request) Canceled after 38s
Publishes one package release train to the Gitea npm registry under
River_of_Glory_Church, whose packages are publicly readable on purpose:
exactly the .tgz files the package-release gate proved, and nothing else.
Nothing is published by this change.

- scripts/publish-package-train.ts validates the whole local train before
  any request (gate manifest hash, version, revision, unmodified source,
  the nine packages, every artifact's size, SHA-256, integrity,
  SHA256SUMS line, and inner package.json), then publishes in dependency
  order with `npm publish <file.tgz>` under a staging tag. Each package
  is reconciled first (absent: publish; same bytes: accept; other bytes:
  hard stop, version burned), then read back: metadata (name, version,
  integrity, dependency maps, normalized bin, tarball on the registry)
  and the tarball itself, byte for byte, also anonymously. `latest` moves
  only after all nine verify; reruns converge and never delete anything.
  It writes a publication record. The token comes from
  EKKLESIA_PACKAGES_PUBLISH_TOKEN only, through a temporary npm config
  that refers to the variable.
- scripts/test-package-publication.sh proves it against a disposable
  Gitea 1.27.2 with public owners, a publisher team and an outsider:
  anonymous reads, refused unauthorized publishing, byte-identical round
  trips, the registry's own 409, partial-train recovery, the burned
  version hard stop, and dist-tag retry convergence.
- CI: package-release keeps its proven release and hands it on as an
  artifact with its manifest hash; the new package-publication job
  publishes exactly that artifact to the disposable Gitea. Artifact
  actions v3 (node20): v4 refuses any server other than github.com.
- .github/workflows/publish-packages.yml publishes to the real registry
  only by manual dispatch on main, after its own gate run, with the
  secret in the publish step alone.
- docs: the public-read model, the workflow, the record, credentials,
  immutability policy, provider moves, and the first-publication steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 09:49:27 -05:00