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
14 changes: 12 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ jobs:
node-version: 24
cache: npm

- name: Install dependencies
run: npm ci
# The repo .npmrc is credential-less (the K8s image build authenticates
# via ADO npmAuthenticate); GH-hosted builds inject creds here instead.
- name: Authenticate to codat-npm feed
run: |
{
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:username=codat"
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:_password=${NPM_TOKEN}"
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:email=npm-requires-email@example.com"
} >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install dependencies
run: npm ci

- name: Build site
run: npm run build

Expand Down
12 changes: 10 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ jobs:

# The ADO registry in .npmrc requires auth even to install, so PR
# builds need NPM_TOKEN too (fork PRs don't get secrets and will fail)
- name: Install dependencies
run: npm ci
- name: Authenticate to codat-npm feed
run: |
{
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:username=codat"
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:_password=${NPM_TOKEN}"
echo "//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:email=npm-requires-email@example.com"
} >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install dependencies
run: npm ci

- name: Build site
run: npm run build
3 changes: 0 additions & 3 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
registry=https://pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/
save-exact=true
//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:username=codat
//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:_password=${NPM_TOKEN}
//pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/:email=npm-requires-email@example.com
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM nginx:1.29.6-alpine-slim AS final

COPY --from=build /src/build /usr/share/nginx/html
COPY --from=build /src/nginx.conf /etc/nginx/nginx.conf

RUN rm /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
68 changes: 68 additions & 0 deletions deployment/build.azure-pipelines.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: $(Date:yyyy.M.dd).$(Rev:r)

trigger:
branches:
include:
- 'main'
paths:
exclude: []

pr:
branches:
include:
- 'main'

resources:
repositories:
- repository: 'templates'
type: 'git'
name: 'Codat/YAMLBuildTemplates'

# Array values as Variables not supported by ADO
parameters:
- name: ImagesToPublish
type: object
default:
- 'codat.legal.ui'

- name: DeploymentEnvironments
type: object
default:
- name: integration
shortCode: intg
pool:
name: codat-intg-managed-devops-pool-linux
demands:
- CustomCapabilities -equals $(Build.Repository.Name)
registryName: codatintgregistry

- name: production
shortCode: prod
pool: codat-prod-managed-devops-pool-linux
registryName: codatprodregistry

stages:
- stage: 'build'
pool:
name: 'codat-intg-managed-devops-pool-linux'
demands:
- CustomCapabilities -equals $(Build.Repository.Name)
jobs:
- template: Helm/build.frontend.job.yaml@templates
parameters:
ImagesToPublish: ${{ parameters.ImagesToPublish }}
Version: '$(Build.BuildNumber)'
BuildFolder: deployment

- ${{ if not(in( variables['Build.Reason'], 'PullRequest' )) }}:
- ${{ each env in parameters.DeploymentEnvironments }}:
- ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(env.name, 'production')) }}:
- stage: ${{ env.name }}
pool: ${{ env.pool }}
jobs:
- template: Helm/deploy.job.yaml@templates
parameters:
Environment: ${{ env.name }}
RegistryName: ${{ env.registryName }}
ImagesToPublish: ${{ parameters.ImagesToPublish }}
ArgoApplicationName: legal
8 changes: 8 additions & 0 deletions deployment/charts/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: 'v2'
name: 'codat.legal'
type: 'application'
version: '0.0.1' # this is overwritten by the pipeline
dependencies:
- name: 'codat-classic-app'
version: '>=2.6.1'
repository: 'oci://codatintgregistry.azurecr.io/base-charts'
19 changes: 19 additions & 0 deletions deployment/charts/values-intg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
codat-classic-app:
components:
- name: ui
image: codatintgregistry.azurecr.io/codat.legal.ui
routing:
- hosts: ["legal-integration.codat.io"]
containerPort: 80
statusPath: "/"
horizontalPodAutoscaling:
minReplicas: 1
maxReplicas: 2
podDisruptionBudget:
minAvailable: 0
resources:
requests:
memory: 20Mi
cpu: 10m
limits:
memory: 100Mi
19 changes: 19 additions & 0 deletions deployment/charts/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
codat-classic-app:
components:
- name: ui
image: codatprodregistry.azurecr.io/codat.legal.ui
routing:
- hosts: ["legal.codat.io"]
containerPort: 80
statusPath: "/"
horizontalPodAutoscaling:
minReplicas: 2
maxReplicas: 3
podDisruptionBudget:
minAvailable: 1
resources:
requests:
memory: 20Mi
cpu: 10m
limits:
memory: 100Mi
7 changes: 7 additions & 0 deletions deployment/charts/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
codat-classic-app:
appVersion: 0.0.0
metadata:
serviceName: legal
owningTeam: &team Pepper
env:
Codat_This_OwningTeam: *team
25 changes: 25 additions & 0 deletions deployment/docker/build.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ARG DOCKER_REGISTRY

FROM ${DOCKER_REGISTRY:-}base-images/node-build:24-bookworm-slim AS build

# showLastUpdateAuthor/Time shell out to git; the slim base image doesn't ship it.
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*

WORKDIR /src

COPY package.json .
COPY package-lock.json .
# Credential-less in the repo; the ADO pipeline's npm authenticate step injects
# feed credentials into it before the bake (same mechanism as admin-ui /
# link-amex). Local builds need your own authenticated .npmrc.
COPY .npmrc .

RUN npm ci

# .git is deliberately NOT dockerignored: Docusaurus reads real last-updated
# dates from git history (deploy.yml fetches full history for the same reason).
COPY . .

ENV CI=true

RUN npm run build
11 changes: 11 additions & 0 deletions deployment/docker/build.dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# .git is intentionally included: Docusaurus showLastUpdateAuthor/Time read git history.
.vscode
.dockerignore
.gitignore
.env
build
node_modules
Dockerfile
README.md
deployment
.github
29 changes: 29 additions & 0 deletions deployment/docker/docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
variable VERSION {
default = "dev"
}

variable DOCKER_REGISTRY {
default = "codatintgregistry.azurecr.io/"
}

group "default" {
targets = [
"build",
"app"
]
}

target "build" {
context = "./"
dockerfile = "deployment/docker/build.dockerfile"
args = {
DOCKER_REGISTRY = "${DOCKER_REGISTRY}"
}
tags = [ "codat.legal.build:${VERSION}" ]
}

target "app" {
contexts = { "build" = "target:build" }
dockerfile = "Dockerfile"
tags = [ "codat.legal.ui:${VERSION}" ]
}
69 changes: 69 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;

gzip on;
gzip_types text/plain text/css text/javascript application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;

include /etc/nginx/conf.d/*.conf;

server {
listen 80;
server_name localhost;
server_tokens off;
port_in_redirect off;

# `always` so the headers are also sent on 404s (add_header only
# covers 2xx/3xx by default, and unknown paths here are real 404s).
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header Referrer-Policy "origin-when-cross-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

root /usr/share/nginx/html;
index index.html;

# Static Docusaurus output: no SPA fallback — unknown paths get the
# generated 404 page with a real 404 status.
error_page 404 /404.html;

location / {
absolute_redirect off;
try_files $uri $uri/ =404;
}

# Disable .git directory
location ~ /\.git {
deny all;
access_log off;
log_not_found off;
}
}
}