Skip to content

feat(health): expose running app version metadata#4757

Open
byigitt wants to merge 2 commits into
simstudioai:mainfrom
byigitt:byigitt/issue-2014-health-version
Open

feat(health): expose running app version metadata#4757
byigitt wants to merge 2 commits into
simstudioai:mainfrom
byigitt:byigitt/issue-2014-health-version

Conversation

@byigitt
Copy link
Copy Markdown

@byigitt byigitt commented May 27, 2026

This adds version and commit metadata to /api/health so self-hosted instances can report what is running.

Testing:

  • bun run test -- app/api/health/route.test.ts
  • bun run type-check
  • bun run check:api-validation

Closes #2014

@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

@byigitt is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@cursor
Copy link
Copy Markdown

cursor Bot commented May 27, 2026

PR Summary

Low Risk
Additive health JSON fields and optional env/build metadata; no auth or data-path changes.

Overview
/api/health now returns version and commit alongside status and timestamp, so self-hosted and container deployments can see what build is running. Version comes from APP_VERSION / NEXT_PUBLIC_APP_VERSION, then falls back to package.json; commit comes from GIT_SHA and common CI env aliases, or null.

The route is wired through the shared API contract layer (passthrough query params for probes like ?_=123) and has expanded tests.

Docker/CI: docker/app.Dockerfile accepts APP_VERSION and GIT_SHA build args and sets them at runtime; ci.yml and images.yml pass those args on image builds (dev uses APP_VERSION=dev; main/staging use detected release version or ref name).

Docs: README documents curl against health; .env.example documents optional APP_VERSION / GIT_SHA.

Reviewed by Cursor Bugbot for commit c1f7e4d. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 27, 2026

Greptile Summary

This PR exposes version and commit fields on the /api/health endpoint so self-hosted instances can report what build is running, wiring the values from APP_VERSION/GIT_SHA environment variables (with fallbacks to NEXT_PUBLIC_APP_VERSION, VERCEL_GIT_COMMIT_SHA, COMMIT_SHA, and the package.json version). CI workflows and the Dockerfile are updated to inject these as build-args.

  • Health route now calls parseRequest with a strict noInputSchema, which returns 400 for any request that includes unexpected query parameters — a behavioral regression for a health endpoint that previously returned 200 unconditionally.
  • Tests cover the version/commit metadata paths but hardcode the fallback version as '0.1.0', which will silently break when the package version is bumped.
  • Docker and CI changes are clean and correctly scoped to the runner stage.

Confidence Score: 3/5

The core metadata plumbing (env vars, Dockerfile, CI) is solid, but the health endpoint now rejects requests with any query parameter, which could cause real health probes to report the service as unhealthy.

The health route wraps its response in parseRequest with a strict noInputSchema. Any monitoring tool, load balancer probe, or cache-busting request that appends even a single query parameter will receive a 400 instead of the expected 200, silently marking the instance unhealthy. The previous handler returned 200 unconditionally. This regression is confined to one file and has a straightforward fix, but it affects the reliability of deployment health checks in production.

apps/sim/app/api/health/route.ts — the strict query validation wrapping a health endpoint that should always respond 200.

Important Files Changed

Filename Overview
apps/sim/app/api/health/route.ts Adds version/commit metadata to the health response; introduces a strict-query parseRequest that can reject health probes with unexpected query parameters, breaking the unconditional-200 contract health endpoints must provide.
apps/sim/lib/api/contracts/health.ts New contract file; response schema and type look correct; noInputSchema (strict) for query is the root cause of the health-probe regression flagged in route.ts.
apps/sim/app/api/health/route.test.ts Tests cover version/commit metadata paths; fallback-version assertion hardcodes '0.1.0' which will drift when package.json version changes; env cleanup in afterEach is correct.
docker/app.Dockerfile Correctly declares ARG and ENV for APP_VERSION/GIT_SHA in the runner stage; defaults are sensible.
.github/workflows/ci.yml Passes APP_VERSION and GIT_SHA as build-args to all three Docker build steps; fallback to github.ref_name is reasonable for non-semver builds.
.github/workflows/images.yml Mirrors ci.yml build-arg changes; looks correct.
apps/sim/lib/api/contracts/index.ts Adds health contract to barrel export; alphabetically ordered correctly.
apps/sim/.env.example Documents APP_VERSION and GIT_SHA as optional environment variables with useful comments.

Sequence Diagram

sequenceDiagram
    participant Probe as Health Probe / curl
    participant Route as GET /api/health
    participant PR as parseRequest (noInputSchema strict)
    participant Env as process.env

    Probe->>Route: GET /api/health
    Route->>PR: "parseRequest(healthContract, request, {})"
    alt No query params
        PR-->>Route: "{ success: true }"
        Route->>Env: APP_VERSION, GIT_SHA etc.
        Env-->>Route: version, commit
        Route-->>Probe: "200 { status, timestamp, version, commit }"
    else "Any unexpected query param (e.g. ?_=ts)"
        PR-->>Route: "{ success: false, response: 400 }"
        Route-->>Probe: 400 Validation error
    end
Loading

Reviews (1): Last reviewed commit: "feat(health): expose running app version..." | Re-trigger Greptile

Comment on lines +21 to +22
const parsed = await parseRequest(healthContract, request, {})
if (!parsed.success) return parsed.response
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Strict query validation can cause health probes to return 400

healthContract uses noInputSchema which is z.object({}).strict(). Any request carrying unexpected query parameters — e.g. ?_=1234567890 (cache-busting), ?format=json (monitoring tools), or any debugging param — will fail Zod strict validation and return a 400 before the health payload is produced. The previous handler accepted no request argument at all and would return 200 unconditionally. Kubernetes liveness/readiness probes, uptime checkers, and other health-poll tools sometimes append arbitrary params; this change silently turns those into "unhealthy" signals.

Note also that parsed.data is never read inside the handler — the contract's query validation is pure overhead here. Consider removing the parseRequest call entirely, or using a passthrough/permissive query schema, to preserve the unconditional 200 behaviour that health endpoints are expected to provide.

Comment thread apps/sim/app/api/health/route.test.ts Outdated
Comment on lines +45 to +46
version: '0.1.0',
commit: null,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The fallback version is hardcoded as '0.1.0' in the test, but it is derived at runtime from appPackage.version in the route. If the package.json version is ever bumped, this assertion will fail without an obvious cause. Importing the package version directly makes the assertion resilient to version changes.

Suggested change
version: '0.1.0',
commit: null,
version: expect.any(String),
commit: null,

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.

Finding out what the version of a running SIM instance

1 participant