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
9 changes: 8 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
!.prettierrc.json
!.prod
!.release-please-manifest.json
!.env.example
!.env.runtime.template
!.env.playwright.local.example
!.env.development.local.example
!codegen.yml
!commitlint.config.js
!eslint.config.mjs
!index.html
!package.json
!pnpm-lock.yaml
!pnpm-workspace.yaml
!printVersion.js
!public
!release-please-config.json
!scripts
!scripts/**
!src
!tsconfig.json
!tsconfig.node.json
!vite.config.ts
!vitest.config.ts
!yarn.lock
9 changes: 3 additions & 6 deletions .env.development.local.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# Local environment variable overrides:
# See https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used
# Local overrides for Vite development mode.
#
# Files on the left have more priority than files on the right:
# - npm start: .env.development.local, .env.local, .env.development, .env
# - npm run build: .env.production.local, .env.local, .env.production, .env
# - npm test: .env.test.local, .env.test, .env (note .env.local is missing)
# File naming follows Vite conventions. This file is intended for local-only
# development overrides and should not be committed.

# Endpoint URI overrides for the fully local development environment..
# When running a local API, then also the LinkedEvents needs to be local.
Expand Down
20 changes: 12 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Default environment variables
# See https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used
# Default environment variables for Vite-based tooling.
#
# Files on the left have more priority than files on the right:
# - npm start: .env.development.local, .env.local, .env.development, .env
# - npm run build: .env.production.local, .env.local, .env.production, .env
# - npm test: .env.test.local, .env.test, .env (note .env.local is missing)
# Vite file priority (highest to lowest):
# 1) Existing process env
# 2) .env.[mode].local
# 3) .env.[mode]
# 4) .env.local
# 5) .env
#
# In this project:
# - pnpm dev uses mode "development"
# - pnpm build uses mode "production"
# - pnpm test:coverage loads .env and .env.test in src/test/vitest-setup.ts
VITE_APP_ORIGIN=http://localhost:3000
# Staging CMS URI:
VITE_APP_CMS_URI=https://kultus.app-staging.hkih.hion.dev/graphql
# Production CMS URI:
# VITE_APP_CMS_URI=https://kultus.content.api.hel.fi/graphql
VITE_APP_APPLICATION_NAME=$npm_package_name
VITE_APP_VERSION=$npm_package_version

# Sentry
VITE_APP_SENTRY_ENVIRONMENT=local
Expand Down
27 changes: 27 additions & 0 deletions .env.runtime.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
VITE_APP_ORIGIN=
VITE_APP_CMS_URI=
VITE_APP_SENTRY_ENVIRONMENT=
VITE_APP_SENTRY_DSN=
VITE_APP_SENTRY_TRACES_SAMPLE_RATE=
VITE_APP_SENTRY_TRACE_PROPAGATION_TARGETS=
VITE_APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE=
VITE_APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE=
VITE_APP_CSP_REPORT_URI=
VITE_APP_API_URI=
VITE_APP_API_REPORT_URI=
VITE_APP_LINKEDEVENTS_API_URI=
VITE_APP_OIDC_AUDIENCES=
VITE_APP_OIDC_RETURN_TYPE=
VITE_APP_OIDC_SERVER_TYPE=
VITE_APP_OIDC_API_CLIENT_ID=
VITE_APP_OIDC_AUTHORITY=
VITE_APP_OIDC_CLIENT_ID=
VITE_APP_OIDC_SCOPE=
VITE_APP_OIDC_AUTOMATIC_SILENT_RENEW_ENABLED=
VITE_APP_HELSINKI_PROFILE_URL=
VITE_APP_IDLE_TIMEOUT_IN_MS=
VITE_APP_APPLICATION_NAME=
VITE_APP_VERSION=
VITE_APP_COMMITHASH=
VITE_APP_BUILDTIME=
VITE_APP_SENTRY_RELEASE=
36 changes: 36 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Test environment variables (used by Vitest + update-runtime-env in test mode)

VITE_APP_ORIGIN=http://localhost:3000
VITE_APP_CMS_URI=https://kultus.app-staging.hkih.hion.dev/graphql
VITE_APP_APPLICATION_NAME=$npm_package_name
VITE_APP_VERSION=$npm_package_version

# Sentry
VITE_APP_SENTRY_ENVIRONMENT=test
VITE_APP_SENTRY_DSN=
VITE_APP_SENTRY_TRACES_SAMPLE_RATE=1.0
VITE_APP_SENTRY_TRACE_PROPAGATION_TARGETS=http://localhost:8081/graphql
VITE_APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE=0
VITE_APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE=0
VITE_APP_CSP_REPORT_URI=

# API / backend services
VITE_APP_API_URI=https://kultus.api.test.hel.ninja/graphql
VITE_APP_API_REPORT_URI=https://kultus.api.test.hel.ninja/reports
VITE_APP_LINKEDEVENTS_API_URI=https://linkedevents.api.test.hel.ninja/v1

# OIDC client configuration
VITE_APP_OIDC_AUDIENCES=kultus-api-test,profile-api-test
VITE_APP_OIDC_RETURN_TYPE=code
VITE_APP_OIDC_SERVER_TYPE=KEYCLOAK
VITE_APP_OIDC_API_CLIENT_ID=kultus-api-test
VITE_APP_OIDC_AUTHORITY=https://tunnistus.test.hel.ninja/auth/realms/helsinki-tunnistus
VITE_APP_OIDC_CLIENT_ID=kultus-admin-ui-test
VITE_APP_OIDC_SCOPE="openid profile email"
VITE_APP_OIDC_AUTOMATIC_SILENT_RENEW_ENABLED=1

# Helsinki profile UI URI (for link)
VITE_APP_HELSINKI_PROFILE_URL=https://profiili.test.hel.ninja/loginsso

# Client session timeout in ms
VITE_APP_IDLE_TIMEOUT_IN_MS=3600000
13 changes: 6 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ name: CI

on:
push:
branches: [ main, master ]
branches: [main, master]
pull_request:
branches: [ main, master ]
branches: [main, master]
workflow_dispatch:

jobs:
common:
uses: City-of-Helsinki/.github/.github/workflows/ci-node.yml@main
secrets: inherit
uses: City-of-Helsinki/.github/.github/workflows/ci-pnpm-node.yml@main
Comment thread
tjgofore marked this conversation as resolved.
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
node-version: 22
extra-commands: |
cp .env.example .env
node-version: 24.x
Comment thread
tjgofore marked this conversation as resolved.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ sonar-report.*

# production
/build
/public/env-config.js
/public/test-env-config.js

# misc
.DS_Store
Expand All @@ -24,7 +26,6 @@ sonar-report.*
.env.playwright.local
.env.production
.env.production.local
.env.test
.env.test.local

npm-debug.log*
Expand Down
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
yarn doctoc . -u
yarn lint-staged --relative
pnpm doctoc . -u
pnpm lint-staged --relative
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v22.13.1
v24.15.0
171 changes: 51 additions & 120 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,41 +1,31 @@
# ===============================================
FROM registry.access.redhat.com/ubi9/nodejs-22 AS appbase
# ===============================================

# install yarn
USER root
RUN curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo
RUN yum -y install yarn

# ============================================================
# STAGE 1: Build the Static Assets
# ============================================================
FROM helsinki.azurecr.io/ubi9/nodejs-24-pnpm-builder-base AS appbase
WORKDIR /app

# Offical image has npm log verbosity as info. More info - https://github.com/nodejs/docker-node#verbosity
ENV NPM_CONFIG_LOGLEVEL warn

# set our node environment, either development or production
# defaults to production, compose overrides this to development on build and run
ARG NODE_ENV=${NODE_ENV:-"production"}
ENV NODE_ENV $NODE_ENV

# Global npm deps in a non-root user directory
ENV NPM_CONFIG_PREFIX=/app/.npm-global
ENV PATH=$PATH:/app/.npm-global/bin

# Yarn
ENV YARN_VERSION 1.22.22
RUN yarn policies set-version ${YARN_VERSION}
# 1. Copy needed files for build
COPY --chown=default:root package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json tsconfig.node.json ./
COPY --chown=default:root ./public ./public
COPY --chown=default:root ./scripts ./scripts
COPY --chown=default:root .env.runtime.template ./

# Copy package.json and package-lock.json/yarn.lock files
COPY --chown=default:root package*.json *yarn* ./
# 2. Run the install
# corepack in the base image will automatically use the version of pnpm
# defined in your package.json 'packageManager' field if present.
RUN pnpm install --frozen-lockfile --ignore-scripts && pnpm store prune
RUN pnpm update-runtime-env
Comment thread
sentry[bot] marked this conversation as resolved.

# Install npm dependencies
ENV PATH /app/node_modules/.bin:$PATH
# 3. Copy remaining source files
COPY --chown=default:root index.html vite.config.ts eslint.config.mjs .prettierrc.json .env* ./
Comment thread
tjgofore marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
COPY --chown=default:root ./src ./src

RUN yarn install --frozen-lockfile --ignore-scripts && yarn cache clean --force

# =============================
# ============================================================
# STAGE 2: Development
# ============================================================
FROM appbase AS development
# =============================
WORKDIR /app

# Set NODE_ENV to development in the development container
ARG NODE_ENV=development
Expand All @@ -48,99 +38,40 @@ ENV NODE_ENV $NODE_ENV
ARG CHOKIDAR_USEPOLLING=true
ENV CHOKIDAR_USEPOLLING=${CHOKIDAR_USEPOLLING}

# copy in our source code last, as it changes the most
COPY --chown=default:root . .
# Expose port and start development server
EXPOSE 8080
CMD pnpm exec vite --port 8080 --no-open --host

# Bake package.json start command into the image
CMD ["yarn", "dev", "--no-open", "--host"]

Comment thread
sentry[bot] marked this conversation as resolved.
# ===================================
# ============================================================
# STAGE 3: Static builder for production
# ============================================================
Comment thread
sentry[bot] marked this conversation as resolved.
FROM appbase AS staticbuilder
# ===================================

# Oidc authority
ARG VITE_APP_OIDC_AUDIENCES
ARG VITE_APP_OIDC_RETURN_TYPE
ARG VITE_APP_OIDC_SERVER_TYPE
ARG VITE_APP_OIDC_API_CLIENT_ID
ARG VITE_APP_OIDC_AUTHORITY
ARG VITE_APP_OIDC_CLIENT_ID
ARG VITE_APP_OIDC_SCOPE
ARG VITE_APP_OIDC_AUTOMATIC_SILENT_RENEW_ENABLED

# Sentry variables
ARG VITE_APP_SENTRY_ENVIRONMENT
ARG VITE_APP_SENTRY_DSN
ARG VITE_APP_SENTRY_TRACES_SAMPLE_RATE
ARG VITE_APP_SENTRY_TRACE_PROPAGATION_TARGETS
ARG VITE_APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE
ARG VITE_APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE
ARG VITE_APP_CSP_REPORT_URI

# Api url
ARG VITE_APP_API_URI
ARG VITE_APP_CMS_URI
ARG VITE_APP_API_REPORT_URI

# Linkedevents api url
ARG VITE_APP_LINKEDEVENTS_API_URI

# Application's origin (i.e. where this application is hosted)
ARG VITE_APP_ORIGIN

# Helsinki profile URL
ARG VITE_APP_HELSINKI_PROFILE_URL

# Time before user logout if idle
ARG VITE_APP_IDLE_TIMEOUT_IN_MS

ARG NODE_OPTIONS

# Fix FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed -
# JavaScript heap out of memory: https://github.com/vitejs/vite/issues/2433.
ENV NODE_OPTIONS=${NODE_OPTIONS}

# Release information
ARG VITE_APP_SENTRY_RELEASE
ARG VITE_APP_COMMITHASH
ARG VITE_APP_BUILDTIME

# Use template and inject the environment variables into .prod/nginx.conf
ENV VITE_APP_BUILDTIME=${VITE_APP_BUILDTIME:-""}
ENV VITE_APP_RELEASE=${VITE_APP_SENTRY_RELEASE:-""}
ENV VITE_APP_COMMITHASH=${VITE_APP_COMMITHASH:-""}
COPY .prod/nginx.conf.template /tmp/.prod/nginx.conf.template
RUN export APP_VERSION=$(yarn --silent app:version) && \
envsubst '${APP_VERSION},${VITE_APP_BUILDTIME},${VITE_APP_RELEASE},${VITE_APP_COMMITHASH}' < \
"/tmp/.prod/nginx.conf.template" > \
"/tmp/.prod/nginx.conf"

# Copy all files
COPY . /app

# Build
RUN yarn build

# =============================
FROM registry.access.redhat.com/ubi9/nginx-122 AS production
# =============================
# Add application sources to a directory that the assemble script expects them
# and set permissions so that the container runs without root access
ARG PORT

USER root

RUN chgrp -R 0 /usr/share/nginx/html && \
chmod -R g=u /usr/share/nginx/html

# Copy static build

# Perform the build
ARG VITE_SENTRY_RELEASE
RUN pnpm build


# ============================================================
# STAGE 4: Production Runtime
# ============================================================
FROM helsinki.azurecr.io/ubi10/nginx-126-spa-standard AS production

ARG VITE_SENTRY_RELEASE
ENV APP_RELEASE=${VITE_SENTRY_RELEASE:-""}
# 1. Copy the compiled assets
COPY --from=staticbuilder /app/build /usr/share/nginx/html
# Copy nginx config
COPY --from=staticbuilder /tmp/.prod/nginx.conf /etc/nginx/nginx.conf

USER 1001
# 2. Setup Runtime Env Injection
# env.sh is provided by the base image
WORKDIR /usr/share/nginx/html
COPY --from=staticbuilder /app/.env.runtime.template ./.env
Comment thread
sentry[bot] marked this conversation as resolved.

# Run script uses standard ways to run the application
CMD ["/bin/bash", "-c", "nginx -g \"daemon off;\""]
# 3. Inject Versioning for the /readiness endpoint from package.json using base image
COPY package.json .

EXPOSE ${PORT:-8080}
# - env.sh (Inherited from base image at /usr/share/nginx/html/env.sh)
# - USER 1001 (Inherited from base image)
# - EXPOSE 8080 (Inherited from base image)
# - ENTRYPOINT/CMD (Inherited from base image)
Comment thread
sentry[bot] marked this conversation as resolved.
Loading
Loading