Skip to main content

Build & Deployment

The Admin UI is a static single-page application (SPA) built with Vite and served via Nginx inside a Docker container.


Environment Variables

The UI supports three environment files out of the box:

FileUsed when
.env.developmentpnpm dev
.env.stagingpnpm build targeting staging
.env.productionpnpm build targeting production

Variables reference

VariableRequiredDescription
VITE_API_URLYesBackend API base URL (e.g. https://api.example.com)
VITE_APP_NAMENoBrowser tab title
VITE_APP_VERSIONNoSemantic version (injected by CI)
VITE_GIT_COMMIT_HASHNoGit commit SHA (injected by CI)
VITE_FIREBASE_API_KEYNo*Firebase push notifications
VITE_FIREBASE_AUTH_DOMAINNo*Firebase push notifications
VITE_FIREBASE_PROJECT_IDNo*Firebase push notifications
VITE_FIREBASE_STORAGE_BUCKETNo*Firebase push notifications
VITE_FIREBASE_MESSAGING_SENDER_IDNo*Firebase push notifications
VITE_FIREBASE_APP_IDNo*Firebase push notifications
VITE_FIREBASE_VAPID_KEYNo*Firebase push notifications

*Firebase variables are optional — push notifications are disabled if omitted.

caution

VITE_* variables are embedded into the compiled JS bundle at build time. Never put secrets (private keys, database credentials) in these variables.


Production Build

pnpm build

This runs TypeScript type-checking first (tsc --noEmit), then Vite compiles and bundles the app into the dist/ directory.

Preview the production build locally:

pnpm preview
# App available at http://localhost:4173

Build output

dist/
├── index.html
├── assets/
│ ├── index-[hash].js # Main bundle (code-split)
│ ├── index-[hash].css # Tailwind styles
│ └── ... # Lazy-loaded route chunks
└── favicon.ico

TanStack Router automatically splits each route into its own chunk, so only the code for the current page is loaded on first render.


Docker

The project ships a multistage Dockerfile:

Stage 1: node:20-alpine   →  Build the Vite app
Stage 2: nginx:alpine → Serve the built dist/

Build the image

docker build \
--build-arg VITE_API_URL=https://api.example.com \
--build-arg VITE_APP_VERSION=1.0.0 \
--build-arg VITE_GIT_COMMIT_HASH=$(git rev-parse --short HEAD) \
-t association-app-frontend:latest .

Run the container

docker run -p 80:80 \
-e API_BACKEND_URL=https://api.example.com \
association-app-frontend:latest

The API_BACKEND_URL runtime variable is injected into the Nginx configuration at container startup via envsubst. This allows the same Docker image to be deployed to different environments by changing only the environment variable — no rebuild required.

Docker entrypoint

On startup, docker-entrypoint.sh performs two steps before handing off to Nginx:

  1. Injects API_BACKEND_URL into the Nginx config template using envsubst
  2. Writes runtime-config.js containing VITE_APP_VERSION and VITE_GIT_COMMIT_HASH for in-app display

Nginx configuration

FeatureDetail
Port80
SPA routingtry_files $uri $uri/ /index.html (all unknown paths serve index.html)
API proxy/api/*$API_BACKEND_URL
Static asset cachemax-age=31536000 (1 year) for JS/CSS/images
GzipEnabled for HTML, JS, CSS, JSON, SVG
Health checkGET /health → 200 OK

CI/CD Pipeline

CI is handled by GitHub Actions (.github/workflows/docker-build.yml).

Triggers

EventEnvironment
Push to develop branchDevelopment
Push to staging branchStaging
Push to main branchProduction
Push a version tag (v*)Production
Manual dispatchSelectable

Pipeline steps

  1. Checkout source code
  2. Authenticate with Google Artifact Registry (australia-southeast2)
  3. Docker build with appropriate VITE_API_URL for the target environment
  4. Push image to association-app/frontend registry
  5. Semantic release (main branch only) — bumps version, generates CHANGELOG, creates GitHub release

Image tagging strategy

Branch / TriggerImage tag
developdevelop-{commit-sha}
stagingstaging-{commit-sha}
mainlatest, {semver}
Version tag v1.2.31.2.3, latest

Alternative Hosting Options

The dist/ output is a standard static site and can be hosted on any static hosting provider.

Netlify

A netlify.toml is included. Deploy by connecting the repository to Netlify and setting environment variables in the Netlify dashboard.

# netlify.toml (already in repo)
[[redirects]]
from = "/*"
to = "/index.html"
status = 200

Firebase Hosting

A firebase.json config is included for Firebase Hosting. Deploy with:

firebase deploy --only hosting

Other platforms (Vercel, Cloudflare Pages, etc.)

Set the build command to pnpm build and the publish directory to dist. Configure a catch-all redirect to index.html for SPA routing to work correctly.


Semantic Versioning & Releases

The project uses semantic-release for automated versioning on the main branch. Commit messages must follow Conventional Commits format:

Commit prefixVersion bump
fix:Patch (1.0.x)
feat:Minor (1.x.0)
feat!: or BREAKING CHANGE:Major (x.0.0)

On a successful main build, semantic-release will:

  1. Determine the next version from commit history
  2. Update CHANGELOG.md
  3. Create a git tag (e.g. v1.3.0)
  4. Publish a GitHub release with release notes