Self-hosted: coherent versioning across images, configs and templates - #1460
Conversation
The hosting API installs from a committed lockfile with npm ci, so a sha image tag finally pins its contents (a floating dependency made the same tag able to produce different artifacts). Tagging self-hosted-vX.Y.Z now builds the same images with an immutable version tag plus latest, giving independent deployments something meaningful to pin; the develop-gated deploy is untouched by tag pushes. Both images bake GIT_SHA: the API answers it from /health beside its package version and the SPA carries it as data-build on the document, so skew between the paired images is observable instead of a guess. The DOM applier clamps unknown template ids to the roster default, agreeing with the component registry (a newer config meeting an older image rendered an unstyled page while components rendered medium). The config version field's schema-major semantics and the reserved, unenforced tier field are documented where they live.
Code Review by Qodo
1. /health response untested fields
|
|
Warning Review limit reached
Next review available in: 13 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (13)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoSelf-hosted: coherent versioning across images, configs, and templates
AI Description
Diagram
High-Level Assessment
Files changed (12)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1370c9943d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| */ | ||
|
|
||
| import { Hono } from 'hono'; | ||
| import { version as apiVersion } from '../package.json'; |
There was a problem hiding this comment.
Import the package version in a Node-compatible way
When the production container executes npx tsx src/index.ts, this package is ESM ("type": "module"), but Node JSON modules do not provide named exports and require JSON import attributes. TypeScript's bundler resolution leaves this statement unchanged, so the API exits during module loading instead of starting. Use the JSON default export with a Node-compatible import attribute, or obtain the version through createRequire/an environment variable.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in the follow-up commit: the version is read with fs from import.meta.url (with an unknown fallback) instead of a JSON named import, verified against a live Node ESM run. Good catch — vitest's bundler resolution masks exactly this class.
This package is ESM: Node's JSON modules need import attributes and carry no named exports, so the import form that typechecks under bundler resolution crashes tsx at boot. Read with fs from import.meta.url and fall back to unknown.
Code Review by Qodo
1. /health response untested fields
|
| version: apiVersion, | ||
| sha: process.env.GIT_SHA || 'unknown', | ||
| }), |
There was a problem hiding this comment.
1. /health response untested fields 📘 Rule violation ▣ Testability
The /health endpoint now returns additional version and sha fields, but no corresponding automated test was added/updated in this PR to verify the new response shape. This can allow regressions in build observability to slip through unnoticed.
Agent Prompt
## Issue description
The hosting API `/health` route was changed to include `version` and `sha`, but the change is not covered by a new/updated test in this PR.
## Issue Context
This endpoint is used for build observability; a regression (missing/renamed fields, wrong defaults) would reduce the ability to detect skew between the blog and API images.
## Fix Focus Areas
- apps/self-hosted/hosting/api/src/index.ts[57-68]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in the follow-up commit: the health payload moved into utils/build-info.ts with tests covering the real version read, the baked sha and the unknown fallbacks.
…ict semver main gets its own channel tag, since an untagged main commit overwriting what a release just pinned is exactly the incoherence this PR exists to end, and the release path refuses any tag that is not self-hosted-vX.Y.Z loudly instead of publishing junk. CI installs the hosting API with npm ci to match the image build, so lockfile drift fails in tests rather than resolving silently at image build. The health payload moves into a tested util (version read plus sha fallback both covered).
… answer The health endpoint reported the API package version, a third clock nothing ties to the self-hosted-vX.Y.Z tag: an image released as v1.0.1 would still answer 1.0.0. Release-tag builds now bake RELEASE_VERSION into both images; /health answers it (untagged for sha-only builds, never a number nothing enforces) and the SPA carries data-version beside data-build when present. The package.json read is gone.
Closes #1454
Implements the versioning recommendations from the #1454 audit.
Reproducible images
The hosting API had no lockfile and floated its SDK dependency, so the same sha image tag could produce different artifacts whenever a dependency released. The lockfile is committed and the Dockerfile installs with
npm ci.Releases
Pushing a
self-hosted-vX.Y.Zgit tag builds the same blog and API images with an immutable version tag alongside the sha tag, pluslatest(a release is what latest means to an independent deployment). The develop-gated deploy job is untouched by tag pushes.Observable builds
Both images bake
GIT_SHA: the API answers{ version, sha }from /health and the SPA carriesdata-buildon the document element, so skew between the paired images (built from one commit, tagged independently) is observable instead of a guess.Agreeing fallbacks
The DOM applier clamps an unknown template id to the roster default, matching the component registry: a newer config meeting an older image (rollback, or a pinned independent deployment) used to render an unstyled page while components rendered the default. Test pins the clamp.
Defined semantics
The config
versionfield is documented as the schema major with a never-bump-without-a-migration rule at the single place every document passes through, and the manifesttierfield is documented as reserved and unenforced (premium tiers were rejected).Tests
929 SPA tests (unknown-id clamp added), 434 hosting API tests, both typechecks, SPA production build.