From 11a599de1b935655843e89140fb1fdbf50a2699a Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sun, 16 Aug 2026 21:44:46 +0800 Subject: [PATCH 1/4] feat(developer-portal-fe): render config.yaml from a pass-through values block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chart hand-wrote config.yaml and covered only 7 fields of the Developer Portal config schema, with app.name pinned to a literal. Application name and description, email-and-password auth, generic OAuth providers, db.ssl/schema/ pool and the rest were unreachable from Helm. Render config.yaml from developerPortal.config instead, so any field the application schema accepts is settable without a chart release. The connection settings the chart owns — portal.url, the Secret-backed ${PORTAL_TOKEN} / ${DB_URL} / ${AUTH_SECRET} placeholders, app.baseURL and app.trustedOrigins — are merged last and always win, so credentials cannot be moved into the ConfigMap by setting them there and an untouched values.yaml renders the same config as before. Credentials belong in ${VAR} placeholders plus developerPortal.extraEnvVars; the application substitutes them across every string value at startup. Both are documented in the chart README. Also add the chart to `ct lint` with a ci values file that exercises the whole config surface, so a template change that breaks the pass-through fails CI. --- .github/workflows/ci.yaml | 1 + charts/developer-portal-fe/.helmignore | 2 + charts/developer-portal-fe/Chart.yaml | 2 +- charts/developer-portal-fe/README.md | 104 +++++++++++++++- charts/developer-portal-fe/README.md.gotmpl | 111 ++++++++++++++++++ .../ci/full-config-values.yaml | 82 +++++++++++++ .../templates/configmap.yaml | 33 +++--- charts/developer-portal-fe/values.yaml | 31 +++++ 8 files changed, 344 insertions(+), 22 deletions(-) create mode 100644 charts/developer-portal-fe/README.md.gotmpl create mode 100644 charts/developer-portal-fe/ci/full-config-values.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b28ef922..0f3efd53 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -39,6 +39,7 @@ jobs: --charts charts/ingress-controller \ --charts charts/aisix-cp \ --charts charts/aisix \ + --charts charts/developer-portal-fe \ --charts charts/ngxdig' - name: Verify Chart.lock files diff --git a/charts/developer-portal-fe/.helmignore b/charts/developer-portal-fe/.helmignore index 0e8a0eb3..879b3670 100644 --- a/charts/developer-portal-fe/.helmignore +++ b/charts/developer-portal-fe/.helmignore @@ -21,3 +21,5 @@ .idea/ *.tmproj .vscode/ +README.md.gotmpl +ci/ diff --git a/charts/developer-portal-fe/Chart.yaml b/charts/developer-portal-fe/Chart.yaml index fc992f66..e4948b67 100644 --- a/charts/developer-portal-fe/Chart.yaml +++ b/charts/developer-portal-fe/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: developer-portal-fe description: A Helm chart for API7 Developer Portal Frontend type: application -version: 0.1.0 +version: 0.2.0 appVersion: "0.5.7" maintainers: - name: API7 diff --git a/charts/developer-portal-fe/README.md b/charts/developer-portal-fe/README.md index 77891072..001e9db1 100644 --- a/charts/developer-portal-fe/README.md +++ b/charts/developer-portal-fe/README.md @@ -1,6 +1,6 @@ # developer-portal-fe -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.7](https://img.shields.io/badge/AppVersion-0.5.7-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.7](https://img.shields.io/badge/AppVersion-0.5.7-informational?style=flat-square) A Helm chart for API7 Developer Portal Frontend @@ -16,6 +16,106 @@ A Helm chart for API7 Developer Portal Frontend |------------|------|---------| | https://charts.bitnami.com/bitnami | postgresql | 12.12.10 | +## Install + +```sh +helm repo add api7 https://charts.api7.ai +helm repo update + +helm install developer-portal-fe api7/developer-portal-fe --namespace api7 --create-namespace +``` + +## Configuring the portal application + +The chart renders the application's `config.yaml` from `developerPortal.config`, +which is passed through verbatim. Any field the application's config schema +accepts can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, +`auth.genericOAuthProviders`, `db.ssl`, and so on — without waiting for a chart +release. Which fields exist depends on the deployed application version +(`developerPortal.image.tag`). + +The connection settings stay with the chart and are merged in last, so they +always win over `developerPortal.config`: + +| Config path | Comes from | +| :--- | :--- | +| `portal.url` | `portal.url` | +| `portal.token` | `${PORTAL_TOKEN}` — Secret (`portal.existingSecret`) | +| `db.url` | `${DB_URL}` — Secret (`db.existingSecret`) | +| `auth.secret` | `${AUTH_SECRET}` — Secret (`auth.existingSecret`) | +| `app.baseURL`, `app.trustedOrigins` | `app.baseURL`, `app.trustedOrigins` | + +Changing `developerPortal.config` rolls the Pods automatically — the Deployment +carries a checksum of the rendered ConfigMap. + +### Keep credentials out of the ConfigMap + +`developerPortal.config` is rendered into a ConfigMap, so it is the wrong place +for a client secret or a database password. The application substitutes +`${VAR}` and `${VAR:default}` in every string value of `config.yaml` at startup, +so put a placeholder in the config and inject the real value as an environment +variable with `developerPortal.extraEnvVars`: + +```yaml +developerPortal: + config: + auth: + genericOAuthProviders: + - providerId: keycloak + discoveryUrl: https://sso.example.com/realms/main/.well-known/openid-configuration + clientId: devportal + clientSecret: ${OIDC_CLIENT_SECRET} + extraEnvVars: + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: devportal-oidc + key: client-secret +``` + +A missing variable is a startup error, not an empty string — the Pod fails +fast instead of running with a blank secret. + +### Examples + +Name and describe the portal: + +```yaml +developerPortal: + config: + app: + name: "Example APIs" + desc: "Everything you need to build on Example." +``` + +Turn off password sign-in and require two-factor authentication: + +```yaml +developerPortal: + config: + auth: + emailAndPassword: + enabled: false + twoFactor: + enabled: true + required: true +``` + +Connect to a managed PostgreSQL that requires TLS, in its own schema: + +```yaml +developerPortal: + config: + db: + schema: portal + ssl: + rejectUnauthorized: true + ca: ${DB_CA_PEM} + pool: + max: 30 + min: 2 +``` + ## Values | Key | Type | Default | Description | @@ -29,6 +129,7 @@ A Helm chart for API7 Developer Portal Frontend | db.existingSecret | string | `""` | | | db.existingSecretKey | string | `"db-url"` | | | db.url | string | `"postgres://portal:portal123@developer-portal-fe-postgresql:5432/portal"` | | +| developerPortal.config | object | `{"app":{"name":"Developer Portal"}}` | Developer Portal application config, rendered verbatim into `config.yaml` | | developerPortal.extraEnvVars | list | `[]` | | | developerPortal.extraVolumeMounts | list | `[]` | | | developerPortal.extraVolumes | list | `[]` | | @@ -88,4 +189,3 @@ A Helm chart for API7 Developer Portal Frontend | serviceAccount.name | string | `""` | | | tolerations | list | `[]` | | | topologySpreadConstraints | list | `[]` | | - diff --git a/charts/developer-portal-fe/README.md.gotmpl b/charts/developer-portal-fe/README.md.gotmpl new file mode 100644 index 00000000..69154c22 --- /dev/null +++ b/charts/developer-portal-fe/README.md.gotmpl @@ -0,0 +1,111 @@ +{{ template "chart.header" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.requirementsSection" . }} + +## Install + +```sh +helm repo add api7 https://charts.api7.ai +helm repo update + +helm install developer-portal-fe api7/developer-portal-fe --namespace api7 --create-namespace +``` + +## Configuring the portal application + +The chart renders the application's `config.yaml` from `developerPortal.config`, +which is passed through verbatim. Any field the application's config schema +accepts can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, +`auth.genericOAuthProviders`, `db.ssl`, and so on — without waiting for a chart +release. Which fields exist depends on the deployed application version +(`developerPortal.image.tag`). + +The connection settings stay with the chart and are merged in last, so they +always win over `developerPortal.config`: + +| Config path | Comes from | +| :--- | :--- | +| `portal.url` | `portal.url` | +| `portal.token` | `${PORTAL_TOKEN}` — Secret (`portal.existingSecret`) | +| `db.url` | `${DB_URL}` — Secret (`db.existingSecret`) | +| `auth.secret` | `${AUTH_SECRET}` — Secret (`auth.existingSecret`) | +| `app.baseURL`, `app.trustedOrigins` | `app.baseURL`, `app.trustedOrigins` | + +Changing `developerPortal.config` rolls the Pods automatically — the Deployment +carries a checksum of the rendered ConfigMap. + +### Keep credentials out of the ConfigMap + +`developerPortal.config` is rendered into a ConfigMap, so it is the wrong place +for a client secret or a database password. The application substitutes +`${VAR}` and `${VAR:default}` in every string value of `config.yaml` at startup, +so put a placeholder in the config and inject the real value as an environment +variable with `developerPortal.extraEnvVars`: + +```yaml +developerPortal: + config: + auth: + genericOAuthProviders: + - providerId: keycloak + discoveryUrl: https://sso.example.com/realms/main/.well-known/openid-configuration + clientId: devportal + clientSecret: ${OIDC_CLIENT_SECRET} + extraEnvVars: + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: devportal-oidc + key: client-secret +``` + +A missing variable is a startup error, not an empty string — the Pod fails +fast instead of running with a blank secret. + +### Examples + +Name and describe the portal: + +```yaml +developerPortal: + config: + app: + name: "Example APIs" + desc: "Everything you need to build on Example." +``` + +Turn off password sign-in and require two-factor authentication: + +```yaml +developerPortal: + config: + auth: + emailAndPassword: + enabled: false + twoFactor: + enabled: true + required: true +``` + +Connect to a managed PostgreSQL that requires TLS, in its own schema: + +```yaml +developerPortal: + config: + db: + schema: portal + ssl: + rejectUnauthorized: true + ca: ${DB_CA_PEM} + pool: + max: 30 + min: 2 +``` + +{{ template "chart.valuesSection" . }} diff --git a/charts/developer-portal-fe/ci/full-config-values.yaml b/charts/developer-portal-fe/ci/full-config-values.yaml new file mode 100644 index 00000000..a75ef795 --- /dev/null +++ b/charts/developer-portal-fe/ci/full-config-values.yaml @@ -0,0 +1,82 @@ +# Renders every part of the Developer Portal config surface through +# developerPortal.config, so a template change that breaks the pass-through is +# caught by `ct lint`. Credentials are ${ENV} placeholders on purpose — that is +# the documented way to keep them out of the ConfigMap. +portal: + url: "https://api7-developer-portal:4321" + +app: + baseURL: "https://portal.example.com" + trustedOrigins: + - "https://portal.example.com" + +developerPortal: + config: + db: + schema: portal + pool: + max: 30 + min: 2 + idleTimeout: 60000 + connectionTimeout: 5000 + allowExitOnIdle: false + ssl: + rejectUnauthorized: true + ca: ${DB_CA_PEM} + auth: + adminUserIds: + - usr_admin + session: + expiresIn: 604800 + updateAge: 86400 + emailAndPassword: + enabled: false + requireEmailVerification: true + revokeSessionsOnPasswordReset: true + twoFactor: + enabled: true + required: true + socialProviders: + github: + clientId: devportal + clientSecret: ${GITHUB_CLIENT_SECRET} + genericOAuthProviders: + - providerId: keycloak + discoveryUrl: https://sso.example.com/realms/main/.well-known/openid-configuration + clientId: devportal + clientSecret: ${OIDC_CLIENT_SECRET} + scopes: ["openid", "profile", "email"] + sso: + providers: + - providerId: keycloak + domains: + - '@example\.com$' + app: + name: "Example APIs" + desc: "Everything you need to build on Example." + signUpConsentLabel: 'I agree to the Terms.' + applicationDetail: + subscriptions: true + usage: false + credentialsTabs: + keyAuth: true + basicAuth: false + oauth: false + apiHub: + enabled: true + extraEnvVars: + - name: DB_CA_PEM + valueFrom: + secretKeyRef: + name: devportal-db-ca + key: ca.crt + - name: GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: devportal-github + key: client-secret + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: devportal-oidc + key: client-secret diff --git a/charts/developer-portal-fe/templates/configmap.yaml b/charts/developer-portal-fe/templates/configmap.yaml index 41cf3505..127b7ace 100644 --- a/charts/developer-portal-fe/templates/configmap.yaml +++ b/charts/developer-portal-fe/templates/configmap.yaml @@ -6,23 +6,18 @@ metadata: labels: {{- include "developer-portal-fe.labels" . | nindent 4 }} data: + {{- /* + developerPortal.config passes the whole application config schema through. + The connection settings the chart owns are merged last so they win over + anything set there — in particular the ${...} placeholders that keep the + portal token, database URL and auth secret in a Secret instead of here. + deepCopy is required: mergeOverwrite mutates its first argument in place. + */}} + {{- $owned := dict + "portal" (dict "url" .Values.portal.url "token" "${PORTAL_TOKEN}") + "db" (dict "url" "${DB_URL}") + "auth" (dict "secret" "${AUTH_SECRET}") + "app" (dict "baseURL" .Values.app.baseURL + "trustedOrigins" .Values.app.trustedOrigins) }} config.yaml: | - # Portal API connection configuration - portal: - url: {{ .Values.portal.url | quote }} - token: ${PORTAL_TOKEN} - - # Database configuration - db: - url: ${DB_URL} - - # Authentication configuration - auth: - secret: ${AUTH_SECRET} - - # Application configuration - app: - name: "Developer Portal" - baseURL: {{ .Values.app.baseURL | quote }} - trustedOrigins: - {{- toYaml .Values.app.trustedOrigins | nindent 8 }} + {{- toYaml (mergeOverwrite (deepCopy (.Values.developerPortal.config | default dict)) $owned) | nindent 4 }} diff --git a/charts/developer-portal-fe/values.yaml b/charts/developer-portal-fe/values.yaml index e32744c6..e8e9b0fb 100644 --- a/charts/developer-portal-fe/values.yaml +++ b/charts/developer-portal-fe/values.yaml @@ -11,6 +11,37 @@ developerPortal: pullPolicy: IfNotPresent tag: "v0.5.7" + # Any field the application's config schema accepts can be set here, so new + # application config does not need a chart release. See the chart README for + # the fields in common use and for complete examples. + # + # The connection settings the chart owns are merged in last and always win, + # so setting them here has no effect: portal.url (from portal.url), + # portal.token / db.url / auth.secret (Secret-backed ${...} placeholders), + # app.baseURL and app.trustedOrigins (from app.baseURL / app.trustedOrigins). + # + # Keep credentials OUT of this block — it renders into a ConfigMap. Every + # string value supports ${VAR} / ${VAR:default} substitution at startup, so + # reference a placeholder here and inject the real value through + # developerPortal.extraEnvVars below. + # -- Developer Portal application config, rendered verbatim into `config.yaml` + config: + app: + name: "Developer Portal" + # desc: "Explore and integrate with our APIs." + # auth: + # emailAndPassword: + # enabled: true + # requireEmailVerification: false + # genericOAuthProviders: + # - providerId: keycloak + # discoveryUrl: https://sso.example.com/realms/main/.well-known/openid-configuration + # clientId: devportal + # clientSecret: ${OIDC_CLIENT_SECRET} + # db: + # schema: portal + # ssl: true + # Resource limits and requests resources: {} # limits: From ff309cdc7b0ab6a10ffc3f73ff7f459ecdbe5aca Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sun, 16 Aug 2026 21:53:51 +0800 Subject: [PATCH 2/4] chore(developer-portal-fe): upgrade the application to 0.14.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chart pinned 0.5.7, whose config schema predates db.schema, auth.adminUserIds, auth.twoFactor, auth.genericOAuthProviders, auth.sso and the app.applicationDetail / app.apiHub / app.signUpConsentLabel group — those fields are now reachable from values but the pinned image would drop them. 0.14.0 keeps the container contract unchanged (same port, entrypoint and /app/apps/site/config.yaml search path), and brings one breaking change: auth.secret must be at least 32 characters. Both it and the one config field to avoid on this version (auth.socialProviders, whose upstream schema is mis-declared and fails to parse) are documented in the chart README, and the ci values file leaves that field out. --- charts/developer-portal-fe/Chart.yaml | 2 +- charts/developer-portal-fe/README.md | 17 +++++++++++++++-- charts/developer-portal-fe/README.md.gotmpl | 13 +++++++++++++ .../ci/full-config-values.yaml | 12 +++--------- charts/developer-portal-fe/values.yaml | 2 +- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/charts/developer-portal-fe/Chart.yaml b/charts/developer-portal-fe/Chart.yaml index e4948b67..b0a564d7 100644 --- a/charts/developer-portal-fe/Chart.yaml +++ b/charts/developer-portal-fe/Chart.yaml @@ -3,7 +3,7 @@ name: developer-portal-fe description: A Helm chart for API7 Developer Portal Frontend type: application version: 0.2.0 -appVersion: "0.5.7" +appVersion: "0.14.0" maintainers: - name: API7 email: support@api7.ai diff --git a/charts/developer-portal-fe/README.md b/charts/developer-portal-fe/README.md index 001e9db1..47e83861 100644 --- a/charts/developer-portal-fe/README.md +++ b/charts/developer-portal-fe/README.md @@ -1,6 +1,6 @@ # developer-portal-fe -![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.7](https://img.shields.io/badge/AppVersion-0.5.7-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.14.0](https://img.shields.io/badge/AppVersion-0.14.0-informational?style=flat-square) A Helm chart for API7 Developer Portal Frontend @@ -135,7 +135,7 @@ developerPortal: | developerPortal.extraVolumes | list | `[]` | | | developerPortal.image.pullPolicy | string | `"IfNotPresent"` | | | developerPortal.image.repository | string | `"api7/api7-ee-developer-portal-fe"` | | -| developerPortal.image.tag | string | `"v0.5.7"` | | +| developerPortal.image.tag | string | `"v0.14.0"` | | | developerPortal.livenessProbe.failureThreshold | int | `10` | | | developerPortal.livenessProbe.initialDelaySeconds | int | `30` | | | developerPortal.livenessProbe.path | string | `"/"` | | @@ -189,3 +189,16 @@ developerPortal: | serviceAccount.name | string | `""` | | | tolerations | list | `[]` | | | topologySpreadConstraints | list | `[]` | | + +## Upgrading + +### To 0.2.0 + +The bundled application moves from 0.5.7 to 0.14.0, which requires `auth.secret` +to be at least 32 characters — a shorter secret fails validation at startup. +Rotate it before upgrading (`openssl rand -base64 32`); rotating signs existing +users out, so they will have to sign in again. + +`auth.socialProviders` is the one config field to avoid on this application +version: its schema is mis-declared upstream and the Pod fails to start when the +field is set. Use `auth.genericOAuthProviders` instead. diff --git a/charts/developer-portal-fe/README.md.gotmpl b/charts/developer-portal-fe/README.md.gotmpl index 69154c22..b2625100 100644 --- a/charts/developer-portal-fe/README.md.gotmpl +++ b/charts/developer-portal-fe/README.md.gotmpl @@ -109,3 +109,16 @@ developerPortal: ``` {{ template "chart.valuesSection" . }} + +## Upgrading + +### To 0.2.0 + +The bundled application moves from 0.5.7 to 0.14.0, which requires `auth.secret` +to be at least 32 characters — a shorter secret fails validation at startup. +Rotate it before upgrading (`openssl rand -base64 32`); rotating signs existing +users out, so they will have to sign in again. + +`auth.socialProviders` is the one config field to avoid on this application +version: its schema is mis-declared upstream and the Pod fails to start when the +field is set. Use `auth.genericOAuthProviders` instead. diff --git a/charts/developer-portal-fe/ci/full-config-values.yaml b/charts/developer-portal-fe/ci/full-config-values.yaml index a75ef795..88dbef12 100644 --- a/charts/developer-portal-fe/ci/full-config-values.yaml +++ b/charts/developer-portal-fe/ci/full-config-values.yaml @@ -36,10 +36,9 @@ developerPortal: twoFactor: enabled: true required: true - socialProviders: - github: - clientId: devportal - clientSecret: ${GITHUB_CLIENT_SECRET} + # auth.socialProviders is deliberately absent: the application's own + # schema mis-declares it and fails to parse a config that sets it + # (fixed after 0.14.0). genericOAuthProviders covers the same ground. genericOAuthProviders: - providerId: keycloak discoveryUrl: https://sso.example.com/realms/main/.well-known/openid-configuration @@ -70,11 +69,6 @@ developerPortal: secretKeyRef: name: devportal-db-ca key: ca.crt - - name: GITHUB_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: devportal-github - key: client-secret - name: OIDC_CLIENT_SECRET valueFrom: secretKeyRef: diff --git a/charts/developer-portal-fe/values.yaml b/charts/developer-portal-fe/values.yaml index e8e9b0fb..23af6604 100644 --- a/charts/developer-portal-fe/values.yaml +++ b/charts/developer-portal-fe/values.yaml @@ -9,7 +9,7 @@ developerPortal: image: repository: api7/api7-ee-developer-portal-fe pullPolicy: IfNotPresent - tag: "v0.5.7" + tag: "v0.14.0" # Any field the application's config schema accepts can be set here, so new # application config does not need a chart release. See the chart README for From 4c16febbc01e4ae7493c6cb791f095c6e4957e64 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sun, 16 Aug 2026 21:55:43 +0800 Subject: [PATCH 3/4] docs(developer-portal-fe): say the chart-owned settings are merged over the config block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Verbatim" read as "no key is touched", which is wrong — the chart-owned connection settings are merged over developerPortal.config. --- charts/developer-portal-fe/README.md | 13 +++++++------ charts/developer-portal-fe/README.md.gotmpl | 11 ++++++----- charts/developer-portal-fe/values.yaml | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/charts/developer-portal-fe/README.md b/charts/developer-portal-fe/README.md index 47e83861..332c9442 100644 --- a/charts/developer-portal-fe/README.md +++ b/charts/developer-portal-fe/README.md @@ -27,15 +27,16 @@ helm install developer-portal-fe api7/developer-portal-fe --namespace api7 --cre ## Configuring the portal application -The chart renders the application's `config.yaml` from `developerPortal.config`, -which is passed through verbatim. Any field the application's config schema -accepts can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, +The chart renders the application's `config.yaml` from `developerPortal.config`: +the block is copied into the file as it is written, and the connection settings +below are then merged over it. Any field the application's config schema accepts +can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, `auth.genericOAuthProviders`, `db.ssl`, and so on — without waiting for a chart release. Which fields exist depends on the deployed application version (`developerPortal.image.tag`). -The connection settings stay with the chart and are merged in last, so they -always win over `developerPortal.config`: +These are the paths the chart owns. They are merged in last, so they always win +over `developerPortal.config` and setting them there has no effect: | Config path | Comes from | | :--- | :--- | @@ -129,7 +130,7 @@ developerPortal: | db.existingSecret | string | `""` | | | db.existingSecretKey | string | `"db-url"` | | | db.url | string | `"postgres://portal:portal123@developer-portal-fe-postgresql:5432/portal"` | | -| developerPortal.config | object | `{"app":{"name":"Developer Portal"}}` | Developer Portal application config, rendered verbatim into `config.yaml` | +| developerPortal.config | object | `{"app":{"name":"Developer Portal"}}` | Developer Portal application config, copied into `config.yaml` with the chart-owned connection settings merged over it | | developerPortal.extraEnvVars | list | `[]` | | | developerPortal.extraVolumeMounts | list | `[]` | | | developerPortal.extraVolumes | list | `[]` | | diff --git a/charts/developer-portal-fe/README.md.gotmpl b/charts/developer-portal-fe/README.md.gotmpl index b2625100..84f3352e 100644 --- a/charts/developer-portal-fe/README.md.gotmpl +++ b/charts/developer-portal-fe/README.md.gotmpl @@ -19,15 +19,16 @@ helm install developer-portal-fe api7/developer-portal-fe --namespace api7 --cre ## Configuring the portal application -The chart renders the application's `config.yaml` from `developerPortal.config`, -which is passed through verbatim. Any field the application's config schema -accepts can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, +The chart renders the application's `config.yaml` from `developerPortal.config`: +the block is copied into the file as it is written, and the connection settings +below are then merged over it. Any field the application's config schema accepts +can be set there — `app.name`, `app.desc`, `auth.emailAndPassword`, `auth.genericOAuthProviders`, `db.ssl`, and so on — without waiting for a chart release. Which fields exist depends on the deployed application version (`developerPortal.image.tag`). -The connection settings stay with the chart and are merged in last, so they -always win over `developerPortal.config`: +These are the paths the chart owns. They are merged in last, so they always win +over `developerPortal.config` and setting them there has no effect: | Config path | Comes from | | :--- | :--- | diff --git a/charts/developer-portal-fe/values.yaml b/charts/developer-portal-fe/values.yaml index 23af6604..bb748638 100644 --- a/charts/developer-portal-fe/values.yaml +++ b/charts/developer-portal-fe/values.yaml @@ -24,7 +24,7 @@ developerPortal: # string value supports ${VAR} / ${VAR:default} substitution at startup, so # reference a placeholder here and inject the real value through # developerPortal.extraEnvVars below. - # -- Developer Portal application config, rendered verbatim into `config.yaml` + # -- Developer Portal application config, copied into `config.yaml` with the chart-owned connection settings merged over it config: app: name: "Developer Portal" From 862398f87fbbdbe2556f15c7ec0a7fcabe2d6a47 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sun, 16 Aug 2026 21:58:04 +0800 Subject: [PATCH 4/4] docs(agents): record the helm-docs version trap and the portal chart's pass-through rule --- AGENTS.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 00d2e5bf..7fe97636 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,7 +60,7 @@ Operational rule: **on any `release/*` branch, only bump the charts you actually - `CR_SKIP_EXISTING: true`: already-released versions are skipped and the index is merged (not overwritten), so multiple branches are safe. - the GitHub Release tag is `-` (e.g. `gateway-3.9.0`); the numbering rule keeps it unique across lines. - `ci.yaml` (lint / ct install / helm-docs) also covers `release/**`. -- after editing a chart, regenerate each chart's `README.md` with `helm-docs --chart-search-root=charts`, or the helm-docs CI check fails. +- after editing a chart, regenerate each chart's `README.md` with `helm-docs --chart-search-root=charts`, or the helm-docs CI check fails. Use **v1.13.1**, the version CI installs: 1.14.x appends an `Autogenerated from chart metadata...` footer that CI then rejects, and the `helm-docs` pre-commit hook runs whatever `helm-docs` is on `PATH` — so it happily rewrites every chart README into a form CI fails on. Put a v1.13.1 binary first on `PATH` when committing. ## Gateway chart: which config to expose @@ -73,6 +73,12 @@ The gateway chart's `values.yaml`/configmap mirror the gateway's `conf/config-de When adding values, keep defaults identical to `config-default.yaml` so a default render changes nothing. To verify a rendered config is valid, extract `config.yaml` from the rendered configmap and run it through the real image: `docker run --rm --entrypoint sh -v $PWD/config.yaml:/usr/local/apisix/conf/config.yaml api7/api7-ee-3-gateway: -c 'apisix init'` — the config schema is validated at init. +## developer-portal-fe chart: config is a pass-through + +`config.yaml` is rendered from `developerPortal.config`, which carries the application's whole config schema. **Do not add per-field `.Values` keys for it** — the schema lives in `api7/api7ee-developer-portal` (`apps/site/src/lib/config/schema.ts`) and mirroring it drifts on every application release. Only the connection settings the chart itself owns are templated, and they are merged over the block so a user cannot move the Secret-backed `${PORTAL_TOKEN}` / `${DB_URL}` / `${AUTH_SECRET}` placeholders into the ConfigMap. + +To verify a rendered config, run it through the image's own loader: `docker run --rm -v $PWD/config.yaml:/app/apps/site/config.yaml -e PORTAL_TOKEN=x -e DB_URL=x -e AUTH_SECRET=$(head -c 32 /dev/zero | tr '\0' a) --entrypoint sh api7/api7-ee-developer-portal-fe: -c 'node preflight.js'` — reaching the `Portal URL:` line means the config validated (the portal/database checks after it need a live backend). That mount path is fixed: the standalone server chdirs into `apps/site`, and preflight looks there. + ## User upgrade guidance - Follow the latest line: `helm repo update && helm upgrade api7/` (picks the highest version, always the newest EE line).