From 7427445ae061028c83937b7275f00330bf4254b5 Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 09:40:08 +0100 Subject: [PATCH 1/7] EXP-2106: Add K8s container deployment (nginx image, chart, ADO pipeline) Mirrors the legal pattern (legal#75): nginx:1.29.6-alpine-slim serving the Docusaurus build, codat.docs chart on codat-classic-app, docker-bake + ADO pipeline (ArgoApplicationName: docs). Deltas vs legal: build args for ZENDESK_KEY/AMPLITUDE_API_KEY/GTM_ID/feature flags via the codat-docs-build variable group, GitLfs checkout (images are LFS-tracked), explicit CORS on /updates/rss.xml for the portal What's New feed, .git dockerignored (no showLastUpdateAuthor/Time here). Co-Authored-By: Claude Fable 5 --- Dockerfile | 10 +++ deployment/build.azure-pipelines.yaml | 84 +++++++++++++++++++ deployment/charts/Chart.yaml | 8 ++ deployment/charts/values-intg.yaml | 19 +++++ deployment/charts/values-prod.yaml | 21 +++++ deployment/charts/values.yaml | 7 ++ deployment/docker/build.dockerfile | 36 ++++++++ .../docker/build.dockerfile.dockerignore | 14 ++++ deployment/docker/docker-bake.hcl | 56 +++++++++++++ nginx.conf | 84 +++++++++++++++++++ 10 files changed, 339 insertions(+) create mode 100644 Dockerfile create mode 100644 deployment/build.azure-pipelines.yaml create mode 100644 deployment/charts/Chart.yaml create mode 100644 deployment/charts/values-intg.yaml create mode 100644 deployment/charts/values-prod.yaml create mode 100644 deployment/charts/values.yaml create mode 100644 deployment/docker/build.dockerfile create mode 100644 deployment/docker/build.dockerfile.dockerignore create mode 100644 deployment/docker/docker-bake.hcl create mode 100644 nginx.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..573fcd06b9 --- /dev/null +++ b/Dockerfile @@ -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;"] diff --git a/deployment/build.azure-pipelines.yaml b/deployment/build.azure-pipelines.yaml new file mode 100644 index 0000000000..b987b9c6ca --- /dev/null +++ b/deployment/build.azure-pipelines.yaml @@ -0,0 +1,84 @@ +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' + +# Build-time site secrets/config (ZENDESK_KEY, AMPLITUDE_API_KEY, GTM_ID, +# feature flags) — created for EXP-2106; values mirror the GitHub Actions +# secrets/variables the Pages build uses. +variables: + - group: codat-docs-build + +# Array values as Variables not supported by ADO +parameters: + - name: ImagesToPublish + type: object + default: + - 'codat.docs.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 + # Images are Git LFS-tracked; a plain checkout leaves 130-byte + # pointer files and Docusaurus only WARNS on unreadable images, + # so the build would stay green while shipping broken images. + GitLfs: true + AdditionalBakeEnvironment: + ZENDESK_KEY: $(ZENDESK_KEY) + AMPLITUDE_API_KEY: $(AMPLITUDE_API_KEY) + GTM_ID: $(GTM_ID) + FEATURE_DEV_FLAG: $(FEATURE_DEV_FLAG) + FEATURE_NEW_PRODUCTS_FLAG: $(FEATURE_NEW_PRODUCTS_FLAG) + + - ${{ 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: docs diff --git a/deployment/charts/Chart.yaml b/deployment/charts/Chart.yaml new file mode 100644 index 0000000000..c5e543599c --- /dev/null +++ b/deployment/charts/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: 'v2' +name: 'codat.docs' +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' diff --git a/deployment/charts/values-intg.yaml b/deployment/charts/values-intg.yaml new file mode 100644 index 0000000000..9bd9b0f065 --- /dev/null +++ b/deployment/charts/values-intg.yaml @@ -0,0 +1,19 @@ +codat-classic-app: + components: + - name: ui + image: codatintgregistry.azurecr.io/codat.docs.ui + routing: + - hosts: ["docs-integration.codat.io"] + containerPort: 80 + statusPath: "/" + horizontalPodAutoscaling: + minReplicas: 1 + maxReplicas: 2 + podDisruptionBudget: + minAvailable: 0 + resources: + requests: + memory: 20Mi + cpu: 10m + limits: + memory: 100Mi diff --git a/deployment/charts/values-prod.yaml b/deployment/charts/values-prod.yaml new file mode 100644 index 0000000000..db5a914b59 --- /dev/null +++ b/deployment/charts/values-prod.yaml @@ -0,0 +1,21 @@ +codat-classic-app: + components: + - name: ui + image: codatprodregistry.azurecr.io/codat.docs.ui + routing: + - hosts: ["docs.codat.io"] + containerPort: 80 + statusPath: "/" + # High-traffic, client-facing (unlike legal): sized up per EXP-2106. + # Review requests/limits against real usage after a week in prod. + horizontalPodAutoscaling: + minReplicas: 2 + maxReplicas: 4 + podDisruptionBudget: + minAvailable: 1 + resources: + requests: + memory: 50Mi + cpu: 25m + limits: + memory: 200Mi diff --git a/deployment/charts/values.yaml b/deployment/charts/values.yaml new file mode 100644 index 0000000000..88a1573e7b --- /dev/null +++ b/deployment/charts/values.yaml @@ -0,0 +1,7 @@ +codat-classic-app: + appVersion: 0.0.0 + metadata: + serviceName: docs + owningTeam: &team Pepper + env: + Codat_This_OwningTeam: *team diff --git a/deployment/docker/build.dockerfile b/deployment/docker/build.dockerfile new file mode 100644 index 0000000000..25cbb0b040 --- /dev/null +++ b/deployment/docker/build.dockerfile @@ -0,0 +1,36 @@ +ARG DOCKER_REGISTRY + +FROM ${DOCKER_REGISTRY:-}base-images/node-build:24-bookworm-slim AS build + +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 legal / admin-ui). +# Local builds need your own authenticated .npmrc. +COPY .npmrc . + +RUN npm ci + +# .git IS dockerignored: this site doesn't use showLastUpdateAuthor/Time, so +# the build never shells out to git (unlike legal). +COPY . . + +ENV CI=true + +# Build-time site config, baked into the static output (docusaurus.config.js +# customFields). Values arrive via docker-bake.hcl from the pipeline env; +# BRANCH is deliberately unset — editUrl falls back to main. +ARG ZENDESK_KEY +ARG AMPLITUDE_API_KEY +ARG GTM_ID +ARG FEATURE_DEV_FLAG +ARG FEATURE_NEW_PRODUCTS_FLAG +ENV ZENDESK_KEY=${ZENDESK_KEY} \ + AMPLITUDE_API_KEY=${AMPLITUDE_API_KEY} \ + GTM_ID=${GTM_ID} \ + FEATURE_DEV_FLAG=${FEATURE_DEV_FLAG} \ + FEATURE_NEW_PRODUCTS_FLAG=${FEATURE_NEW_PRODUCTS_FLAG} + +RUN npm run build diff --git a/deployment/docker/build.dockerfile.dockerignore b/deployment/docker/build.dockerfile.dockerignore new file mode 100644 index 0000000000..79c1325840 --- /dev/null +++ b/deployment/docker/build.dockerfile.dockerignore @@ -0,0 +1,14 @@ +# .git is excluded: no showLastUpdateAuthor/Time in docusaurus.config.js, so +# the build never reads git history (legal keeps it; we don't need it). +.git +.vscode +.dockerignore +.gitignore +.env +build +node_modules +Dockerfile +README.md +deployment +.github +code_utils diff --git a/deployment/docker/docker-bake.hcl b/deployment/docker/docker-bake.hcl new file mode 100644 index 0000000000..6e5bbfa4e1 --- /dev/null +++ b/deployment/docker/docker-bake.hcl @@ -0,0 +1,56 @@ +variable VERSION { + default = "dev" +} + +variable DOCKER_REGISTRY { + default = "codatintgregistry.azurecr.io/" +} + +# Build-time site config; bake reads these from the pipeline step's env +# (values come from the codat-docs-build variable group). +variable ZENDESK_KEY { + default = "" +} + +variable AMPLITUDE_API_KEY { + default = "" +} + +variable GTM_ID { + default = "" +} + +variable FEATURE_DEV_FLAG { + default = "" +} + +variable FEATURE_NEW_PRODUCTS_FLAG { + default = "" +} + +group "default" { + targets = [ + "build", + "app" + ] +} + +target "build" { + context = "./" + dockerfile = "deployment/docker/build.dockerfile" + args = { + DOCKER_REGISTRY = "${DOCKER_REGISTRY}" + ZENDESK_KEY = "${ZENDESK_KEY}" + AMPLITUDE_API_KEY = "${AMPLITUDE_API_KEY}" + GTM_ID = "${GTM_ID}" + FEATURE_DEV_FLAG = "${FEATURE_DEV_FLAG}" + FEATURE_NEW_PRODUCTS_FLAG = "${FEATURE_NEW_PRODUCTS_FLAG}" + } + tags = [ "codat.docs.build:${VERSION}" ] +} + +target "app" { + contexts = { "build" = "target:build" } + dockerfile = "Dockerfile" + tags = [ "codat.docs.ui:${VERSION}" ] +} diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000000..96bf54610d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,84 @@ +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; + } + + # portal-ui's "What's New" dropdown (useParseWhatsNewFeed) fetches this + # cross-origin from app.codat.io. GitHub Pages sent + # Access-Control-Allow-Origin: * on everything; nginx must opt in. + # add_header in a location suppresses the inherited server-level set, + # so the security headers are repeated here. + location = /updates/rss.xml { + add_header Access-Control-Allow-Origin "*" always; + 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; + } + + # Disable .git directory + location ~ /\.git { + deny all; + access_log off; + log_not_found off; + } + } +} From 2850cdb0518f31b0185314f70b3145b257b9595e Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 09:41:13 +0100 Subject: [PATCH 2/7] EXP-2106: Make .npmrc credential-less; workflows auth via ~/.npmrc The ADO pipeline injects feed creds with npmAuthenticate pre-bake, which needs a credential-less repo .npmrc (and always-auth is deprecated). The Pages/check workflows keep working by appending creds to ~/.npmrc instead (same mechanism as legal). Pages stays the serving path until cutover. Co-Authored-By: Claude Fable 5 --- .github/workflows/check-formatting.yml | 13 +++++++++++-- .github/workflows/check-markdown-lint.yml | 13 +++++++++++-- .github/workflows/check-spelling.yml | 13 +++++++++++-- .github/workflows/deploy.yml | 14 ++++++++++++-- .github/workflows/pr.yml | 17 +++++++++++++---- .npmrc | 4 ---- 6 files changed, 58 insertions(+), 16 deletions(-) diff --git a/.github/workflows/check-formatting.yml b/.github/workflows/check-formatting.yml index 3f33c754e5..4681b3a1a2 100644 --- a/.github/workflows/check-formatting.yml +++ b/.github/workflows/check-formatting.yml @@ -18,10 +18,19 @@ jobs: node-version: '20' cache: 'npm' - - name: Install dependencies - run: npm ci + # The repo .npmrc is credential-less; inject feed creds into ~/.npmrc + - 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: Check formatting run: npm run format:js:check \ No newline at end of file diff --git a/.github/workflows/check-markdown-lint.yml b/.github/workflows/check-markdown-lint.yml index 0f3ff9be37..ab2aa7404b 100644 --- a/.github/workflows/check-markdown-lint.yml +++ b/.github/workflows/check-markdown-lint.yml @@ -29,10 +29,19 @@ jobs: node-version: '20' cache: 'npm' - - name: Install dependencies - run: npm ci + # The repo .npmrc is credential-less; inject feed creds into ~/.npmrc + - 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: Check markdown formatting with Prettier run: npm run format:mdx:check \ No newline at end of file diff --git a/.github/workflows/check-spelling.yml b/.github/workflows/check-spelling.yml index d7039d1ac1..894fb895be 100644 --- a/.github/workflows/check-spelling.yml +++ b/.github/workflows/check-spelling.yml @@ -18,10 +18,19 @@ jobs: node-version: '20' cache: 'npm' - - name: Install dependencies - run: npm install -g cspell + # The repo .npmrc is credential-less; inject feed creds into ~/.npmrc + - 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 install -g cspell - name: Run spell check run: cspell "**/*.md" "**/*.mdx" --config cspell.json \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9164428910..a41eda209b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,11 +29,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 env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 45aaf931ff..f4525d0f06 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -25,13 +25,22 @@ jobs: node-version: 24 cache: npm - # 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 + # The ADO registry requires auth even to install; the repo .npmrc is + # credential-less so creds go into ~/.npmrc here (fork PRs don't get + # secrets and will fail) + - 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 env: diff --git a/.npmrc b/.npmrc index 8df58e10f6..8ac16dab13 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,2 @@ registry=https://pkgs.dev.azure.com/codat/Codat/_packaging/codat-npm/npm/registry/ -always-auth=true 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=engineering@codat.io \ No newline at end of file From 076b6122a37abce85b84625fd478d26f47ecd904 Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 11:30:20 +0100 Subject: [PATCH 3/7] EXP-2106: Remove dead FEATURE_DEV_FLAG / FEATURE_NEW_PRODUCTS_FLAG Both have been false everywhere since the Pages migration. The staging banner is incompatible with build-once-promote (one image serves intg and prod), and the new-products banner links to a Sept 2023 announcement. Removes the flags end-to-end: theme components + dead styles, customFields, .env.example, GH workflows, docker build args, pipeline env. The remaining build-time config (ZENDESK_KEY, AMPLITUDE_API_KEY, GTM_ID) now comes from Key Vault via the codat-docs-build KV-linked group - hyphenated $() names because KV forbids underscores in secret names. Co-Authored-By: Claude Fable 5 --- .env.example | 3 --- .github/workflows/deploy.yml | 2 -- .github/workflows/pr.yml | 2 -- .husky/_/post-checkout | 3 +++ .husky/_/post-commit | 3 +++ .husky/_/post-merge | 3 +++ .husky/_/pre-push | 3 +++ deployment/build.azure-pipelines.yaml | 14 ++++++-------- deployment/docker/build.dockerfile | 6 +----- deployment/docker/docker-bake.hcl | 18 ++++-------------- docusaurus.config.js | 2 -- src/theme/BlogLayout/index.tsx | 17 ----------------- src/theme/BlogLayout/styles.module.scss | 23 ----------------------- src/theme/DocPage/index.tsx | 11 ----------- src/theme/DocPage/styles.module.scss | 21 --------------------- 15 files changed, 23 insertions(+), 108 deletions(-) create mode 100644 .husky/_/post-checkout create mode 100644 .husky/_/post-commit create mode 100644 .husky/_/post-merge create mode 100644 .husky/_/pre-push diff --git a/.env.example b/.env.example index 5c51f5ae20..7eaf8973c3 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,3 @@ ZENDESK_KEY= GTM_ID= BRANCH=optional (sets the edit path) - -FEATURE_DEV_FLAG=false -FEATURE_NEW_PRODUCTS_FLAG=false \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a41eda209b..b59d4c3c5a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -50,8 +50,6 @@ jobs: ZENDESK_KEY: ${{ secrets.ZENDESK_KEY }} AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }} GTM_ID: ${{ vars.GTM_ID }} - FEATURE_DEV_FLAG: ${{ vars.FEATURE_DEV_FLAG }} - FEATURE_NEW_PRODUCTS_FLAG: ${{ vars.FEATURE_NEW_PRODUCTS_FLAG }} - uses: actions/configure-pages@v6 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f4525d0f06..6415c237a2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -46,8 +46,6 @@ jobs: env: # plugin-google-gtag requires a trackingID, so PR builds need it too GTM_ID: ${{ vars.GTM_ID }} - FEATURE_DEV_FLAG: ${{ vars.FEATURE_DEV_FLAG }} - FEATURE_NEW_PRODUCTS_FLAG: ${{ vars.FEATURE_NEW_PRODUCTS_FLAG }} # Link checking reuses the build above: linkinator serves ./build on an # ephemeral localhost port, so no deployed preview environment is needed. diff --git a/.husky/_/post-checkout b/.husky/_/post-checkout new file mode 100644 index 0000000000..ca7fcb4008 --- /dev/null +++ b/.husky/_/post-checkout @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } +git lfs post-checkout "$@" diff --git a/.husky/_/post-commit b/.husky/_/post-commit new file mode 100644 index 0000000000..52b339cb3f --- /dev/null +++ b/.husky/_/post-commit @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } +git lfs post-commit "$@" diff --git a/.husky/_/post-merge b/.husky/_/post-merge new file mode 100644 index 0000000000..a912e667aa --- /dev/null +++ b/.husky/_/post-merge @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } +git lfs post-merge "$@" diff --git a/.husky/_/pre-push b/.husky/_/pre-push new file mode 100644 index 0000000000..0f0089bc25 --- /dev/null +++ b/.husky/_/pre-push @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } +git lfs pre-push "$@" diff --git a/deployment/build.azure-pipelines.yaml b/deployment/build.azure-pipelines.yaml index b987b9c6ca..6ece628122 100644 --- a/deployment/build.azure-pipelines.yaml +++ b/deployment/build.azure-pipelines.yaml @@ -18,9 +18,9 @@ resources: type: 'git' name: 'Codat/YAMLBuildTemplates' -# Build-time site secrets/config (ZENDESK_KEY, AMPLITUDE_API_KEY, GTM_ID, -# feature flags) — created for EXP-2106; values mirror the GitHub Actions -# secrets/variables the Pages build uses. +# Build-time site config (zendesk-key, amplitude-api-key, gtm-id) — a Key +# Vault-linked group created for EXP-2106. KV secret names can't contain +# underscores, hence the hyphenated $() references below. variables: - group: codat-docs-build @@ -64,11 +64,9 @@ stages: # so the build would stay green while shipping broken images. GitLfs: true AdditionalBakeEnvironment: - ZENDESK_KEY: $(ZENDESK_KEY) - AMPLITUDE_API_KEY: $(AMPLITUDE_API_KEY) - GTM_ID: $(GTM_ID) - FEATURE_DEV_FLAG: $(FEATURE_DEV_FLAG) - FEATURE_NEW_PRODUCTS_FLAG: $(FEATURE_NEW_PRODUCTS_FLAG) + ZENDESK_KEY: $(zendesk-key) + AMPLITUDE_API_KEY: $(amplitude-api-key) + GTM_ID: $(gtm-id) - ${{ if not(in( variables['Build.Reason'], 'PullRequest' )) }}: - ${{ each env in parameters.DeploymentEnvironments }}: diff --git a/deployment/docker/build.dockerfile b/deployment/docker/build.dockerfile index 25cbb0b040..6827e7fe43 100644 --- a/deployment/docker/build.dockerfile +++ b/deployment/docker/build.dockerfile @@ -25,12 +25,8 @@ ENV CI=true ARG ZENDESK_KEY ARG AMPLITUDE_API_KEY ARG GTM_ID -ARG FEATURE_DEV_FLAG -ARG FEATURE_NEW_PRODUCTS_FLAG ENV ZENDESK_KEY=${ZENDESK_KEY} \ AMPLITUDE_API_KEY=${AMPLITUDE_API_KEY} \ - GTM_ID=${GTM_ID} \ - FEATURE_DEV_FLAG=${FEATURE_DEV_FLAG} \ - FEATURE_NEW_PRODUCTS_FLAG=${FEATURE_NEW_PRODUCTS_FLAG} + GTM_ID=${GTM_ID} RUN npm run build diff --git a/deployment/docker/docker-bake.hcl b/deployment/docker/docker-bake.hcl index 6e5bbfa4e1..832392824c 100644 --- a/deployment/docker/docker-bake.hcl +++ b/deployment/docker/docker-bake.hcl @@ -20,14 +20,6 @@ variable GTM_ID { default = "" } -variable FEATURE_DEV_FLAG { - default = "" -} - -variable FEATURE_NEW_PRODUCTS_FLAG { - default = "" -} - group "default" { targets = [ "build", @@ -39,12 +31,10 @@ target "build" { context = "./" dockerfile = "deployment/docker/build.dockerfile" args = { - DOCKER_REGISTRY = "${DOCKER_REGISTRY}" - ZENDESK_KEY = "${ZENDESK_KEY}" - AMPLITUDE_API_KEY = "${AMPLITUDE_API_KEY}" - GTM_ID = "${GTM_ID}" - FEATURE_DEV_FLAG = "${FEATURE_DEV_FLAG}" - FEATURE_NEW_PRODUCTS_FLAG = "${FEATURE_NEW_PRODUCTS_FLAG}" + DOCKER_REGISTRY = "${DOCKER_REGISTRY}" + ZENDESK_KEY = "${ZENDESK_KEY}" + AMPLITUDE_API_KEY = "${AMPLITUDE_API_KEY}" + GTM_ID = "${GTM_ID}" } tags = [ "codat.docs.build:${VERSION}" ] } diff --git a/docusaurus.config.js b/docusaurus.config.js index dd466c30f9..0ff1d23173 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -113,8 +113,6 @@ const config = { customFields: { DEVELOPMENT: process.env.NODE_ENV === "development", ZENDESK_KEY: process.env.ZENDESK_KEY, - FEATURE_DEV_FLAG: process.env.FEATURE_DEV_FLAG, - FEATURE_NEW_PRODUCTS_FLAG: process.env.FEATURE_NEW_PRODUCTS_FLAG, AMPLITUDE_API_KEY: process.env.AMPLITUDE_API_KEY, }, diff --git a/src/theme/BlogLayout/index.tsx b/src/theme/BlogLayout/index.tsx index 1f505bc8ed..344039dade 100644 --- a/src/theme/BlogLayout/index.tsx +++ b/src/theme/BlogLayout/index.tsx @@ -12,7 +12,6 @@ import Layout from "@theme/Layout"; import BlogSidebar from "@theme/BlogSidebar"; import Navbar from "@theme/Navbar"; -import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import { useLocation } from "@docusaurus/router"; import styles from "./styles.module.scss"; @@ -23,7 +22,6 @@ export default function BlogLayout(props: Props): JSX.Element { const { sidebar, toc, children, ...layoutProps } = props; const hasSidebar = sidebar && sidebar.items.length > 0; - const { siteConfig } = useDocusaurusContext(); const location = useLocation(); const isUpdatesPage = @@ -32,21 +30,6 @@ export default function BlogLayout(props: Props): JSX.Element { return ( - {siteConfig.customFields?.FEATURE_DEV_FLAG === "true" && ( -
- This is a staging build of the docs -
- )} - - {siteConfig.customFields?.FEATURE_NEW_PRODUCTS_FLAG === "true" && ( - - )} -
diff --git a/src/theme/BlogLayout/styles.module.scss b/src/theme/BlogLayout/styles.module.scss index c14f2314c1..af08e6b737 100644 --- a/src/theme/BlogLayout/styles.module.scss +++ b/src/theme/BlogLayout/styles.module.scss @@ -42,27 +42,4 @@ .article { padding: 1em; max-width: 1000px; -} - -.devFlag { - width: 100%; - text-align: center; - padding: 4px 8px; - color: black; - background-color: orange; -} - -.newFlagPositive { - width: 100%; - text-align: center; - padding: 4px 8px; - background-color: var(--button-primary-bg); - transition: 0.5s; - - --ifm-link-color: white; - --ifm-link-hover-color: white; - - &:hover a { - opacity: 0.7; - } } \ No newline at end of file diff --git a/src/theme/DocPage/index.tsx b/src/theme/DocPage/index.tsx index 4974173ba1..1f7dea11a1 100644 --- a/src/theme/DocPage/index.tsx +++ b/src/theme/DocPage/index.tsx @@ -22,7 +22,6 @@ import {translate} from '@docusaurus/Translate'; import {ThemeClassNames} from '@docusaurus/theme-common'; import Head from '@docusaurus/Head'; import {useWindowSize} from '@docusaurus/theme-common'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import renderRoutes from '@docusaurus/renderRoutes'; import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs-types'; @@ -178,22 +177,12 @@ function DocPage(props: Props): JSX.Element { />; } - const {siteConfig} = useDocusaurusContext(); - return ( <> - { - siteConfig.customFields?.FEATURE_DEV_FLAG === "true" &&
This is a staging build of the docs. Do not share the link externally.
- } - - { - siteConfig.customFields?.FEATURE_NEW_PRODUCTS_FLAG === "true" && - } - diff --git a/src/theme/DocPage/styles.module.scss b/src/theme/DocPage/styles.module.scss index 5456de3d4a..2a8a430d76 100644 --- a/src/theme/DocPage/styles.module.scss +++ b/src/theme/DocPage/styles.module.scss @@ -32,28 +32,7 @@ display: none; } -.devFlag { - width: 100%; - text-align: center; - padding: 4px 8px; - color: black; - background-color: orange; -} - -.newFlagPositive { - width: 100%; - text-align: center; - padding: 4px 8px; - background-color: var(--button-primary-bg); - transition: 0.5s; - --ifm-link-color: white; - --ifm-link-hover-color: white; - - &:hover a { - opacity: 0.7; - } -} @media (min-width: 997px) { .docMainContainer { From 13bdd239078910385954a043fa0ede3b6eb84aae Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 11:31:04 +0100 Subject: [PATCH 4/7] EXP-2106: Remove accidentally committed husky shims .husky/_ is husky-generated, not meant to be tracked (main only tracks .husky/pre-commit). Co-Authored-By: Claude Fable 5 --- .husky/_/post-checkout | 3 --- .husky/_/post-commit | 3 --- .husky/_/post-merge | 3 --- .husky/_/pre-push | 3 --- 4 files changed, 12 deletions(-) delete mode 100644 .husky/_/post-checkout delete mode 100644 .husky/_/post-commit delete mode 100644 .husky/_/post-merge delete mode 100644 .husky/_/pre-push diff --git a/.husky/_/post-checkout b/.husky/_/post-checkout deleted file mode 100644 index ca7fcb4008..0000000000 --- a/.husky/_/post-checkout +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } -git lfs post-checkout "$@" diff --git a/.husky/_/post-commit b/.husky/_/post-commit deleted file mode 100644 index 52b339cb3f..0000000000 --- a/.husky/_/post-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } -git lfs post-commit "$@" diff --git a/.husky/_/post-merge b/.husky/_/post-merge deleted file mode 100644 index a912e667aa..0000000000 --- a/.husky/_/post-merge +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } -git lfs post-merge "$@" diff --git a/.husky/_/pre-push b/.husky/_/pre-push deleted file mode 100644 index 0f0089bc25..0000000000 --- a/.husky/_/pre-push +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } -git lfs pre-push "$@" From 838d0e3386c1edc5034f4ff87c67b0709e79a00b Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 13:31:52 +0100 Subject: [PATCH 5/7] EXP-2106: Point pipeline at the docs-* Key Vault secret names codat-docs-build is now a KV-linked group reading docs-zendesk-key, docs-amplitude-api-key and docs-gtm-id from the codat-integration vault. Co-Authored-By: Claude Fable 5 --- deployment/build.azure-pipelines.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/deployment/build.azure-pipelines.yaml b/deployment/build.azure-pipelines.yaml index 6ece628122..bb37a2f9c3 100644 --- a/deployment/build.azure-pipelines.yaml +++ b/deployment/build.azure-pipelines.yaml @@ -18,9 +18,9 @@ resources: type: 'git' name: 'Codat/YAMLBuildTemplates' -# Build-time site config (zendesk-key, amplitude-api-key, gtm-id) — a Key -# Vault-linked group created for EXP-2106. KV secret names can't contain -# underscores, hence the hyphenated $() references below. +# Build-time site config — a Key Vault-linked group (EXP-2106) reading +# docs-* secrets from the codat-integration vault at queue time. KV secret +# names can't contain underscores, hence the hyphenated $() references below. variables: - group: codat-docs-build @@ -64,9 +64,9 @@ stages: # so the build would stay green while shipping broken images. GitLfs: true AdditionalBakeEnvironment: - ZENDESK_KEY: $(zendesk-key) - AMPLITUDE_API_KEY: $(amplitude-api-key) - GTM_ID: $(gtm-id) + ZENDESK_KEY: $(docs-zendesk-key) + AMPLITUDE_API_KEY: $(docs-amplitude-api-key) + GTM_ID: $(docs-gtm-id) - ${{ if not(in( variables['Build.Reason'], 'PullRequest' )) }}: - ${{ each env in parameters.DeploymentEnvironments }}: From 951f3902e8b746a36de6895cf97dba3a834992ae Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 14:40:02 +0100 Subject: [PATCH 6/7] EXP-2106: Fetch Key Vault secrets on the agent, not via variable group codat-integration has public network access disabled, so a KV-linked variable group cannot fetch from it (ADO service-side fetch is not a trusted service - ForbiddenByConnection). The pool agents CAN reach it via the private endpoint, so use the template's new KeyVaultName / KeyVaultSecretsFilter params (AzureKeyVault@2 on the agent, same pattern as Build/DotNet.yml). AdditionalBakeEnvironment mappings are unchanged. Co-Authored-By: Claude Fable 5 --- deployment/build.azure-pipelines.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/deployment/build.azure-pipelines.yaml b/deployment/build.azure-pipelines.yaml index bb37a2f9c3..c349e42fbd 100644 --- a/deployment/build.azure-pipelines.yaml +++ b/deployment/build.azure-pipelines.yaml @@ -18,12 +18,6 @@ resources: type: 'git' name: 'Codat/YAMLBuildTemplates' -# Build-time site config — a Key Vault-linked group (EXP-2106) reading -# docs-* secrets from the codat-integration vault at queue time. KV secret -# names can't contain underscores, hence the hyphenated $() references below. -variables: - - group: codat-docs-build - # Array values as Variables not supported by ADO parameters: - name: ImagesToPublish @@ -63,6 +57,13 @@ stages: # pointer files and Docusaurus only WARNS on unreadable images, # so the build would stay green while shipping broken images. GitLfs: true + # Build-time site config, fetched on the agent (the vault blocks + # public network access, so a KV-linked variable group can't read + # it — only the pool agents can, via the private endpoint). KV + # secret names can't contain underscores, hence the hyphenated + # $() references; the bake file maps them to the underscored args. + KeyVaultName: codat-integration + KeyVaultSecretsFilter: 'docs-zendesk-key,docs-amplitude-api-key,docs-gtm-id' AdditionalBakeEnvironment: ZENDESK_KEY: $(docs-zendesk-key) AMPLITUDE_API_KEY: $(docs-amplitude-api-key) From bfce4ae3dfc80036fd988a3551cb5073aba9c6f8 Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Fri, 31 Jul 2026 15:25:43 +0100 Subject: [PATCH 7/7] Remove stale connection-management interest form link The low-code embeddable UI component call-for-interest (Google Form) is no longer current; the tip now just points at the connection management best-practices article. Co-Authored-By: Claude Fable 5 --- docs/auth-flow/build/build-your-own-authorization-journey.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/auth-flow/build/build-your-own-authorization-journey.md b/docs/auth-flow/build/build-your-own-authorization-journey.md index aa24e5f660..d2859e5b12 100644 --- a/docs/auth-flow/build/build-your-own-authorization-journey.md +++ b/docs/auth-flow/build/build-your-own-authorization-journey.md @@ -130,8 +130,6 @@ The end user would need to authorize a new data connection if you wish to view n :::tip Codat's connection management -Codat is releasing a low-code embeddable UI component for connection management. Please [let us know](https://forms.gle/d1zuh2iHBLJCNCsj9) if you are interested in using it. - For a detailed best practices article on connection management, see [Connection management](/auth-flow/optimize/connection-management). :::