From 8ed0626627de363221933462d74fa66efdb68ea7 Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 3 Sep 2026 11:38:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=A2=84=E8=A7=88=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E6=9C=AC=E5=9C=B0=E9=85=8D=E7=BD=AE=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 恢复 API 与 worker 的 .env.local BuildKit secret 挂载 补齐 Jenkins 固定文件校验与预览文档 增加双配置文件构建门禁 --- deploy/container/README.md | 6 +- deploy/container/api-server.Dockerfile | 8 +++ docs/project-memory/shared-memory/pitfalls.md | 4 +- ...Jenkins容器预览部署控制面技术方案-2026-08-15.md | 6 +- jenkins/Jenkinsfile.preview-deployer | 1 + scripts/check-preview-deployer.mjs | 51 +++++++++++++++-- scripts/jenkins-preview-deployer.sh | 56 ++++++++++++++----- 7 files changed, 104 insertions(+), 28 deletions(-) diff --git a/deploy/container/README.md b/deploy/container/README.md index 6631905a1..ed97a2924 100644 --- a/deploy/container/README.md +++ b/deploy/container/README.md @@ -60,11 +60,11 @@ Linux Docker Engine 若要从宿主机 CLI 连到容器内服务,直接用 `ht ### Jenkins 预览 secrets 镜像边界 -Jenkins 分支预览构建固定从宿主 `/data/jenkins/preview-secrets/.env.secrets.local` 读取 secrets。目录由 Jenkins 运行账号所有且权限为 `0700`,文件由同一账号所有且权限为 `0600`;构建入口对缺失、链接、非普通文件、owner 不匹配和过宽权限均失败关闭。不要把真实值写入本 README、仓库示例或 Jenkins 参数。 +Jenkins 分支预览构建固定从宿主 `/data/jenkins/preview-secrets/.env.local` 与 `/data/jenkins/preview-secrets/.env.secrets.local` 读取运行时配置。目录由 Jenkins 运行账号所有且权限为 `0700`,两个文件由同一账号所有且权限为 `0600`;构建入口对缺失、链接、非普通文件、owner 不匹配和过宽权限均失败关闭。两个文件都包含敏感配置,不要把真实值写入本 README、仓库示例或 Jenkins 参数。 -该文件不复制到源码 checkout 和 Docker build context,而是以 BuildKit `secret` mount 只提供给 `api-runtime` stage。构建会把它安装到 API 运行镜像的 `/srv/genarrative/.env.secrets.local`,owner 为 `genarrative`、权限为 `0400`。Web builder、`nginx-runtime`、SpacetimeDB 和其它运行镜像不得获得该 mount 或目标文件;构建日志和 artifact 也不得回显或保存文件内容。容器的显式运行环境变量优先于该内置文件,可按预览实例覆盖其中的值。 +两个文件都不复制到源码 checkout 和 Docker build context,而是分别以 BuildKit `secret` mount 只提供给 `api-runtime` stage。构建会把它们安装到 API 运行镜像的 `/srv/genarrative/.env.local` 与 `/srv/genarrative/.env.secrets.local`,owner 为 `genarrative`、权限为 `0400`。Web builder、`nginx-runtime`、SpacetimeDB 和其它运行镜像不得获得这些 mount 或目标文件;构建日志和 artifact 也不得回显或保存文件内容。容器的显式运行环境变量优先于这两个内置文件,可按预览实例覆盖其中的值。 -修改宿主固定文件后必须重新构建并替换 API 镜像;重启旧容器不会读取宿主新内容。这个镜像不是可公开分发的无密钥产物:镜像持有者可以提取 `/srv/genarrative/.env.secrets.local`。只允许在当前受信任内网 Docker 主机使用,禁止 push 或 `docker save`、artifact 导出到跨信任边界的 registry、主机或存储。 +修改任一宿主固定文件后必须重新构建并替换 API 与 worker 镜像;重启旧容器不会读取宿主新内容。这个镜像不是可公开分发的无密钥产物:镜像持有者可以提取 `/srv/genarrative/.env.local` 与 `/srv/genarrative/.env.secrets.local`。只允许在当前受信任内网 Docker 主机使用,禁止 push 或 `docker save`、artifact 导出到跨信任边界的 registry、主机或存储。 ### Gitea CI 预构建 Job 镜像 diff --git a/deploy/container/api-server.Dockerfile b/deploy/container/api-server.Dockerfile index d97732bce..e61d73eae 100644 --- a/deploy/container/api-server.Dockerfile +++ b/deploy/container/api-server.Dockerfile @@ -24,12 +24,20 @@ RUN mkdir -p /var/lib/genarrative/auth /var/lib/genarrative/tracking-outbox /var chown -R genarrative:genarrative /srv/genarrative /var/lib/genarrative ARG GENARRATIVE_PREVIEW_SECRETS_SHA256= +ARG GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256= RUN --mount=type=secret,id=genarrative_preview_secrets,required=false \ + --mount=type=secret,id=genarrative_preview_env_local,required=false \ if [ -n "${GENARRATIVE_PREVIEW_SECRETS_SHA256}" ]; then \ test -f /run/secrets/genarrative_preview_secrets; \ test "$(sha256sum /run/secrets/genarrative_preview_secrets | cut -d ' ' -f 1)" = "${GENARRATIVE_PREVIEW_SECRETS_SHA256}"; \ install -o genarrative -g genarrative -m 0400 \ /run/secrets/genarrative_preview_secrets /srv/genarrative/.env.secrets.local; \ + fi; \ + if [ -n "${GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256}" ]; then \ + test -f /run/secrets/genarrative_preview_env_local; \ + test "$(sha256sum /run/secrets/genarrative_preview_env_local | cut -d ' ' -f 1)" = "${GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256}"; \ + install -o genarrative -g genarrative -m 0400 \ + /run/secrets/genarrative_preview_env_local /srv/genarrative/.env.local; \ fi USER genarrative diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 5860fb644..722ae47c9 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -4926,8 +4926,8 @@ - 现象:构建时使用 BuildKit secret mount,日志和普通 build context 都没有出现明文,于是误以为最终镜像也能不可提取地保存 secrets,随后将镜像 push 或导出给不同信任域。 - 原因:BuildKit secret mount 只避免秘密作为 `ARG` / `COPY` 进入构建上下文和中间指令;一旦 Dockerfile 把 mount 的内容安装到最终 rootfs,任何能读取、保存或运行该镜像的主体都可以提取它。 -- 处理:预览固定 secrets 只从 Jenkins 宿主受控路径读取,严格校验目录 `0700`、文件 `0600`、owner、普通文件与非链接边界;只将其安装到 `api-runtime:/srv/genarrative/.env.secrets.local` 并设为 `0400`,明确排除 Nginx、Web、artifact 和其它镜像。镜像禁止推送或导出到跨信任边界。 -- 更新与验证:源文件变更不会改动已存镜像,必须重建并替换容器;不能用重启代替。验收同时扫描 transcript/context/artifact 零泄漏,检查只有 API 最终 rootfs 存在目标文件,并验证容器显式运行 env 优先覆盖内置值。 +- 处理:预览固定 `.env.local` 与 secrets 只从 Jenkins 宿主受控路径读取,严格校验目录 `0700`、文件 `0600`、owner、普通文件与非链接边界;只将它们安装到 `api-runtime:/srv/genarrative/.env.local` 与 `/srv/genarrative/.env.secrets.local` 并设为 `0400`,明确排除 Nginx、Web、artifact 和其它镜像。镜像禁止推送或导出到跨信任边界。 +- 更新与验证:任一源文件变更不会改动已存镜像,必须重建并替换 API 与 worker 容器;不能用重启代替。验收同时扫描 transcript/context/artifact 零泄漏,检查只有 API 与 worker 最终 rootfs 存在目标文件,并验证容器显式运行 env 优先覆盖内置值。 ## SpacetimeDB ping 健康不代表完整模块能在内存上限内实例化(2026-08-22) diff --git a/docs/technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md b/docs/technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md index 782224108..6fcf77ff7 100644 --- a/docs/technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md +++ b/docs/technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md @@ -51,9 +51,9 @@ SpacetimeDB 2.7 CLI 发布到受控 Compose 网络地址时固定使用 `--yes=r ## 预览 secrets 内置 -Jenkins 节点上的预览 secrets 只允许来自受控的 Jenkins 凭据目录(目录与文件权限、owner、普通文件和非链接约束由流水线检查)。该文件不进 Git、Docker build context、构建日志或 artifact;构建时只通过 BuildKit `secret` mount 临时提供给 `api-runtime` stage,运行镜像权限固定为 `0400`。`nginx-runtime`、Web 静态产物、SpacetimeDB 镜像及其它镜像不得包含该文件。 +Jenkins 节点上的预览 `.env.local` 与 secrets 只允许来自受控的 Jenkins 凭据目录(目录与文件权限、owner、普通文件和非链接约束由流水线检查)。固定宿主副本不进 Git、Docker build context、构建日志或 artifact;构建时分别通过两个 BuildKit `secret` mount 临时提供给 `api-runtime` stage,并安装为 `/srv/genarrative/.env.local` 与 `/srv/genarrative/.env.secrets.local`,运行镜像权限固定为 `0400`。`nginx-runtime`、Web 静态产物、SpacetimeDB 镜像及其它镜像不得包含这些文件;仓库工作区 `.env.local` 不得替代固定宿主副本。 -宿主固定目录应由 Jenkins 运行账号所有且权限为 `0700`,源文件权限为 `0600`;缺失、不是普通文件、owner 不匹配或权限过宽时,预览构建必须失败关闭。源文件变更后必须重新构建并替换预览镜像,只重启容器不会刷新已内置的内容。容器启动时显式注入的运行环境变量优先级高于镜像内的 `.env.secrets.local`,用于按实例覆盖非通用值。 +宿主固定目录应由 Jenkins 运行账号所有且权限为 `0700`,两个源文件权限均为 `0600`;缺失、不是普通文件、owner 不匹配或权限过宽时,预览构建必须失败关闭。任一源文件变更后必须重新构建并替换 API 与 worker 预览镜像,只重启容器不会刷新已内置的内容。容器启动时显式注入的运行环境变量优先级高于镜像内的 `.env.local` 与 `.env.secrets.local`,用于按实例覆盖非通用值。 这种方案只隐藏构建传输过程,不能让内置后的 secrets 对镜像持有者保密:能读取、保存或运行 `api-runtime` 镜像的人可以提取该文件。因此该镜像只能留在当前受信任内网 Docker 主机,禁止 push 到公共或跨信任边界的 registry,也禁止通过 `docker save`/构建 artifact 导出传播。需要跨边界分发时必须改用不含 secrets 的镜像与运行时密钥注入。 @@ -118,7 +118,7 @@ Jenkins 在构建完成、归档 artifact 和更新 REST 状态之间可能短 - Jenkins service account 只授予 `shared/Genarrative-Preview-Deployer` 的 `Job/Read`、`Job/Build` 和读取构建产物所需权限,不授 `Overall/Administer`、`Job/Configure` 或 `Job/Delete`。 - 后端固定 Jenkins origin、Job 路径和参数白名单;客户端不能传 URL、Job 名、Compose project、容器名、宿主端口或 Jenkins 凭据。 - Git 查询固定使用本机 Gitea SSH 地址和服务端只读凭据;客户端不能传 remote、SSH 参数或凭据。Git 缓存只写入预览控制服务的受控状态目录,搜索接口需要控制台会话且结果有数量上限。 -- 预览 secrets 只从固定宿主路径读取,构建前校验 owner、类型和权限;不允许分支、Jenkins 参数或控制面请求改写 secrets 路径、BuildKit secret ID 或镜像内目标路径。 +- 预览 `.env.local` 与 secrets 只从固定宿主路径读取,构建前校验目录和文件的 owner、类型和权限;不允许分支、Jenkins 参数或控制面请求改写这些路径、BuildKit secret ID 或镜像内目标路径。 - Jenkins POST 支持动态 Crumb;API Token 即使免 Crumb,也不能把 Token 放进 URL 或日志。 - API 默认只接受同源请求,写请求校验 Origin;内网本身不作为认证。 - 同一 deployment 的发布和卸载串行执行;重复请求必须幂等或明确返回冲突。 diff --git a/jenkins/Jenkinsfile.preview-deployer b/jenkins/Jenkinsfile.preview-deployer index d63b06067..46daea974 100644 --- a/jenkins/Jenkinsfile.preview-deployer +++ b/jenkins/Jenkinsfile.preview-deployer @@ -15,6 +15,7 @@ pipeline { GIT_REMOTE_CREDENTIAL_ID = 'genarrative-local-gitea-ssh' GENARRATIVE_PREVIEW_STATE_ROOT = '/data/jenkins/preview-deployments' GENARRATIVE_PREVIEW_SECRETS_FILE = '/data/jenkins/preview-secrets/.env.secrets.local' + GENARRATIVE_PREVIEW_ENV_LOCAL_FILE = '/data/jenkins/preview-secrets/.env.local' GENARRATIVE_PREVIEW_WEB_HOST = '192.168.35.82' } diff --git a/scripts/check-preview-deployer.mjs b/scripts/check-preview-deployer.mjs index a5f3268ba..c79fa3946 100644 --- a/scripts/check-preview-deployer.mjs +++ b/scripts/check-preview-deployer.mjs @@ -139,20 +139,40 @@ assertIncludes( 'GENARRATIVE_PREVIEW_SECRETS_SHA256', '预览构建必须把固定 secrets 文件摘要作为镜像缓存与完整性校验参数。', ); +assertIncludes( + deployer, + 'GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256', + '预览构建必须把固定 .env.local 文件摘要作为镜像缓存与完整性校验参数。', +); assertIncludes( jenkinsfile, "GENARRATIVE_PREVIEW_SECRETS_FILE = '/data/jenkins/preview-secrets/.env.secrets.local'", 'Jenkins 必须从受保护的固定宿主路径读取预览 secrets。', ); assertIncludes( - deployer, - '[[ "${secrets_mode}" == "600" ]]', - '预览构建必须拒绝权限过宽的 secrets 文件。', + jenkinsfile, + "GENARRATIVE_PREVIEW_ENV_LOCAL_FILE = '/data/jenkins/preview-secrets/.env.local'", + 'Jenkins 必须从受保护的固定宿主路径读取预览 .env.local。', ); assertIncludes( deployer, - '[[ "${secrets_owner}" == "${EUID}" ]]', - '预览构建必须校验 secrets 文件归 Jenkins 执行用户所有。', + '[[ "${file_mode}" == "600" ]]', + '预览固定输入文件必须拒绝权限过宽。', +); +assertIncludes( + deployer, + '[[ "${dir_mode}" == "700" ]]', + '预览固定输入文件所在目录必须拒绝权限过宽。', +); +assertIncludes( + deployer, + '[[ "${file_owner}" == "${EUID}" ]]', + '预览固定输入文件必须校验 owner 归 Jenkins 执行用户所有。', +); +assertIncludes( + deployer, + '[[ "${dir_owner}" == "${EUID}" ]]', + '预览固定输入文件所在目录必须校验 owner 归 Jenkins 执行用户所有。', ); assertIncludes( deployer, @@ -165,11 +185,22 @@ assertCount( 2, '预览 secrets 必须且只能提供给 API 和外部生成 worker 两个构建。', ); +assertCount( + deployer, + 'target: genarrative_preview_env_local', + 2, + '预览 .env.local 必须且只能提供给 API 和外部生成 worker 两个构建。', +); assertIncludes( apiServerDockerfile, 'ARG GENARRATIVE_PREVIEW_SECRETS_SHA256=', 'API 镜像必须允许普通构建不提供预览 secrets 摘要。', ); +assertIncludes( + apiServerDockerfile, + 'ARG GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256=', + 'API 镜像必须允许普通构建不提供预览 .env.local 摘要。', +); assertIncludes( apiServerDockerfile, 'RUN --mount=type=secret,id=genarrative_preview_secrets,required=false', @@ -195,6 +226,16 @@ assertIncludes( '/run/secrets/genarrative_preview_secrets /srv/genarrative/.env.secrets.local;', '预览 secrets 文件必须安装到 API 启动时读取的固定路径。', ); +assertIncludes( + apiServerDockerfile, + '--mount=type=secret,id=genarrative_preview_env_local,required=false', + 'API 镜像必须通过可选 BuildKit secret 接收预览 .env.local 文件。', +); +assertIncludes( + apiServerDockerfile, + '/run/secrets/genarrative_preview_env_local /srv/genarrative/.env.local;', + '预览 .env.local 文件必须安装到 API 启动时读取的固定路径。', +); assertIncludes( deployer, 'GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED=true', diff --git a/scripts/jenkins-preview-deployer.sh b/scripts/jenkins-preview-deployer.sh index ec1e7dcb2..b4354fcd1 100644 --- a/scripts/jenkins-preview-deployer.sh +++ b/scripts/jenkins-preview-deployer.sh @@ -11,6 +11,7 @@ RESULT_FILE="${RESULT_FILE:-${WORKSPACE:-$(pwd)}/preview-result.json}" DESCRIPTION_FILE="${DESCRIPTION_FILE:-${WORKSPACE:-$(pwd)}/.jenkins-preview-description}" STATE_ROOT="${GENARRATIVE_PREVIEW_STATE_ROOT:-/data/jenkins/preview-deployments}" PREVIEW_SECRETS_FILE="${GENARRATIVE_PREVIEW_SECRETS_FILE:-/data/jenkins/preview-secrets/.env.secrets.local}" +PREVIEW_ENV_LOCAL_FILE="${GENARRATIVE_PREVIEW_ENV_LOCAL_FILE:-/data/jenkins/preview-secrets/.env.local}" WEB_HOST="${GENARRATIVE_PREVIEW_WEB_HOST:-}" LOCK_FILE="${GENARRATIVE_PREVIEW_LOCK_FILE:-${STATE_ROOT}/.lock}" @@ -20,6 +21,7 @@ PROJECT_NAME="" SCRIPT_ROOT="" SCRIPT_FAILED=1 PREVIEW_SECRETS_SHA256="" +PREVIEW_ENV_LOCAL_SHA256="" fail() { echo "[preview-deployer] $*" >&2 @@ -246,25 +248,39 @@ allocate_port() { fail "端口范围 ${start}-${end} 已无可用端口。" } -validate_preview_secrets_file() { - local secrets_dir secrets_mode secrets_owner canonical_secrets canonical_source - [[ "${PREVIEW_SECRETS_FILE}" == /* ]] || fail "预览 secrets 文件必须使用绝对路径。" - [[ -f "${PREVIEW_SECRETS_FILE}" && ! -L "${PREVIEW_SECRETS_FILE}" && -r "${PREVIEW_SECRETS_FILE}" ]] || \ - fail "预览 secrets 文件必须是 Jenkins 可读的非符号链接普通文件: ${PREVIEW_SECRETS_FILE}" - secrets_dir="$(dirname "${PREVIEW_SECRETS_FILE}")" - [[ -d "${secrets_dir}" && ! -L "${secrets_dir}" ]] || \ - fail "预览 secrets 目录必须是非符号链接目录: ${secrets_dir}" - secrets_mode="$(stat -c '%a' "${PREVIEW_SECRETS_FILE}")" - [[ "${secrets_mode}" == "600" ]] || fail "预览 secrets 文件权限必须是 0600: ${PREVIEW_SECRETS_FILE}" - secrets_owner="$(stat -c '%u' "${PREVIEW_SECRETS_FILE}")" - [[ "${secrets_owner}" == "${EUID}" ]] || fail "预览 secrets 文件必须归当前 Jenkins 执行用户所有。" - canonical_secrets="$(realpath -e "${PREVIEW_SECRETS_FILE}")" +validate_preview_input_file() { + local file="$1" + local label="$2" + local file_dir file_mode file_owner dir_mode dir_owner canonical_file canonical_source + [[ "${file}" == /* ]] || fail "${label}必须使用绝对路径。" + [[ -f "${file}" && ! -L "${file}" && -r "${file}" ]] || \ + fail "${label}必须是 Jenkins 可读的非符号链接普通文件: ${file}" + file_dir="$(dirname "${file}")" + [[ -d "${file_dir}" && ! -L "${file_dir}" ]] || \ + fail "${label}所在目录必须是非符号链接目录: ${file_dir}" + dir_mode="$(stat -c '%a' "${file_dir}")" + [[ "${dir_mode}" == "700" ]] || fail "${label}所在目录权限必须是 0700: ${file_dir}" + dir_owner="$(stat -c '%u' "${file_dir}")" + [[ "${dir_owner}" == "${EUID}" ]] || fail "${label}所在目录必须归当前 Jenkins 执行用户所有。" + file_mode="$(stat -c '%a' "${file}")" + [[ "${file_mode}" == "600" ]] || fail "${label}权限必须是 0600: ${file}" + file_owner="$(stat -c '%u' "${file}")" + [[ "${file_owner}" == "${EUID}" ]] || fail "${label}必须归当前 Jenkins 执行用户所有。" + canonical_file="$(realpath -e "${file}")" canonical_source="$(realpath -e "${SOURCE_DIR}")" - [[ "${canonical_secrets}" != "${canonical_source}"/* ]] || \ - fail "预览 secrets 文件不能位于目标分支源码上下文内。" + [[ "${canonical_file}" != "${canonical_source}"/* ]] || \ + fail "${label}不能位于目标分支源码上下文内。" +} + +validate_preview_secrets_file() { + validate_preview_input_file "${PREVIEW_SECRETS_FILE}" '预览 secrets 文件' + validate_preview_input_file "${PREVIEW_ENV_LOCAL_FILE}" '预览 .env.local 文件' PREVIEW_SECRETS_SHA256="$(sha256sum "${PREVIEW_SECRETS_FILE}")" PREVIEW_SECRETS_SHA256="${PREVIEW_SECRETS_SHA256%% *}" [[ "${PREVIEW_SECRETS_SHA256}" =~ ^[0-9a-f]{64}$ ]] || fail "无法计算预览 secrets 文件摘要。" + PREVIEW_ENV_LOCAL_SHA256="$(sha256sum "${PREVIEW_ENV_LOCAL_FILE}")" + PREVIEW_ENV_LOCAL_SHA256="${PREVIEW_ENV_LOCAL_SHA256%% *}" + [[ "${PREVIEW_ENV_LOCAL_SHA256}" =~ ^[0-9a-f]{64}$ ]] || fail "无法计算预览 .env.local 文件摘要。" } remove_project_resources() { @@ -298,6 +314,8 @@ compose() { GENARRATIVE_PREVIEW_CONTROLLER_ROOT="${SCRIPT_ROOT}/.." \ GENARRATIVE_PREVIEW_SECRETS_FILE="${PREVIEW_SECRETS_FILE}" \ GENARRATIVE_PREVIEW_SECRETS_SHA256="${PREVIEW_SECRETS_SHA256}" \ + GENARRATIVE_PREVIEW_ENV_LOCAL_FILE="${PREVIEW_ENV_LOCAL_FILE}" \ + GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256="${PREVIEW_ENV_LOCAL_SHA256}" \ GENARRATIVE_CONTAINER_API_ENV_FILE="${STATE_DIR}/api-server.env" \ GENARRATIVE_CONTAINER_HTTP_PORT="${WEB_PORT}" \ GENARRATIVE_CONTAINER_SPACETIME_PORT="${SPACETIME_PORT}" \ @@ -319,18 +337,24 @@ services: dockerfile: ${GENARRATIVE_PREVIEW_CONTROLLER_ROOT}/deploy/container/api-server.Dockerfile args: GENARRATIVE_PREVIEW_SECRETS_SHA256: ${GENARRATIVE_PREVIEW_SECRETS_SHA256} + GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256: ${GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256} secrets: - source: preview_runtime_env target: genarrative_preview_secrets + - source: preview_runtime_env_local + target: genarrative_preview_env_local external-generation-worker: build: context: ${GENARRATIVE_PREVIEW_SOURCE_DIR} dockerfile: ${GENARRATIVE_PREVIEW_CONTROLLER_ROOT}/deploy/container/api-server.Dockerfile args: GENARRATIVE_PREVIEW_SECRETS_SHA256: ${GENARRATIVE_PREVIEW_SECRETS_SHA256} + GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256: ${GENARRATIVE_PREVIEW_ENV_LOCAL_SHA256} secrets: - source: preview_runtime_env target: genarrative_preview_secrets + - source: preview_runtime_env_local + target: genarrative_preview_env_local restart: on-failure nginx: build: @@ -344,6 +368,8 @@ services: secrets: preview_runtime_env: file: ${GENARRATIVE_PREVIEW_SECRETS_FILE} + preview_runtime_env_local: + file: ${GENARRATIVE_PREVIEW_ENV_LOCAL_FILE} YAML chmod 0600 "${override_file}" }