Skip to content

Self-hosted: coherent versioning across images, configs and templates - #1460

Merged
feruzm merged 4 commits into
developfrom
fix/self-hosted-versioning
Aug 12, 2026
Merged

Self-hosted: coherent versioning across images, configs and templates#1460
feruzm merged 4 commits into
developfrom
fix/self-hosted-versioning

Conversation

@feruzm

@feruzm feruzm commented Aug 12, 2026

Copy link
Copy Markdown
Member

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.Z git tag builds the same blog and API images with an immutable version tag alongside the sha tag, plus latest (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 carries data-build on 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 version field is documented as the schema major with a never-bump-without-a-migration rule at the single place every document passes through, and the manifest tier field 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.

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.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (3) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. /health response untested fields 📘 Rule violation ▣ Testability
Description
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.
Code

apps/self-hosted/hosting/api/src/index.ts[R65-67]

+    version: apiVersion,
+    sha: process.env.GIT_SHA || 'unknown',
+  }),
Relevance

●●● Strong

Repo frequently accepts adding/updating API tests for functional changes; new /health fields likely
expected to be asserted.

PR-#1306
PR-#1439

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667972 requires tests for new/modified functional code paths. The PR changes the
/health payload to include new fields (version, sha) in the route handler.

Rule 2667972: Require tests for all new functional code paths
apps/self-hosted/hosting/api/src/index.ts[57-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


2. CI/Docker install mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The hosting API Dockerfile switched to npm ci (lockfile-strict), but the GitHub Actions test step
still runs npm install, which can tolerate/repair lockfile drift that npm ci would hard-fail on.
This can let CI pass while the Docker image build fails, or test a dependency state that isn’t the
one the image ships.
Code

apps/self-hosted/hosting/api/Dockerfile[R15-16]

+COPY package.json package-lock.json ./
+RUN npm ci
Relevance

●●● Strong

Team often accepts workflow reliability fixes; aligning CI install method prevents Docker build-only
failures.

PR-#702
PR-#1192

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Dockerfile now installs via npm ci, while the workflow’s hosting API tests still use `npm
install. Per npm’s documentation, npm ci exits with an error when package.json` and
package-lock.json are out of sync instead of updating the lockfile, so CI using npm install can
mask failures that will occur during image builds.

apps/self-hosted/hosting/api/Dockerfile[12-16]
.github/workflows/self-hosted.yml[92-95]
🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hosting API Docker image build now uses `npm ci` (lockfile-strict), but CI tests still run `npm install`. This introduces a parity gap: `npm install` may succeed by modifying/repairing lock state that `npm ci` would reject, allowing CI to pass while the Docker build fails.
### Issue Context
- Dockerfile switched to `npm ci` for reproducible, locked installs.
- Workflow test step still uses `npm install`.
### Fix Focus Areas
- .github/workflows/self-hosted.yml[92-96]
- apps/self-hosted/hosting/api/Dockerfile[12-16]
### Suggested change
- Update the workflow step `cd apps/self-hosted/hosting/api && npm install && npm test` to `cd apps/self-hosted/hosting/api && npm ci && npm test`.
- (Optional) Add a post-step check that `package-lock.json` is unchanged (e.g., `git diff --exit-code package-lock.json`) to ensure CI never silently regenerates lock state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. /health response untested fields 📘 Rule violation ▣ Testability
Description
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.
Code

apps/self-hosted/hosting/api/src/index.ts[R65-67]

+    version: apiVersion,
+    sha: process.env.GIT_SHA || 'unknown',
+  }),
Relevance

●●● Strong

Repo frequently accepts adding/updating API tests for functional changes; new /health fields likely
expected to be asserted.

PR-#1306
PR-#1439

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667972 requires tests for new/modified functional code paths. The PR changes the
/health payload to include new fields (version, sha) in the route handler.

Rule 2667972: Require tests for all new functional code paths
apps/self-hosted/hosting/api/src/index.ts[57-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


View medium (3)
4. CI/Docker install mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The hosting API Dockerfile switched to npm ci (lockfile-strict), but the GitHub Actions test step
still runs npm install, which can tolerate/repair lockfile drift that npm ci would hard-fail on.
This can let CI pass while the Docker image build fails, or test a dependency state that isn’t the
one the image ships.
Code

apps/self-hosted/hosting/api/Dockerfile[R15-16]

+COPY package.json package-lock.json ./
+RUN npm ci
Relevance

●●● Strong

Team often accepts workflow reliability fixes; aligning CI install method prevents Docker build-only
failures.

PR-#702
PR-#1192

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Dockerfile now installs via npm ci, while the workflow’s hosting API tests still use `npm
install. Per npm’s documentation, npm ci exits with an error when package.json` and
package-lock.json are out of sync instead of updating the lockfile, so CI using npm install can
mask failures that will occur during image builds.

apps/self-hosted/hosting/api/Dockerfile[12-16]
.github/workflows/self-hosted.yml[92-95]
🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hosting API Docker image build now uses `npm ci` (lockfile-strict), but CI tests still run `npm install`. This introduces a parity gap: `npm install` may succeed by modifying/repairing lock state that `npm ci` would reject, allowing CI to pass while the Docker build fails.
### Issue Context
- Dockerfile switched to `npm ci` for reproducible, locked installs.
- Workflow test step still uses `npm install`.
### Fix Focus Areas
- .github/workflows/self-hosted.yml[92-96]
- apps/self-hosted/hosting/api/Dockerfile[12-16]
### Suggested change
- Update the workflow step `cd apps/self-hosted/hosting/api && npm install && npm test` to `cd apps/self-hosted/hosting/api && npm ci && npm test`.
- (Optional) Add a post-step check that `package-lock.json` is unchanged (e.g., `git diff --exit-code package-lock.json`) to ensure CI never silently regenerates lock state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. /health response untested fields 📘 Rule violation ▣ Testability
Description
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.
Code

apps/self-hosted/hosting/api/src/index.ts[R65-67]

+    version: apiVersion,
+    sha: process.env.GIT_SHA || 'unknown',
+  }),
Relevance

●●● Strong

Repo frequently accepts adding/updating API tests for functional changes; new /health fields likely
expected to be asserted.

PR-#1306
PR-#1439

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667972 requires tests for new/modified functional code paths. The PR changes the
/health payload to include new fields (version, sha) in the route handler.

Rule 2667972: Require tests for all new functional code paths
apps/self-hosted/hosting/api/src/index.ts[57-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


6. CI/Docker install mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The hosting API Dockerfile switched to npm ci (lockfile-strict), but the GitHub Actions test step
still runs npm install, which can tolerate/repair lockfile drift that npm ci would hard-fail on.
This can let CI pass while the Docker image build fails, or test a dependency state that isn’t the
one the image ships.
Code

apps/self-hosted/hosting/api/Dockerfile[R15-16]

+COPY package.json package-lock.json ./
+RUN npm ci
Relevance

●●● Strong

Team often accepts workflow reliability fixes; aligning CI install method prevents Docker build-only
failures.

PR-#702
PR-#1192

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Dockerfile now installs via npm ci, while the workflow’s hosting API tests still use `npm
install. Per npm’s documentation, npm ci exits with an error when package.json` and
package-lock.json are out of sync instead of updating the lockfile, so CI using npm install can
mask failures that will occur during image builds.

apps/self-hosted/hosting/api/Dockerfile[12-16]
.github/workflows/self-hosted.yml[92-95]
🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.: 🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hosting API Docker image build now uses `npm ci` (lockfile-strict), but CI tests still run `npm install`. This introduces a parity gap: `npm install` may succeed by modifying/repairing lock state that `npm ci` would reject, allowing CI to pass while the Docker build fails.
### Issue Context
- Dockerfile switched to `npm ci` for reproducible, locked installs.
- Workflow test step still uses `npm install`.
### Fix Focus Areas
- .github/workflows/self-hosted.yml[92-96]
- apps/self-hosted/hosting/api/Dockerfile[12-16]
### Suggested change
- Update the workflow step `cd apps/self-hosted/hosting/api && npm install && npm test` to `cd apps/self-hosted/hosting/api && npm ci && npm test`.
- (Optional) Add a post-step check that `package-lock.json` is unchanged (e.g., `git diff --exit-code package-lock.json`) to ensure CI never silently regenerates lock state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@feruzm, you've reached your PR review limit, so we couldn't start this review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9955f1cb-e483-4f65-a95b-996c319bc6e0

📥 Commits

Reviewing files that changed from the base of the PR and between 5aaf3bd and e197219.

⛔ Files ignored due to path filters (1)
  • apps/self-hosted/hosting/api/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • .github/workflows/self-hosted.yml
  • apps/self-hosted/Dockerfile
  • apps/self-hosted/hosting/api/Dockerfile
  • apps/self-hosted/hosting/api/src/index.ts
  • apps/self-hosted/hosting/api/src/utils/build-info.test.ts
  • apps/self-hosted/hosting/api/src/utils/build-info.ts
  • apps/self-hosted/rsbuild.config.ts
  • apps/self-hosted/src/build-info.d.ts
  • apps/self-hosted/src/core/apply-config-dom.test.ts
  • apps/self-hosted/src/core/apply-config-dom.ts
  • apps/self-hosted/src/core/configuration-loader.ts
  • apps/self-hosted/src/index.tsx
  • apps/self-hosted/src/themes/manifest.ts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 12, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Self-hosted: coherent versioning across images, configs, and templates

✨ Enhancement 🐞 Bug fix ⚙️ Configuration changes 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Publish self-hosted release images from self-hosted-vX.Y.Z tags with immutable version tags.
• Make hosting-api builds reproducible by committing package-lock.json and using npm ci.
• Expose build sha in API /health and SPA DOM; clamp unknown template ids to default.
Diagram

graph TD
  A{{"Git ref (branch/tag)"}} --> B["CI: self-hosted workflow"] --> C["Blog image build"] --> E["SPA runtime"]
  B["CI: self-hosted workflow"] --> D["API image build"] --> F["Hosting API"]
  E["SPA runtime"] --> G["Config DOM apply"]
  E -. "Build sha visible" .-> F["Hosting API"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use OCI image labels for version/sha
  • ➕ Standardized metadata discoverable without app endpoints/DOM inspection
  • ➕ Keeps build provenance out of runtime env/HTML surface
  • ➖ Harder for end-users to verify in-browser without registry tooling
  • ➖ Still needs an app-level surfacing mechanism if “observable without tooling” is a goal
2. Centralize tag computation in a reusable workflow/action
  • ➕ Reduces duplication between blog and API jobs
  • ➕ Easier to keep tagging rules consistent across images
  • ➖ More indirection for a relatively small workflow
  • ➖ Requires additional repo plumbing (composite action/reusable workflow versioning)

Recommendation: Current approach is a good fit: immutable version tags on release-tag pushes solve deploy pinning, and baking GIT_SHA into both services makes skew diagnosable without extra tooling. Consider OCI labels later if you want provenance that survives beyond app-level surfacing, but keep the DOM/API exposure for operational clarity.

Files changed (12) +4111 / -12

Enhancement (2) +15 / -1
index.tsExpose version and sha on /health +12/-1

Expose version and sha on /health

• Extends the /health response to include the API package version and the baked GIT_SHA env var, making the running build identifiable and skew detectable.

apps/self-hosted/hosting/api/src/index.ts

index.tsxExpose build sha on the root document element +3/-0

Expose build sha on the root document element

• Sets data-build on the document element from __BUILD_SHA__ so operators can verify the running SPA build without additional tooling.

apps/self-hosted/src/index.tsx

Bug fix (1) +15 / -3
apply-config-dom.tsClamp unknown style template ids to the default template +15/-3

Clamp unknown style template ids to the default template

• Updates the DOM config applier to validate configured style template ids against STYLE_TEMPLATES and fall back to DEFAULT_STYLE_TEMPLATE when unknown, preventing unstyled pages under config/image skew.

apps/self-hosted/src/core/apply-config-dom.ts

Tests (1) +16 / -0
apply-config-dom.test.tsTest template-id clamping to default +16/-0

Test template-id clamping to default

• Adds a regression test ensuring an unknown style template id is clamped to the roster default, matching component registry behavior during rollback/skew scenarios.

apps/self-hosted/src/core/apply-config-dom.test.ts

Documentation (2) +12 / -2
configuration-loader.tsDocument config version semantics as schema-major +8/-0

Document config version semantics as schema-major

• Documents the InstanceConfig.version field as a schema major with a no-bump-without-migration rule, clarifying how config compatibility should be managed.

apps/self-hosted/src/core/configuration-loader.ts

manifest.tsClarify tier semantics as reserved/unenforced +4/-2

Clarify tier semantics as reserved/unenforced

• Updates the ThemeManifest.tier documentation to reflect that tiers are currently reserved and not enforced, preserving schema stability for potential future use.

apps/self-hosted/src/themes/manifest.ts

Other (6) +4053 / -6
self-hosted.ymlAdd release-tag builds with version tags and pass GIT_SHA build args +30/-2

Add release-tag builds with version tags and pass GIT_SHA build args

• Workflow now triggers on self-hosted-v* tags and computes an additional immutable version tag alongside sha and channel tags. Both blog and API docker builds receive GIT_SHA as a build-arg and publish the version tag when present, while deploy remains gated separately.

.github/workflows/self-hosted.yml

DockerfilePlumb GIT_SHA into SPA build container +4/-1

Plumb GIT_SHA into SPA build container

• Adds a GIT_SHA build ARG/ENV so the self-hosted build can bake the commit sha into the produced bundle via rsbuild defines.

apps/self-hosted/Dockerfile

DockerfileMake hosting-api image reproducible with lockfile + npm ci; bake GIT_SHA +10/-3

Make hosting-api image reproducible with lockfile + npm ci; bake GIT_SHA

• Switches dependency installation to copy package-lock.json and run npm ci to prevent floating dependency changes. Adds a GIT_SHA ARG/ENV to the runtime image so the service can report build provenance.

apps/self-hosted/hosting/api/Dockerfile

package-lock.jsonCommit hosting-api npm lockfile for deterministic installs +3998/-0

Commit hosting-api npm lockfile for deterministic installs

• Introduces package-lock.json so container builds can be pinned to exact dependency versions when building from a given commit.

apps/self-hosted/hosting/api/package-lock.json

rsbuild.config.tsDefine __BUILD_SHA__ at build time from GIT_SHA +9/-0

Define BUILD_SHA at build time from GIT_SHA

• Adds an rsbuild source.define constant (__BUILD_SHA__) derived from process.env.GIT_SHA (fallbacking to unknown) so the frontend can surface build identity.

apps/self-hosted/rsbuild.config.ts

build-info.d.tsDeclare __BUILD_SHA__ global for TypeScript +2/-0

Declare BUILD_SHA global for TypeScript

• Adds a global type declaration for the build-time __BUILD_SHA__ constant injected by rsbuild.

apps/self-hosted/src/build-info.d.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@qodo-code-review

qodo-code-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. /health response untested fields 📘 Rule violation ▣ Testability
Description
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.
Code

apps/self-hosted/hosting/api/src/index.ts[R65-67]

+    version: apiVersion,
+    sha: process.env.GIT_SHA || 'unknown',
+  }),
Relevance

●●● Strong

Repo frequently accepts adding/updating API tests for functional changes; new /health fields likely
expected to be asserted.

PR-#1306
PR-#1439

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667972 requires tests for new/modified functional code paths. The PR changes the
/health payload to include new fields (version, sha) in the route handler.

Rule 2667972: Require tests for all new functional code paths
apps/self-hosted/hosting/api/src/index.ts[57-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


2. CI/Docker install mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The hosting API Dockerfile switched to npm ci (lockfile-strict), but the GitHub Actions test step
still runs npm install, which can tolerate/repair lockfile drift that npm ci would hard-fail on.
This can let CI pass while the Docker image build fails, or test a dependency state that isn’t the
one the image ships.
Code

apps/self-hosted/hosting/api/Dockerfile[R15-16]

+COPY package.json package-lock.json ./
+RUN npm ci
Relevance

●●● Strong

Team often accepts workflow reliability fixes; aligning CI install method prevents Docker build-only
failures.

PR-#702
PR-#1192

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Dockerfile now installs via npm ci, while the workflow’s hosting API tests still use `npm
install. Per npm’s documentation, npm ci exits with an error when package.json` and
package-lock.json are out of sync instead of updating the lockfile, so CI using npm install can
mask failures that will occur during image builds.

apps/self-hosted/hosting/api/Dockerfile[12-16]
.github/workflows/self-hosted.yml[92-95]
🌐 npm ci requires an existing lockfile and will exit with an error if dependencies in the lockfile do not match package.json, instead of updating the lockfile.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The hosting API Docker image build now uses `npm ci` (lockfile-strict), but CI tests still run `npm install`. This introduces a parity gap: `npm install` may succeed by modifying/repairing lock state that `npm ci` would reject, allowing CI to pass while the Docker build fails.

### Issue Context
- Dockerfile switched to `npm ci` for reproducible, locked installs.
- Workflow test step still uses `npm install`.

### Fix Focus Areas
- .github/workflows/self-hosted.yml[92-96]
- apps/self-hosted/hosting/api/Dockerfile[12-16]

### Suggested change
- Update the workflow step `cd apps/self-hosted/hosting/api && npm install && npm test` to `cd apps/self-hosted/hosting/api && npm ci && npm test`.
- (Optional) Add a post-step check that `package-lock.json` is unchanged (e.g., `git diff --exit-code package-lock.json`) to ensure CI never silently regenerates lock state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 75 rules
✅ Skills: 6 invoked
  add-feature
  add-query
  add-sdk-mutation
  add-test
  code-review
  debug
✅ Web pages:
  +17 more
Review mode: ⚖️ Balanced: This spans release workflows, Docker image reproducibility, API/SPA build metadata, and config fallback semantics, so it carries real cross-cutting risk; however, most edits are small or mechanical and do not clearly warrant redundant review passes.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +65 to +67
version: apiVersion,
sha: process.env.GIT_SHA || 'unknown',
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/self-hosted/hosting/api/Dockerfile
feruzm added 2 commits August 12, 2026 18:45
…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.
@feruzm
feruzm merged commit 3bde040 into develop Aug 12, 2026
12 checks passed
@feruzm
feruzm deleted the fix/self-hosted-versioning branch August 12, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit self-hosted release, image, config, and theme versioning

1 participant