Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ WORKER_DATABASE_URL=postgresql://starter:replace-with-a-real-docker-secret@postg
MIGRATION_DATABASE_URL=postgresql://starter:replace-with-a-real-docker-secret@postgres:5432/business_app_starter?connection_limit=2

BASE_PATH=/webapp-template
APP_ENVIRONMENT=local
APP_VERSION=local
APP_REVISION=unknown
APP_BUILD_ID=docker-compose
APP_BUILT_AT=unknown
PORT=3270
AUTH_BASE_URL=http://localhost:3270/webapp-template
BETTER_AUTH_SECRET=replace-with-at-least-32-characters
Expand Down
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ PORT=3270
# App base path for local and reverse-proxied deployments.
BASE_PATH=

# Non-secret build/deployment identity displayed in the UI and /api/version.
# CI should set these from the commit, run id, and release/tag metadata.
APP_ENVIRONMENT=local
APP_VERSION=local
APP_REVISION=unknown
APP_BUILD_ID=local
APP_BUILT_AT=unknown

# Public origin used to build BetterAuth callback and post-login URLs.
AUTH_BASE_URL=http://localhost:3270

Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/deploy-azure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ jobs:
echo "migration_repository=$MIGRATION_IMAGE_REPOSITORY"
} >> "$GITHUB_OUTPUT"

- name: Export build metadata
shell: bash
env:
TARGET_ENVIRONMENT: ${{ inputs.environment }}
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail

if [[ "$REF_TYPE" == "tag" ]]; then
app_version="$REF_NAME"
else
app_version="$TARGET_ENVIRONMENT-$GITHUB_RUN_NUMBER"
fi

{
echo "TF_VAR_app_version=$app_version"
echo "TF_VAR_app_revision=$GITHUB_SHA"
echo "TF_VAR_app_build_id=$GITHUB_RUN_ID.$GITHUB_RUN_ATTEMPT"
echo "TF_VAR_app_built_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} >> "$GITHUB_ENV"

- name: Provision infrastructure
id: provision
shell: bash
Expand Down
40 changes: 23 additions & 17 deletions CONTINUE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,46 @@

## Current Snapshot

- Updated: 2026-06-11 11:06:34
- Branch: `020-deploy-smoke-verification`
- Updated: 2026-06-11 14:46:32
- Branch: `main`

## Recent Non-Continuity Commits

- 6c81729 feat: add azure deployment smoke verification (#3)
- 3d52264 fix: move state queue logging to dedicated resource
- 25306fd chore: refresh specs overview
- dd226de test: update opentofu action pin assertion
- 9b92cb5 ci: update opentofu setup action
- fdf3418 chore: close completed active specs

## Git Status

- M .env.docker.example
- M .env.example
- M .github/workflows/deploy-azure.yml
- M .specify/feature.json
- M ACTIVE_SPECS.md
- M AGENTS.md
- M package.json
- M Dockerfile.app
- M Dockerfile.worker
- M README.md
- M docker-compose.yml
- M infra/azure/main.tf
- M infra/azure/modules/runtime/app.tf
- M infra/azure/modules/runtime/job.tf
- M infra/azure/modules/runtime/variables.tf
- M infra/azure/modules/runtime/worker.tf
- M infra/azure/variables.tf
- M specs/018-opentofu-azure-infra/quickstart.md
- M specs/OVERVIEW.md
- M src/components/ui/AppVersionBadge.tsx
- M src/lib/app-version.ts
- M tests/unit/security/deploy-workflow.test.ts
- ?? docs/azure-deploy-smoke.md
- ?? scripts/azure-deploy-smoke.ts
- ?? scripts/run-azure-deploy-smoke.mjs
- ?? specs/020-deploy-smoke-verification/
- ?? tests/integration/azure-deploy-smoke-cli.test.ts
- ?? tests/unit/azure-deploy-smoke.test.ts
- ?? src/app/api/version/
- ?? tests/unit/app-version.test.ts
- ?? tests/unit/version-route.test.ts

## Active Specs

- None

## Next Recommended Actions

1. Commit and push `020-deploy-smoke-verification`.
2. Open a pull request for the deployment smoke verification feature.
3. Confirm GitHub Actions validation, then merge if green.
1. Review, commit, and push the runtime build metadata changes.
2. Optionally open a PR and confirm GitHub Actions validation.
3. Use `APP_ENVIRONMENT`, `APP_VERSION`, `APP_REVISION`, `APP_BUILD_ID`, and `APP_BUILT_AT` for dev/staging traceability instead of generated version files.
8 changes: 8 additions & 0 deletions CONTINUE_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1509,3 +1509,11 @@
- Validation passed: focused smoke/workflow tests and `.\validate.ps1 all`.
- Active specs: none.
- Next focus: commit/push the feature branch, open a PR, and confirm GitHub Actions validation.

## 2026-06-11 14:46:32

- Added env-driven build/deployment metadata for app version traceability without committing generated version files.
- Added `/api/version`, updated the UI version badge, Docker image labels/build args, Docker Compose env, Azure runtime env, and deploy workflow metadata export.
- Validation passed: focused version/workflow tests, `pnpm run typecheck`, and `.\validate.ps1 all`.
- Active specs: none.
- Next focus: review, commit, push, and optionally open a PR for the metadata changes.
28 changes: 28 additions & 0 deletions Dockerfile.app
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ RUN pnpm install --prod
FROM base AS builder
WORKDIR /app
ARG BASE_PATH
ARG APP_VERSION
ARG APP_REVISION
ARG APP_BUILD_ID
ARG APP_BUILT_AT
ENV BASE_PATH=$BASE_PATH
ENV APP_VERSION=$APP_VERSION
ENV APP_REVISION=$APP_REVISION
ENV APP_BUILD_ID=$APP_BUILD_ID
ENV APP_BUILT_AT=$APP_BUILT_AT
ENV AUTH_BASE_URL=http://localhost:3270
ENV BETTER_AUTH_SECRET=docker-build-secret-change-me-at-least-32-characters
ENV APP_DATABASE_URL=postgresql://starter:starter@localhost:5432/business_app_starter
Expand All @@ -33,6 +41,14 @@ RUN pnpm exec prisma generate --config prisma.config.postgres.ts && pnpm run bui

FROM base AS migrate-runner
WORKDIR /app
ARG APP_VERSION
ARG APP_REVISION
ARG APP_BUILD_ID
ARG APP_BUILT_AT
LABEL org.opencontainers.image.version=$APP_VERSION
LABEL org.opencontainers.image.revision=$APP_REVISION
LABEL org.opencontainers.image.created=$APP_BUILT_AT
LABEL org.opencontainers.image.source="https://github.com/TKlerx/webapp-template"
COPY --from=migrate-deps /migrate-runtime/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/prisma ./prisma
Expand All @@ -42,9 +58,21 @@ COPY --from=builder /app/scripts ./scripts

FROM base AS runner
WORKDIR /app
ARG APP_VERSION
ARG APP_REVISION
ARG APP_BUILD_ID
ARG APP_BUILT_AT
LABEL org.opencontainers.image.version=$APP_VERSION
LABEL org.opencontainers.image.revision=$APP_REVISION
LABEL org.opencontainers.image.created=$APP_BUILT_AT
LABEL org.opencontainers.image.source="https://github.com/TKlerx/webapp-template"
ENV NODE_ENV=production
ENV PORT=3270
ENV HOSTNAME=0.0.0.0
ENV APP_VERSION=$APP_VERSION
ENV APP_REVISION=$APP_REVISION
ENV APP_BUILD_ID=$APP_BUILD_ID
ENV APP_BUILT_AT=$APP_BUILT_AT
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/generated ./generated
Expand Down
12 changes: 12 additions & 0 deletions Dockerfile.worker
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
FROM python:3.12-slim

WORKDIR /worker
ARG APP_VERSION
ARG APP_REVISION
ARG APP_BUILD_ID
ARG APP_BUILT_AT
LABEL org.opencontainers.image.version=$APP_VERSION
LABEL org.opencontainers.image.revision=$APP_REVISION
LABEL org.opencontainers.image.created=$APP_BUILT_AT
LABEL org.opencontainers.image.source="https://github.com/TKlerx/webapp-template"
ENV APP_VERSION=$APP_VERSION
ENV APP_REVISION=$APP_REVISION
ENV APP_BUILD_ID=$APP_BUILD_ID
ENV APP_BUILT_AT=$APP_BUILT_AT

RUN apt-get update -y \
&& apt-get upgrade -y \
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ That guide covers:
- Structured JSON logs are emitted through `src/lib/logger.ts` and redact common secrets automatically. See [`docs/logging.md`](./docs/logging.md) for event naming, safe metadata, worker logging, and guardrail rules.
- Runtime process failures are captured in `src/instrumentation.ts`.
- Health checks are exposed at `/api/health` with process and database status.
- Build metadata is exposed at `/api/version` and shown in the app badge from `APP_ENVIRONMENT`, `APP_VERSION`, `APP_REVISION`, `APP_BUILD_ID`, and `APP_BUILT_AT`; CI/deploys should set these from the commit, run id, and release/tag instead of committing generated version files.
- `LOG_LEVEL` controls severity filtering. `ENABLE_REQUEST_LOGGING=true` enables opt-in request completion logs.

## Dependency Safety
Expand Down
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ x-app-image: &app-image
target: runner
args:
BASE_PATH: ${BASE_PATH}
APP_VERSION: ${APP_VERSION:-local}
APP_REVISION: ${APP_REVISION:-unknown}
APP_BUILD_ID: ${APP_BUILD_ID:-docker-compose}
APP_BUILT_AT: ${APP_BUILT_AT:-unknown}

x-migrate-image: &migrate-image
image: ${MIGRATE_IMAGE_NAME:-business-app-starter-migrate}:latest
Expand All @@ -15,9 +19,18 @@ x-migrate-image: &migrate-image
target: migrate-runner
args:
BASE_PATH: ${BASE_PATH}
APP_VERSION: ${APP_VERSION:-local}
APP_REVISION: ${APP_REVISION:-unknown}
APP_BUILD_ID: ${APP_BUILD_ID:-docker-compose}
APP_BUILT_AT: ${APP_BUILT_AT:-unknown}

x-common-env: &common-env
AUTH_BASE_URL: ${AUTH_BASE_URL:-http://localhost:3270}
APP_BUILD_ID: ${APP_BUILD_ID:-docker-compose}
APP_BUILT_AT: ${APP_BUILT_AT:-unknown}
APP_ENVIRONMENT: ${APP_ENVIRONMENT:-local}
APP_REVISION: ${APP_REVISION:-unknown}
APP_VERSION: ${APP_VERSION:-local}
BASE_PATH: ${BASE_PATH:-}
ENABLE_REQUEST_LOGGING: ${ENABLE_REQUEST_LOGGING:-true}
LOG_LEVEL: ${LOG_LEVEL:-info}
Expand Down Expand Up @@ -139,6 +152,11 @@ services:
build:
context: .
dockerfile: Dockerfile.worker
args:
APP_VERSION: ${APP_VERSION:-local}
APP_REVISION: ${APP_REVISION:-unknown}
APP_BUILD_ID: ${APP_BUILD_ID:-docker-compose}
APP_BUILT_AT: ${APP_BUILT_AT:-unknown}
environment:
<<: *worker-env
networks: [internal]
Expand Down
5 changes: 5 additions & 0 deletions infra/azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ module "runtime" {
app_min_replicas = var.app_min_replicas
app_max_replicas = var.app_max_replicas
worker_min_replicas = var.worker_min_replicas
app_environment = var.environment
app_version = var.app_version
app_revision = var.app_revision
app_build_id = var.app_build_id
app_built_at = var.app_built_at
base_path = var.base_path
custom_domain = var.custom_domain
enable_mail = var.enable_mail
Expand Down
25 changes: 25 additions & 0 deletions infra/azure/modules/runtime/app.tf
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,31 @@ resource "azurerm_container_app" "app" {
value = var.base_path
}

env {
name = "APP_ENVIRONMENT"
value = var.app_environment
}

env {
name = "APP_VERSION"
value = var.app_version
}

env {
name = "APP_REVISION"
value = var.app_revision
}

env {
name = "APP_BUILD_ID"
value = var.app_build_id
}

env {
name = "APP_BUILT_AT"
value = var.app_built_at
}

env {
name = "AUTH_BASE_URL"
value = local.auth_base_url
Expand Down
25 changes: 25 additions & 0 deletions infra/azure/modules/runtime/job.tf
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ resource "azurerm_container_app_job" "migration" {
value = "prisma.config.postgres.ts"
}

env {
name = "APP_ENVIRONMENT"
value = var.app_environment
}

env {
name = "APP_VERSION"
value = var.app_version
}

env {
name = "APP_REVISION"
value = var.app_revision
}

env {
name = "APP_BUILD_ID"
value = var.app_build_id
}

env {
name = "APP_BUILT_AT"
value = var.app_built_at
}

env {
name = "DATABASE_URL"
secret_name = "migration-database-url"
Expand Down
25 changes: 25 additions & 0 deletions infra/azure/modules/runtime/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ variable "worker_min_replicas" {
type = number
}

variable "app_environment" {
description = "Non-secret application environment label."
type = string
}

variable "app_version" {
description = "Non-secret application version label."
type = string
}

variable "app_revision" {
description = "Non-secret source revision."
type = string
}

variable "app_build_id" {
description = "Non-secret CI/build identifier."
type = string
}

variable "app_built_at" {
description = "Non-secret build timestamp."
type = string
}

variable "base_path" {
description = "Application base path."
type = string
Expand Down
25 changes: 25 additions & 0 deletions infra/azure/modules/runtime/worker.tf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ resource "azurerm_container_app" "worker" {
value = var.base_path
}

env {
name = "APP_ENVIRONMENT"
value = var.app_environment
}

env {
name = "APP_VERSION"
value = var.app_version
}

env {
name = "APP_REVISION"
value = var.app_revision
}

env {
name = "APP_BUILD_ID"
value = var.app_build_id
}

env {
name = "APP_BUILT_AT"
value = var.app_built_at
}

env {
name = "WORKER_DATABASE_URL"
secret_name = "worker-database-url"
Expand Down
Loading