From d0230e39e8f320f1efba0ad00b681a4505754a22 Mon Sep 17 00:00:00 2001 From: Stanislas Bruhiere Date: Fri, 12 Jun 2026 16:39:29 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(deploy)=20harmonize=20pro?= =?UTF-8?q?d=20deployment=20layout=20under=20deploy/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move helm chart, prod compose examples and buildpack scripts under deploy/{kubernetes,docker,paas}/. Wire THEME_CUSTOMIZATION_JSON directly in settings so the env var works on Docker and k8s without the Scalingo buildpack pivot to a file. --- .github/workflows/helmfile-linter.yaml | 2 +- .github/workflows/release-helm-chart.yaml | 6 +- CHANGELOG.md | 2 + Procfile | 2 +- bin/Tiltfile | 2 +- deploy/docker/README.md | 43 +++++++++++++ .../compose.yaml => deploy/docker/compose.yml | 0 .../docker/examples}/keycloak/README.md | 2 +- .../docker/examples}/keycloak/compose.yaml | 0 .../docker/examples}/minio/README.md | 2 +- .../docker/examples}/minio/compose.yaml | 0 .../docker/examples}/nginx-proxy/README.md | 2 +- .../docker/examples}/nginx-proxy/compose.yaml | 0 .../env.d/dev/configuration/theme/demo.json | 0 .../env.d/dev/values.dev-backend.yaml.gotmpl | 0 .../helm/env.d/dev/values.impress.yaml.gotmpl | 0 .../feature/values.dev-backend.yaml.gotmpl | 0 .../env.d/feature/values.impress.yaml.gotmpl | 0 .../kubernetes}/helm/helmfile.yaml.gotmpl | 0 .../kubernetes}/helm/impress/Chart.yaml | 0 .../kubernetes}/helm/impress/README.md | 0 .../helm/impress/generate-readme.sh | 0 .../helm/impress/templates/_helpers.tpl | 0 .../templates/backend_cronjob_list.yaml | 0 .../impress/templates/backend_deployment.yaml | 0 .../helm/impress/templates/backend_job.yml | 0 .../backend_job_createsuperuser.yaml | 0 .../templates/backend_job_migrate.yaml | 0 .../helm/impress/templates/backend_svc.yaml | 0 .../templates/celery_worker_deployment.yaml | 0 .../impress/templates/docspec_deployment.yaml | 0 .../helm/impress/templates/docspec_svc.yaml | 0 .../templates/frontend_deployment.yaml | 0 .../helm/impress/templates/frontend_svc.yaml | 0 .../impress/templates/ingress-redirects.yaml | 0 .../helm/impress/templates/ingress.yaml | 0 .../helm/impress/templates/ingress_admin.yaml | 0 .../templates/ingress_collaboration_api.yaml | 0 .../templates/ingress_collaboration_ws.yaml | 0 .../helm/impress/templates/ingress_media.yaml | 0 .../impress/templates/ingress_posthog.yaml | 0 .../templates/ingress_posthog_assets.yaml | 0 .../helm/impress/templates/media_svc.yaml | 0 .../impress/templates/posthog_assets_svc.yaml | 0 .../helm/impress/templates/posthog_svc.yaml | 0 .../theme_customization_file_cm.yaml | 0 .../templates/yprovider_deployment.yaml | 0 .../yprovider_deployment_converter.yaml | 0 .../helm/impress/templates/yprovider_svc.yaml | 0 .../templates/yprovider_svc_converter.yaml | 0 .../kubernetes}/helm/impress/values.yaml | 0 deploy/paas/README.md | 31 ++++++++++ {bin => deploy/paas}/buildpack_postcompile.sh | 0 .../paas}/buildpack_postfrontend.sh | 2 +- {bin => deploy/paas}/buildpack_start.sh | 0 docs/customization.md | 32 ++++++++-- docs/env.md | 1 + docs/installation/compose.md | 18 +++--- docs/installation/scalingo.md | 10 +-- docs/release.md | 2 +- src/backend/core/api/viewsets.py | 19 ++++++ src/backend/core/tests/test_api_config.py | 62 +++++++++++++++++++ src/backend/impress/settings.py | 6 ++ 63 files changed, 217 insertions(+), 29 deletions(-) create mode 100644 deploy/docker/README.md rename docs/examples/compose/compose.yaml => deploy/docker/compose.yml (100%) rename {docs/examples/compose => deploy/docker/examples}/keycloak/README.md (97%) rename {docs/examples/compose => deploy/docker/examples}/keycloak/compose.yaml (100%) rename {docs/examples/compose => deploy/docker/examples}/minio/README.md (97%) rename {docs/examples/compose => deploy/docker/examples}/minio/compose.yaml (100%) rename {docs/examples/compose => deploy/docker/examples}/nginx-proxy/README.md (94%) rename {docs/examples/compose => deploy/docker/examples}/nginx-proxy/compose.yaml (100%) rename {src => deploy/kubernetes}/helm/env.d/dev/configuration/theme/demo.json (100%) rename {src => deploy/kubernetes}/helm/env.d/dev/values.dev-backend.yaml.gotmpl (100%) rename {src => deploy/kubernetes}/helm/env.d/dev/values.impress.yaml.gotmpl (100%) rename {src => deploy/kubernetes}/helm/env.d/feature/values.dev-backend.yaml.gotmpl (100%) rename {src => deploy/kubernetes}/helm/env.d/feature/values.impress.yaml.gotmpl (100%) rename {src => deploy/kubernetes}/helm/helmfile.yaml.gotmpl (100%) rename {src => deploy/kubernetes}/helm/impress/Chart.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/README.md (100%) rename {src => deploy/kubernetes}/helm/impress/generate-readme.sh (100%) rename {src => deploy/kubernetes}/helm/impress/templates/_helpers.tpl (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_cronjob_list.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_deployment.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_job.yml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_job_createsuperuser.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_job_migrate.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/backend_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/celery_worker_deployment.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/docspec_deployment.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/docspec_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/frontend_deployment.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/frontend_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress-redirects.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_admin.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_collaboration_api.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_collaboration_ws.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_media.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_posthog.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/ingress_posthog_assets.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/media_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/posthog_assets_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/posthog_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/theme_customization_file_cm.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/yprovider_deployment.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/yprovider_deployment_converter.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/yprovider_svc.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/templates/yprovider_svc_converter.yaml (100%) rename {src => deploy/kubernetes}/helm/impress/values.yaml (100%) create mode 100644 deploy/paas/README.md rename {bin => deploy/paas}/buildpack_postcompile.sh (100%) rename {bin => deploy/paas}/buildpack_postfrontend.sh (98%) rename {bin => deploy/paas}/buildpack_start.sh (100%) diff --git a/.github/workflows/helmfile-linter.yaml b/.github/workflows/helmfile-linter.yaml index 793425e71d..0c36cdd7c4 100644 --- a/.github/workflows/helmfile-linter.yaml +++ b/.github/workflows/helmfile-linter.yaml @@ -21,7 +21,7 @@ jobs: shell: bash run: | set -e - HELMFILE=src/helm/helmfile.yaml.gotmpl + HELMFILE=deploy/kubernetes/helm/helmfile.yaml.gotmpl environments=$(awk 'BEGIN {in_env=0} /^environments:/ {in_env=1; next} /^---/ {in_env=0} in_env && /^ [^ ]/ {gsub(/^ /,""); gsub(/:.*$/,""); print}' "$HELMFILE") for env in $environments; do echo "################### $env lint ###################" diff --git a/.github/workflows/release-helm-chart.yaml b/.github/workflows/release-helm-chart.yaml index fe90a0cc0d..f9977a4d97 100644 --- a/.github/workflows/release-helm-chart.yaml +++ b/.github/workflows/release-helm-chart.yaml @@ -4,7 +4,7 @@ run-name: Release Chart on: push: paths: - - src/helm/impress/** + - deploy/kubernetes/helm/impress/** jobs: release: @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Cleanup - run: rm -rf ./src/helm/extra + run: rm -rf ./deploy/kubernetes/helm/extra - name: Install Helm uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5 @@ -30,5 +30,5 @@ jobs: - name: Publish Helm charts uses: numerique-gouv/helm-gh-pages@2cf477ae49d7c70037ceb1685803f4f7bad9b981 # add-overwrite-option with: - charts_dir: ./src/helm + charts_dir: ./deploy/kubernetes/helm token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ba680c1f..07c1663c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to ## [Unreleased] +- ♻️ (deploy) harmonize prod deployment layout under deploy/ #2425 + ### Added - ✨(frontend) add top parent on sub docs search #1952 diff --git a/Procfile b/Procfile index baed9d65ce..5641107286 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ -web: bin/buildpack_start.sh +web: deploy/paas/buildpack_start.sh postdeploy: python manage.py migrate diff --git a/bin/Tiltfile b/bin/Tiltfile index edcea0a7a6..0254c18814 100644 --- a/bin/Tiltfile +++ b/bin/Tiltfile @@ -46,7 +46,7 @@ k8s_resource('impress-docs-backend-migrate', resource_deps=['dev-backend-postgre k8s_resource('impress-docs-backend-createsuperuser', resource_deps=['impress-docs-backend-migrate']) k8s_resource('dev-backend-keycloak', resource_deps=['dev-backend-keycloak-pg']) k8s_resource('impress-docs-backend', resource_deps=['impress-docs-backend-migrate', 'dev-backend-redis', 'dev-backend-keycloak', 'dev-backend-postgres', 'dev-backend-minio:statefulset']) -k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .')) +k8s_yaml(local('cd ../deploy/kubernetes/helm && helmfile -n impress -e dev template .')) migration = ''' set -eu diff --git a/deploy/docker/README.md b/deploy/docker/README.md new file mode 100644 index 0000000000..923baf0d96 --- /dev/null +++ b/deploy/docker/README.md @@ -0,0 +1,43 @@ +# Docs — Docker Compose deployment + +Prod-oriented Docker Compose layout for [Docs](https://github.com/suitenumerique/docs). + +> The root `compose.yml` of the repository is the **development** stack. Use the files in this folder to deploy Docs in production. + +## Layout + +``` +deploy/docker/ +├── compose.yml # Main production compose file +├── env.d/ # (you provide) postgres / backend / yprovider / common env files +└── examples/ + ├── keycloak/ # Sample OIDC provider + ├── minio/ # Sample S3-compatible object storage + └── nginx-proxy/ # Sample reverse proxy with Let's Encrypt +``` + +## Getting started + +See the [installation walkthrough](../../docs/installation/compose.md) — it covers env files, OIDC, S3, Postgres, Redis, mail and reverse-proxy setup end to end. + +Quick setup: + +```bash +mkdir -p docs/env.d && cd docs + +# Fetch the compose file and example env files +curl -o compose.yml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/deploy/docker/compose.yml +curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/common +curl -o env.d/backend https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/backend +curl -o env.d/yprovider https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/yprovider +curl -o env.d/postgresql https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/postgresql + +# Pin to a tagged release before going to production +docker compose up -d +docker compose run --rm backend python manage.py migrate +``` + +## Other deployments + +* Kubernetes/Helm: see [`deploy/kubernetes/`](../kubernetes/). +* PaaS (Scalingo, Clever Cloud, …): see [`deploy/paas/`](../paas/). diff --git a/docs/examples/compose/compose.yaml b/deploy/docker/compose.yml similarity index 100% rename from docs/examples/compose/compose.yaml rename to deploy/docker/compose.yml diff --git a/docs/examples/compose/keycloak/README.md b/deploy/docker/examples/keycloak/README.md similarity index 97% rename from docs/examples/compose/keycloak/README.md rename to deploy/docker/examples/keycloak/README.md index 5f205b023a..b214d92a8a 100644 --- a/docs/examples/compose/keycloak/README.md +++ b/deploy/docker/examples/keycloak/README.md @@ -9,7 +9,7 @@ ```bash mkdir keycloak -curl -o keycloak/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/keycloak/compose.yaml +curl -o keycloak/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/deploy/docker/examples/keycloak/compose.yaml curl -o keycloak/env.d/kc_postgresql https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/kc_postgresql curl -o keycloak/env.d/keycloak https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/keycloak ``` diff --git a/docs/examples/compose/keycloak/compose.yaml b/deploy/docker/examples/keycloak/compose.yaml similarity index 100% rename from docs/examples/compose/keycloak/compose.yaml rename to deploy/docker/examples/keycloak/compose.yaml diff --git a/docs/examples/compose/minio/README.md b/deploy/docker/examples/minio/README.md similarity index 97% rename from docs/examples/compose/minio/README.md rename to deploy/docker/examples/minio/README.md index 46a16895c6..0db36c64e5 100644 --- a/docs/examples/compose/minio/README.md +++ b/deploy/docker/examples/minio/README.md @@ -9,7 +9,7 @@ ```bash mkdir minio -curl -o minio/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/minio/compose.yaml +curl -o minio/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/deploy/docker/examples/minio/compose.yaml ``` ### Step 2:. Update compose file with your own values diff --git a/docs/examples/compose/minio/compose.yaml b/deploy/docker/examples/minio/compose.yaml similarity index 100% rename from docs/examples/compose/minio/compose.yaml rename to deploy/docker/examples/minio/compose.yaml diff --git a/docs/examples/compose/nginx-proxy/README.md b/deploy/docker/examples/nginx-proxy/README.md similarity index 94% rename from docs/examples/compose/nginx-proxy/README.md rename to deploy/docker/examples/nginx-proxy/README.md index ad2acd611d..e460f62440 100644 --- a/docs/examples/compose/nginx-proxy/README.md +++ b/deploy/docker/examples/nginx-proxy/README.md @@ -13,7 +13,7 @@ Acme-companion is a lightweight companion container for nginx-proxy. It handles ```bash mkdir nginx-proxy -curl -o nginx-proxy/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/nginx-proxy/compose.yaml +curl -o nginx-proxy/compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/deploy/docker/examples/nginx-proxy/compose.yaml ``` ### Step 2: Edit `DEFAULT_EMAIL` in the compose file. diff --git a/docs/examples/compose/nginx-proxy/compose.yaml b/deploy/docker/examples/nginx-proxy/compose.yaml similarity index 100% rename from docs/examples/compose/nginx-proxy/compose.yaml rename to deploy/docker/examples/nginx-proxy/compose.yaml diff --git a/src/helm/env.d/dev/configuration/theme/demo.json b/deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json similarity index 100% rename from src/helm/env.d/dev/configuration/theme/demo.json rename to deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json diff --git a/src/helm/env.d/dev/values.dev-backend.yaml.gotmpl b/deploy/kubernetes/helm/env.d/dev/values.dev-backend.yaml.gotmpl similarity index 100% rename from src/helm/env.d/dev/values.dev-backend.yaml.gotmpl rename to deploy/kubernetes/helm/env.d/dev/values.dev-backend.yaml.gotmpl diff --git a/src/helm/env.d/dev/values.impress.yaml.gotmpl b/deploy/kubernetes/helm/env.d/dev/values.impress.yaml.gotmpl similarity index 100% rename from src/helm/env.d/dev/values.impress.yaml.gotmpl rename to deploy/kubernetes/helm/env.d/dev/values.impress.yaml.gotmpl diff --git a/src/helm/env.d/feature/values.dev-backend.yaml.gotmpl b/deploy/kubernetes/helm/env.d/feature/values.dev-backend.yaml.gotmpl similarity index 100% rename from src/helm/env.d/feature/values.dev-backend.yaml.gotmpl rename to deploy/kubernetes/helm/env.d/feature/values.dev-backend.yaml.gotmpl diff --git a/src/helm/env.d/feature/values.impress.yaml.gotmpl b/deploy/kubernetes/helm/env.d/feature/values.impress.yaml.gotmpl similarity index 100% rename from src/helm/env.d/feature/values.impress.yaml.gotmpl rename to deploy/kubernetes/helm/env.d/feature/values.impress.yaml.gotmpl diff --git a/src/helm/helmfile.yaml.gotmpl b/deploy/kubernetes/helm/helmfile.yaml.gotmpl similarity index 100% rename from src/helm/helmfile.yaml.gotmpl rename to deploy/kubernetes/helm/helmfile.yaml.gotmpl diff --git a/src/helm/impress/Chart.yaml b/deploy/kubernetes/helm/impress/Chart.yaml similarity index 100% rename from src/helm/impress/Chart.yaml rename to deploy/kubernetes/helm/impress/Chart.yaml diff --git a/src/helm/impress/README.md b/deploy/kubernetes/helm/impress/README.md similarity index 100% rename from src/helm/impress/README.md rename to deploy/kubernetes/helm/impress/README.md diff --git a/src/helm/impress/generate-readme.sh b/deploy/kubernetes/helm/impress/generate-readme.sh similarity index 100% rename from src/helm/impress/generate-readme.sh rename to deploy/kubernetes/helm/impress/generate-readme.sh diff --git a/src/helm/impress/templates/_helpers.tpl b/deploy/kubernetes/helm/impress/templates/_helpers.tpl similarity index 100% rename from src/helm/impress/templates/_helpers.tpl rename to deploy/kubernetes/helm/impress/templates/_helpers.tpl diff --git a/src/helm/impress/templates/backend_cronjob_list.yaml b/deploy/kubernetes/helm/impress/templates/backend_cronjob_list.yaml similarity index 100% rename from src/helm/impress/templates/backend_cronjob_list.yaml rename to deploy/kubernetes/helm/impress/templates/backend_cronjob_list.yaml diff --git a/src/helm/impress/templates/backend_deployment.yaml b/deploy/kubernetes/helm/impress/templates/backend_deployment.yaml similarity index 100% rename from src/helm/impress/templates/backend_deployment.yaml rename to deploy/kubernetes/helm/impress/templates/backend_deployment.yaml diff --git a/src/helm/impress/templates/backend_job.yml b/deploy/kubernetes/helm/impress/templates/backend_job.yml similarity index 100% rename from src/helm/impress/templates/backend_job.yml rename to deploy/kubernetes/helm/impress/templates/backend_job.yml diff --git a/src/helm/impress/templates/backend_job_createsuperuser.yaml b/deploy/kubernetes/helm/impress/templates/backend_job_createsuperuser.yaml similarity index 100% rename from src/helm/impress/templates/backend_job_createsuperuser.yaml rename to deploy/kubernetes/helm/impress/templates/backend_job_createsuperuser.yaml diff --git a/src/helm/impress/templates/backend_job_migrate.yaml b/deploy/kubernetes/helm/impress/templates/backend_job_migrate.yaml similarity index 100% rename from src/helm/impress/templates/backend_job_migrate.yaml rename to deploy/kubernetes/helm/impress/templates/backend_job_migrate.yaml diff --git a/src/helm/impress/templates/backend_svc.yaml b/deploy/kubernetes/helm/impress/templates/backend_svc.yaml similarity index 100% rename from src/helm/impress/templates/backend_svc.yaml rename to deploy/kubernetes/helm/impress/templates/backend_svc.yaml diff --git a/src/helm/impress/templates/celery_worker_deployment.yaml b/deploy/kubernetes/helm/impress/templates/celery_worker_deployment.yaml similarity index 100% rename from src/helm/impress/templates/celery_worker_deployment.yaml rename to deploy/kubernetes/helm/impress/templates/celery_worker_deployment.yaml diff --git a/src/helm/impress/templates/docspec_deployment.yaml b/deploy/kubernetes/helm/impress/templates/docspec_deployment.yaml similarity index 100% rename from src/helm/impress/templates/docspec_deployment.yaml rename to deploy/kubernetes/helm/impress/templates/docspec_deployment.yaml diff --git a/src/helm/impress/templates/docspec_svc.yaml b/deploy/kubernetes/helm/impress/templates/docspec_svc.yaml similarity index 100% rename from src/helm/impress/templates/docspec_svc.yaml rename to deploy/kubernetes/helm/impress/templates/docspec_svc.yaml diff --git a/src/helm/impress/templates/frontend_deployment.yaml b/deploy/kubernetes/helm/impress/templates/frontend_deployment.yaml similarity index 100% rename from src/helm/impress/templates/frontend_deployment.yaml rename to deploy/kubernetes/helm/impress/templates/frontend_deployment.yaml diff --git a/src/helm/impress/templates/frontend_svc.yaml b/deploy/kubernetes/helm/impress/templates/frontend_svc.yaml similarity index 100% rename from src/helm/impress/templates/frontend_svc.yaml rename to deploy/kubernetes/helm/impress/templates/frontend_svc.yaml diff --git a/src/helm/impress/templates/ingress-redirects.yaml b/deploy/kubernetes/helm/impress/templates/ingress-redirects.yaml similarity index 100% rename from src/helm/impress/templates/ingress-redirects.yaml rename to deploy/kubernetes/helm/impress/templates/ingress-redirects.yaml diff --git a/src/helm/impress/templates/ingress.yaml b/deploy/kubernetes/helm/impress/templates/ingress.yaml similarity index 100% rename from src/helm/impress/templates/ingress.yaml rename to deploy/kubernetes/helm/impress/templates/ingress.yaml diff --git a/src/helm/impress/templates/ingress_admin.yaml b/deploy/kubernetes/helm/impress/templates/ingress_admin.yaml similarity index 100% rename from src/helm/impress/templates/ingress_admin.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_admin.yaml diff --git a/src/helm/impress/templates/ingress_collaboration_api.yaml b/deploy/kubernetes/helm/impress/templates/ingress_collaboration_api.yaml similarity index 100% rename from src/helm/impress/templates/ingress_collaboration_api.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_collaboration_api.yaml diff --git a/src/helm/impress/templates/ingress_collaboration_ws.yaml b/deploy/kubernetes/helm/impress/templates/ingress_collaboration_ws.yaml similarity index 100% rename from src/helm/impress/templates/ingress_collaboration_ws.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_collaboration_ws.yaml diff --git a/src/helm/impress/templates/ingress_media.yaml b/deploy/kubernetes/helm/impress/templates/ingress_media.yaml similarity index 100% rename from src/helm/impress/templates/ingress_media.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_media.yaml diff --git a/src/helm/impress/templates/ingress_posthog.yaml b/deploy/kubernetes/helm/impress/templates/ingress_posthog.yaml similarity index 100% rename from src/helm/impress/templates/ingress_posthog.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_posthog.yaml diff --git a/src/helm/impress/templates/ingress_posthog_assets.yaml b/deploy/kubernetes/helm/impress/templates/ingress_posthog_assets.yaml similarity index 100% rename from src/helm/impress/templates/ingress_posthog_assets.yaml rename to deploy/kubernetes/helm/impress/templates/ingress_posthog_assets.yaml diff --git a/src/helm/impress/templates/media_svc.yaml b/deploy/kubernetes/helm/impress/templates/media_svc.yaml similarity index 100% rename from src/helm/impress/templates/media_svc.yaml rename to deploy/kubernetes/helm/impress/templates/media_svc.yaml diff --git a/src/helm/impress/templates/posthog_assets_svc.yaml b/deploy/kubernetes/helm/impress/templates/posthog_assets_svc.yaml similarity index 100% rename from src/helm/impress/templates/posthog_assets_svc.yaml rename to deploy/kubernetes/helm/impress/templates/posthog_assets_svc.yaml diff --git a/src/helm/impress/templates/posthog_svc.yaml b/deploy/kubernetes/helm/impress/templates/posthog_svc.yaml similarity index 100% rename from src/helm/impress/templates/posthog_svc.yaml rename to deploy/kubernetes/helm/impress/templates/posthog_svc.yaml diff --git a/src/helm/impress/templates/theme_customization_file_cm.yaml b/deploy/kubernetes/helm/impress/templates/theme_customization_file_cm.yaml similarity index 100% rename from src/helm/impress/templates/theme_customization_file_cm.yaml rename to deploy/kubernetes/helm/impress/templates/theme_customization_file_cm.yaml diff --git a/src/helm/impress/templates/yprovider_deployment.yaml b/deploy/kubernetes/helm/impress/templates/yprovider_deployment.yaml similarity index 100% rename from src/helm/impress/templates/yprovider_deployment.yaml rename to deploy/kubernetes/helm/impress/templates/yprovider_deployment.yaml diff --git a/src/helm/impress/templates/yprovider_deployment_converter.yaml b/deploy/kubernetes/helm/impress/templates/yprovider_deployment_converter.yaml similarity index 100% rename from src/helm/impress/templates/yprovider_deployment_converter.yaml rename to deploy/kubernetes/helm/impress/templates/yprovider_deployment_converter.yaml diff --git a/src/helm/impress/templates/yprovider_svc.yaml b/deploy/kubernetes/helm/impress/templates/yprovider_svc.yaml similarity index 100% rename from src/helm/impress/templates/yprovider_svc.yaml rename to deploy/kubernetes/helm/impress/templates/yprovider_svc.yaml diff --git a/src/helm/impress/templates/yprovider_svc_converter.yaml b/deploy/kubernetes/helm/impress/templates/yprovider_svc_converter.yaml similarity index 100% rename from src/helm/impress/templates/yprovider_svc_converter.yaml rename to deploy/kubernetes/helm/impress/templates/yprovider_svc_converter.yaml diff --git a/src/helm/impress/values.yaml b/deploy/kubernetes/helm/impress/values.yaml similarity index 100% rename from src/helm/impress/values.yaml rename to deploy/kubernetes/helm/impress/values.yaml diff --git a/deploy/paas/README.md b/deploy/paas/README.md new file mode 100644 index 0000000000..bb4d7dac65 --- /dev/null +++ b/deploy/paas/README.md @@ -0,0 +1,31 @@ +# Docs — PaaS deployment + +Buildpack scripts and nginx template for deploying [Docs](https://github.com/suitenumerique/docs) on a PaaS (Scalingo, Clever Cloud, …). + +## Layout + +``` +deploy/paas/ +├── buildpack_postcompile.sh # Build-time: cleanup unused files to reduce slug size +├── buildpack_postfrontend.sh # Build-time: assemble frontend/backend/nginx into slug +├── buildpack_start.sh # Runtime: starts uvicorn + y-provider + nginx +└── servers.conf.erb # Nginx routing template (ERB → consumed by buildpack) +``` + +The root `Procfile` references `deploy/paas/buildpack_start.sh` for the `web` process. + +## Getting started + +See the [Scalingo walkthrough](../../docs/installation/scalingo.md) — it covers the full env-var setup (buildpack URL, OIDC, S3, theme customization, etc.). + +The two build-time hooks are wired via the [La Suite buildpack](https://github.com/suitenumerique/buildpack) env vars: + +```bash +scalingo env-set LASUITE_SCRIPT_POSTCOMPILE="deploy/paas/buildpack_postcompile.sh" +scalingo env-set LASUITE_SCRIPT_POSTFRONTEND="deploy/paas/buildpack_postfrontend.sh" +``` + +## Other deployments + +* Docker Compose: see [`deploy/docker/`](../docker/). +* Kubernetes/Helm: see [`deploy/kubernetes/`](../kubernetes/). diff --git a/bin/buildpack_postcompile.sh b/deploy/paas/buildpack_postcompile.sh similarity index 100% rename from bin/buildpack_postcompile.sh rename to deploy/paas/buildpack_postcompile.sh diff --git a/bin/buildpack_postfrontend.sh b/deploy/paas/buildpack_postfrontend.sh similarity index 98% rename from bin/buildpack_postfrontend.sh rename to deploy/paas/buildpack_postfrontend.sh index 2c389dc916..acc10f7f96 100755 --- a/bin/buildpack_postfrontend.sh +++ b/deploy/paas/buildpack_postfrontend.sh @@ -45,7 +45,7 @@ if [ -n "$THEME_CUSTOMIZATION_LOGO_URL" ]; then fi mv src/backend/* ./ -mv deploy/paas/* ./ +mv deploy/paas/servers.conf.erb ./ # Inject custom theme JSON if [ -n "$THEME_CUSTOMIZATION_JSON" ]; then diff --git a/bin/buildpack_start.sh b/deploy/paas/buildpack_start.sh similarity index 100% rename from bin/buildpack_start.sh rename to deploy/paas/buildpack_start.sh diff --git a/docs/customization.md b/docs/customization.md index ee19200310..c87f35589c 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -89,6 +89,28 @@ Then, set the `FRONTEND_JS_URL` environment variable to the URL of your custom J ---- +## **Providing theme customization** ⚙️ + +The theme customization configuration can be provided in two ways. Pick the one that fits your deployment: + +* **Inline JSON via env var (recommended for PaaS / Docker / Kubernetes)** — set `THEME_CUSTOMIZATION_JSON` to the JSON string itself. No file mount required. + + ```shellscript + THEME_CUSTOMIZATION_JSON='{"header":{"icon":"https://..."}}' + ``` + +* **Mounted JSON file** — set `THEME_CUSTOMIZATION_FILE_PATH` to the absolute path of a JSON file inside the container. + + ```shellscript + THEME_CUSTOMIZATION_FILE_PATH= + ``` + +When both are set, `THEME_CUSTOMIZATION_JSON` is prioritized. + +The sections below describe the schema. Each example links to the reference JSON at `deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json`. + +---- + ## **Your Docs icon** 📝 You can add your own Docs icon in the header from the theme customization file. @@ -97,11 +119,13 @@ You can add your own Docs icon in the header from the theme customization file. ```shellscript THEME_CUSTOMIZATION_FILE_PATH= +# or +THEME_CUSTOMIZATION_JSON= ``` ### Example of JSON -You can activate it with the `header.icon` configuration: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json +You can activate it with the `header.icon` configuration: https://github.com/suitenumerique/docs/blob/main/deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json This configuration is optional. If not set, the default icon will be used. @@ -119,7 +143,7 @@ THEME_CUSTOMIZATION_FILE_PATH= ### Example of JSON -The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json +The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json `footer.default` is the fallback if the language is not supported. @@ -142,7 +166,7 @@ THEME_CUSTOMIZATION_FILE_PATH= ### Example of JSON -The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json +The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json ---- @@ -168,7 +192,7 @@ See: [LaGaufreV2Props](https://github.com/suitenumerique/ui-kit/blob/main/src/co ### Complete Example -From the theme customization file: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json +From the theme customization file: https://github.com/suitenumerique/docs/blob/main/deploy/kubernetes/helm/env.d/dev/configuration/theme/demo.json ### Behavior diff --git a/docs/env.md b/docs/env.md index a559e28cfc..244d62d5c7 100644 --- a/docs/env.md +++ b/docs/env.md @@ -138,6 +138,7 @@ These are the environment variables you can set for the `impress-backend` contai | STORAGES_STATICFILES_BACKEND | | whitenoise.storage.CompressedManifestStaticFilesStorage | | THEME_CUSTOMIZATION_CACHE_TIMEOUT | Cache duration for the customization settings | 86400 | | THEME_CUSTOMIZATION_FILE_PATH | Full path to the file customizing the theme. An example is provided in src/backend/impress/configuration/theme/default.json | BASE_DIR/impress/configuration/theme/default.json | +| THEME_CUSTOMIZATION_JSON | Inline JSON string customizing the theme. Takes precedence over `THEME_CUSTOMIZATION_FILE_PATH` when set. Convenient for PaaS / Docker / Kubernetes deployments. | | | TRASHBIN_CUTOFF_DAYS | Trashbin cutoff | 30 | | TREEBEARD_PATH_COMPUTE_RETRY_MAX_ATTEMPTS | Number of attempts to create a document before failing. | 10 | | USER_OIDC_ESSENTIAL_CLAIMS | Essential claims in OIDC token | [] | diff --git a/docs/installation/compose.md b/docs/installation/compose.md index 014f6e5a17..e928a8b328 100644 --- a/docs/installation/compose.md +++ b/docs/installation/compose.md @@ -6,10 +6,10 @@ We provide a sample configuration for running Docs using Docker Compose. Please - A modern version of Docker and its Compose plugin. - A domain name and DNS configured to your server. -- An Identity Provider that supports OpenID Connect protocol - we provide [an example to deploy Keycloak](../examples/compose/keycloak/README.md). -- An Object Storage that implements S3 API - we provide [an example to deploy Minio](../examples/compose/minio/README.md). -- A Postgresql database - we provide [an example in the compose file](../examples/compose/compose.yaml). -- A Redis database - we provide [an example in the compose file](../examples/compose/compose.yaml). +- An Identity Provider that supports OpenID Connect protocol - we provide [an example to deploy Keycloak](../../deploy/docker/examples/keycloak/README.md). +- An Object Storage that implements S3 API - we provide [an example to deploy Minio](../../deploy/docker/examples/minio/README.md). +- A Postgresql database - we provide [an example in the compose file](../../deploy/docker/compose.yml). +- A Redis database - we provide [an example in the compose file](../../deploy/docker/compose.yml). ## Software Requirements @@ -32,7 +32,7 @@ For older versions of Docker Engine that do not include Docker Compose: ```bash mkdir -p docs/env.d cd docs -curl -o compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/compose.yaml +curl -o compose.yml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/deploy/docker/compose.yml curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/common curl -o env.d/backend https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/backend curl -o env.d/yprovider https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/yprovider @@ -61,7 +61,7 @@ In this example, we assume the following services: Authentication in Docs is managed through Open ID Connect protocol. A functional Identity Provider implementing this protocol is required. -For guidance, refer to our [Keycloak deployment example](../examples/compose/keycloak/README.md). +For guidance, refer to our [Keycloak deployment example](../../deploy/docker/examples/keycloak/README.md). If using Keycloak as your Identity Provider, set `OIDC_RP_CLIENT_ID` and `OIDC_RP_CLIENT_SECRET` variables with those of the OIDC client created for Docs. By default we have set `docs` as the realm name, if you have named your realm differently, update the value `REALM_NAME` in `env.d/common` @@ -71,7 +71,7 @@ For others OIDC providers, update the variables in `env.d/backend`. Files and media are stored in an Object Store that supports the S3 API. -For guidance, refer to our [Minio deployment example](../examples/compose/minio/README.md). +For guidance, refer to our [Minio deployment example](../../deploy/docker/examples/minio/README.md). Set `AWS_S3_ACCESS_KEY_ID` and `AWS_S3_SECRET_ACCESS_KEY` with the credentials of a user with `readwrite` access to the bucket created for Docs. @@ -147,7 +147,7 @@ AI_MODEL= e.g. llama ### Frontend theme -You can [customize your Docs instance](../theming.md) with your own theme and custom css. +You can [customize your Docs instance](../customization.md) with your own theme and custom css. The following environment variables must be set in `env.d/backend`: @@ -163,7 +163,7 @@ FRONTEND_CSS_URL=https://storage.yourdomain.tld/themes/custom.css # custom css If you have your own certificates and proxy setup, you can skip this part. -You can follow our [nginx proxy example](../examples/compose/nginx-proxy/README.md) with automatic generation and renewal of certificate with Let's Encrypt. +You can follow our [nginx proxy example](../../deploy/docker/examples/nginx-proxy/README.md) with automatic generation and renewal of certificate with Let's Encrypt. You will need to uncomment the environment and network sections in compose file and update it with your values. diff --git a/docs/installation/scalingo.md b/docs/installation/scalingo.md index 564ccf5d11..a0445182f1 100644 --- a/docs/installation/scalingo.md +++ b/docs/installation/scalingo.md @@ -39,8 +39,8 @@ scalingo env-set LASUITE_APP_NAME="docs" scalingo env-set LASUITE_BACKEND_DIR="src/backend/" scalingo env-set LASUITE_FRONTEND_DIR="src/frontend/" scalingo env-set LASUITE_NGINX_DIR="." -scalingo env-set LASUITE_SCRIPT_POSTCOMPILE="bin/buildpack_postcompile.sh" -scalingo env-set LASUITE_SCRIPT_POSTFRONTEND="bin/buildpack_postfrontend.sh" +scalingo env-set LASUITE_SCRIPT_POSTCOMPILE="deploy/paas/buildpack_postcompile.sh" +scalingo env-set LASUITE_SCRIPT_POSTFRONTEND="deploy/paas/buildpack_postfrontend.sh" ``` ### Database and Cache @@ -237,12 +237,12 @@ On Scalingo, the application runs as follows: 1. The buildpack compiles the frontend (Next.js static export) 2. The buildpack compiles the backend (Python dependencies) -3. `bin/buildpack_postcompile.sh` runs to clean up unused files and reduce slug size -4. `bin/buildpack_postfrontend.sh` moves the frontend build to `build/frontend-out`, downloads custom logos, injects the custom theme, and prepares the deployment structure +3. `deploy/paas/buildpack_postcompile.sh` runs to clean up unused files and reduce slug size +4. `deploy/paas/buildpack_postfrontend.sh` moves the frontend build to `build/frontend-out`, downloads custom logos, injects the custom theme, and prepares the deployment structure ### Runtime -The `bin/buildpack_start.sh` script starts three processes: +The `deploy/paas/buildpack_start.sh` script starts three processes: - **Nginx** serves static files and proxies requests to the backend - **uvicorn** runs the Django ASGI application on port 8000 diff --git a/docs/release.md b/docs/release.md index 2364c10aa6..b46d673a7f 100644 --- a/docs/release.md +++ b/docs/release.md @@ -7,7 +7,7 @@ Whenever we are cooking a new release (e.g. `4.18.1`) we should follow a standar - for backend, update the version number by hand in `pyproject.toml`, - for each projects (`src/frontend`, `src/frontend/apps/*`, `src/frontend/packages/*`, `src/mail`), run `yarn version --new-version --no-git-tag-version 4.18.1` in their directory. This will update their `package.json` for you, - - for Helm, update Docker image tag in files located at `src/helm/env.d` for both `preprod` and `production` environments: + - for Helm, update Docker image tag in files located at `deploy/kubernetes/helm/env.d` for both `preprod` and `production` environments: ```yaml image: diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 4dfa314655..09ae6595f3 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -3102,6 +3102,25 @@ def get(self, request): return drf.response.Response(dict_settings) def _load_theme_customization(self): + if settings.THEME_CUSTOMIZATION_JSON: + cache_key = "theme_customization_env" + theme_customization = cache.get(cache_key, {}) + if theme_customization: + return theme_customization + + try: + theme_customization = json.loads(settings.THEME_CUSTOMIZATION_JSON) + except json.JSONDecodeError: + logger.error("THEME_CUSTOMIZATION_JSON is not a valid JSON") + return {} + + cache.set( + cache_key, + theme_customization, + settings.THEME_CUSTOMIZATION_CACHE_TIMEOUT, + ) + return theme_customization + if not settings.THEME_CUSTOMIZATION_FILE_PATH: return {} diff --git a/src/backend/core/tests/test_api_config.py b/src/backend/core/tests/test_api_config.py index 571af85ab9..f1c8e0d7d5 100644 --- a/src/backend/core/tests/test_api_config.py +++ b/src/backend/core/tests/test_api_config.py @@ -5,6 +5,7 @@ import json from unittest.mock import patch +from django.core.cache import cache from django.test import override_settings import pytest @@ -199,6 +200,67 @@ def test_api_config_with_original_theme_customization(is_authenticated, settings assert content["theme_customization"] == theme_customization +@override_settings( + THEME_CUSTOMIZATION_JSON=json.dumps( + {"colors": {"primary": "#abcdef", "secondary": "#fedcba"}} + ), +) +@pytest.mark.parametrize("is_authenticated", [False, True]) +def test_api_config_with_theme_customization_from_env_var(is_authenticated): + """Theme customization can be provided inline via THEME_CUSTOMIZATION_JSON.""" + cache.clear() + client = APIClient() + + if is_authenticated: + user = factories.UserFactory() + client.force_login(user) + + response = client.get("/api/v1.0/config/") + assert response.status_code == HTTP_200_OK + assert response.json()["theme_customization"] == { + "colors": {"primary": "#abcdef", "secondary": "#fedcba"}, + } + + +@override_settings(THEME_CUSTOMIZATION_JSON="not a json") +@pytest.mark.parametrize("is_authenticated", [False, True]) +def test_api_config_with_invalid_theme_customization_env_var(is_authenticated): + """An invalid THEME_CUSTOMIZATION_JSON value should yield an empty dict.""" + cache.clear() + client = APIClient() + + if is_authenticated: + user = factories.UserFactory() + client.force_login(user) + + response = client.get("/api/v1.0/config/") + assert response.status_code == HTTP_200_OK + assert response.json()["theme_customization"] == {} + + +@override_settings( + THEME_CUSTOMIZATION_JSON=json.dumps({"from": "env"}), + THEME_CUSTOMIZATION_FILE_PATH="/configuration/theme/default.json", +) +@pytest.mark.parametrize("is_authenticated", [False, True]) +def test_api_config_theme_customization_env_var_takes_precedence(is_authenticated, fs): + """THEME_CUSTOMIZATION_JSON takes precedence over THEME_CUSTOMIZATION_FILE_PATH.""" + cache.clear() + fs.create_file( + "/configuration/theme/default.json", + contents=json.dumps({"from": "file"}), + ) + client = APIClient() + + if is_authenticated: + user = factories.UserFactory() + client.force_login(user) + + response = client.get("/api/v1.0/config/") + assert response.status_code == HTTP_200_OK + assert response.json()["theme_customization"] == {"from": "env"} + + def test_api_config_throttling(settings): """Test api config throttling.""" current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["config"] diff --git a/src/backend/impress/settings.py b/src/backend/impress/settings.py index 991dfae06c..38a65de85a 100755 --- a/src/backend/impress/settings.py +++ b/src/backend/impress/settings.py @@ -551,6 +551,12 @@ class Base(Configuration): environ_prefix=None, ) + THEME_CUSTOMIZATION_JSON = values.Value( + None, + environ_name="THEME_CUSTOMIZATION_JSON", + environ_prefix=None, + ) + THEME_CUSTOMIZATION_CACHE_TIMEOUT = values.IntegerValue( 60 * 60 * 24, environ_name="THEME_CUSTOMIZATION_CACHE_TIMEOUT",