diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d774c83..d44ddac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,9 @@ All notable changes to this project will be documented in this file. - Switched image build pipeline to GHCR with multi-arch layer caching. - Aligned the nginx image env-var contract: split `NGINX_FPM_SERVICE` and `NGINX_FPM_PORT`, raised upload cap and trusted-proxy CIDR defaults. +- Documented the 3.x operator-facing image-deployment contract in + `UPGRADE.md` (full `APP_*` → unprefixed rename list, `env_file:` pattern, + runtime-tuning surfaces). - Allowed same-origin iframe embedding so the admin's screen/playlist preview and fullscreen slide view work (#390). - Image build now writes `public/release.json` so the client's diff --git a/UPGRADE.md b/UPGRADE.md index f9391135..cb52e80e 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -66,12 +66,147 @@ docker compose exec phpfpm bin/console app:utils:convert-config-json-to-env --ty #### 2.1 - Rename environment variables -Rename the following .env variables in `.env.local`: +In 3.x the compose stack carries no `APP_*` prefix translation — variable +names in `.env.local` must match the Symfony names exactly. Every +`APP_X` variable from the previous 2.x `.env.docker.local` is renamed +to `X`, with the **exception of `APP_ENV` and `APP_SECRET`** which are +Symfony-defined and keep their prefix. + +The full rename list: + +```text +APP_TRUSTED_PROXIES → TRUSTED_PROXIES +APP_DATABASE_URL → DATABASE_URL +APP_CORS_ALLOW_ORIGIN → CORS_ALLOW_ORIGIN +APP_DEFAULT_DATE_FORMAT → DEFAULT_DATE_FORMAT +APP_ACTIVATION_CODE_EXPIRE_INTERVAL → ACTIVATION_CODE_EXPIRE_INTERVAL +APP_KEY_VAULT_SOURCE → KEY_VAULT_SOURCE +APP_KEY_VAULT_JSON → KEY_VAULT_JSON +APP_TRACK_SCREEN_INFO → TRACK_SCREEN_INFO +APP_TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS → TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS + +APP_JWT_SECRET_KEY → JWT_SECRET_KEY +APP_JWT_PUBLIC_KEY → JWT_PUBLIC_KEY +APP_JWT_PASSPHRASE → JWT_PASSPHRASE +APP_JWT_TOKEN_TTL → JWT_TOKEN_TTL +APP_JWT_SCREEN_TOKEN_TTL → JWT_SCREEN_TOKEN_TTL +APP_JWT_REFRESH_TOKEN_TTL → JWT_REFRESH_TOKEN_TTL +APP_JWT_SCREEN_REFRESH_TOKEN_TTL → JWT_SCREEN_REFRESH_TOKEN_TTL + +APP_REDIS_CACHE_PREFIX → REDIS_CACHE_PREFIX +APP_REDIS_CACHE_DSN → REDIS_CACHE_DSN + +APP_HTTP_CLIENT_TIMEOUT → HTTP_CLIENT_TIMEOUT +APP_HTTP_CLIENT_MAX_DURATION → HTTP_CLIENT_MAX_DURATION +APP_HTTP_CLIENT_LOG_LEVEL → HTTP_CLIENT_LOG_LEVEL + +APP_INTERNAL_OIDC_METADATA_URL → INTERNAL_OIDC_METADATA_URL +APP_INTERNAL_OIDC_CLIENT_ID → INTERNAL_OIDC_CLIENT_ID +APP_INTERNAL_OIDC_CLIENT_SECRET → INTERNAL_OIDC_CLIENT_SECRET +APP_INTERNAL_OIDC_REDIRECT_URI → INTERNAL_OIDC_REDIRECT_URI +APP_INTERNAL_OIDC_LEEWAY → INTERNAL_OIDC_LEEWAY +APP_INTERNAL_OIDC_CLAIM_NAME → INTERNAL_OIDC_CLAIM_NAME +APP_INTERNAL_OIDC_CLAIM_EMAIL → INTERNAL_OIDC_CLAIM_EMAIL +APP_INTERNAL_OIDC_CLAIM_GROUPS → INTERNAL_OIDC_CLAIM_GROUPS + +APP_EXTERNAL_OIDC_METADATA_URL → EXTERNAL_OIDC_METADATA_URL +APP_EXTERNAL_OIDC_CLIENT_ID → EXTERNAL_OIDC_CLIENT_ID +APP_EXTERNAL_OIDC_CLIENT_SECRET → EXTERNAL_OIDC_CLIENT_SECRET +APP_EXTERNAL_OIDC_REDIRECT_URI → EXTERNAL_OIDC_REDIRECT_URI +APP_EXTERNAL_OIDC_LEEWAY → EXTERNAL_OIDC_LEEWAY +APP_EXTERNAL_OIDC_HASH_SALT → EXTERNAL_OIDC_HASH_SALT +APP_EXTERNAL_OIDC_CLAIM_ID → EXTERNAL_OIDC_CLAIM_ID +APP_OIDC_CLI_REDIRECT → OIDC_CLI_REDIRECT + +APP_CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT → CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT +APP_CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT → CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT +APP_CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT → CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT +APP_CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS → CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS +APP_CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS → CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS +APP_CALENDAR_API_FEED_SOURCE_DATE_FORMAT → CALENDAR_API_FEED_SOURCE_DATE_FORMAT +APP_CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE → CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE +APP_CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS → CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS + +APP_EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS → EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS + +APP_ADMIN_REJSEPLANEN_APIKEY → ADMIN_REJSEPLANEN_APIKEY +APP_ADMIN_SHOW_SCREEN_STATUS → ADMIN_SHOW_SCREEN_STATUS +APP_ADMIN_TOUCH_BUTTON_REGIONS → ADMIN_TOUCH_BUTTON_REGIONS +APP_ADMIN_LOGIN_METHODS → ADMIN_LOGIN_METHODS +APP_ADMIN_ENHANCED_PREVIEW → ADMIN_ENHANCED_PREVIEW + +APP_CLIENT_LOGIN_CHECK_TIMEOUT → CLIENT_LOGIN_CHECK_TIMEOUT +APP_CLIENT_REFRESH_TOKEN_TIMEOUT → CLIENT_REFRESH_TOKEN_TIMEOUT +APP_CLIENT_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT → CLIENT_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT +APP_CLIENT_SCHEDULING_INTERVAL → CLIENT_SCHEDULING_INTERVAL +APP_CLIENT_PULL_STRATEGY_INTERVAL → CLIENT_PULL_STRATEGY_INTERVAL +APP_CLIENT_COLOR_SCHEME → CLIENT_COLOR_SCHEME +APP_CLIENT_DEBUG → CLIENT_DEBUG +``` + +The `os2display-docker-server` repo provides `task env:migrate`, which +performs this rename automatically: it reads a 2.x `.env.docker.local` +and writes a 3.x-shaped `.env.local`. Use it as the recommended +migration path: + +```shell +# In your os2display-docker-server checkout, on the 3.0 branch: +task env:migrate +task env:diff # review the result against the canonical example +``` + +#### 2.2 - Adopt the new operator-facing image-deployment contract + +Production deployments now use the GHCR-published images +(`ghcr.io/os2display/display-api-service` and +`ghcr.io/os2display/display-api-service-nginx`) and follow an `env_file:` +pattern in the compose stack. + +##### Bootstrap `.env.local` from the image + +The image ships a self-documenting `.env` at `/var/www/html/.env` that +lists every Symfony env variable the application consumes, with a +one-line description per variable. Use it as the starting point for +your operator-host `.env.local`: + +```shell +docker run --rm ghcr.io/os2display/display-api-service: \ + cat /var/www/html/.env > .env.local +``` + +Then edit `.env.local` to set the required values for your environment: + +- `APP_ENV=prod` +- `APP_SECRET=` +- `JWT_PASSPHRASE=` +- `DATABASE_URL=` +- `CORS_ALLOW_ORIGIN=` +- OIDC provider settings (`INTERNAL_OIDC_*` and/or `EXTERNAL_OIDC_*`) + +##### Reference `.env.local` from compose via `env_file:` + +```yaml +services: + api: + image: ghcr.io/os2display/display-api-service: + env_file: + - .env.local +``` + +The `os2display-docker-server` compose stack does this for you. See its +`UPGRADE.md` for full operator migration steps. + +##### nginx and PHP-FPM runtime tuning + +OS-level / runtime knobs are independent of the Symfony env surface and +are passed to their respective images via compose `environment:`: -- From `APP_DEFAULT_DATE_FORMAT` to `DEFAULT_DATE_FORMAT` -- From `APP_ACTIVATION_CODE_EXPIRE_INTERVAL` to `ACTIVATION_CODE_EXPIRE_INTERVAL` -- From `APP_KEY_VAULT_SOURCE` to `KEY_VAULT_SOURCE` -- From `APP_KEY_VAULT_JSON` to `KEY_VAULT_JSON` +- nginx: `NGINX_PORT`, `NGINX_FPM_SERVICE`, `NGINX_FPM_PORT`, + `NGINX_MAX_BODY_SIZE`, `NGINX_SET_REAL_IP_FROM`, `NGINX_WEB_ROOT` + (defaults in `infrastructure/nginx/Dockerfile`). +- PHP-FPM: `PHP_MEMORY_LIMIT`, `PHP_MAX_EXECUTION_TIME`, + `PHP_POST_MAX_SIZE`, `PHP_UPLOAD_MAX_FILESIZE`, `PHP_PM_*`, + `PHP_OPCACHE_*` (consumed by the `itkdev/php8.4-fpm` base image). #### 3 - Consolidate Doctrine migrations