Files
OmniRoute/scripts/docker/ensure-docker-base-path.mjs
AmirHossein Rezaei bb5cb51f3e fix(docker): honor OMNIROUTE_BASE_PATH behind reverse-proxy subpaths (#8615)
* fix(docker): honor OMNIROUTE_BASE_PATH behind reverse-proxy subpaths

Next.js basePath is compile-time state; Docker now records the baked value,
forwards the env var as a build-arg, patches root-path images at container
start when needed, and probes health under the active subpath.

Hard Rule #13: scripts/docker/patch-basepath.sh and the entrypoint invoke Node
with a fixed argv; OMNIROUTE_BASE_PATH is read from process.env only — never
interpolated into sed/awk.

Closes #8600

* fix(docs): unblock CI for Docker basePath guide

Describe the build-time basePath marker as a sentinel file instead of a
fabricated env var, and replace the unsupported ```env fence with bash so
fumadocs/Shiki can compile DOCKER_GUIDE.md during DAST smoke.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(docker): add changelog fragment for #8615 basePath bundle patch

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-27 19:07:58 -03:00

87 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
/**
* Compares OMNIROUTE_BASE_PATH at container start against BUILD_OMNIROUTE_BASE_PATH
* recorded during the image build. When they differ and the image was built for the
* domain root, rewrites the standalone bundle before Next.js starts.
*
* Hard Rule #13: the subpath is read only from process.env (or an injected env
* object in tests). Never accept it as a CLI argument or interpolate it into a
* shell sed/awk invocation — see scripts/docker/patch-basepath.sh.
*/
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { normalizeBasePath } from "../build/normalizeBasePath.mjs";
import { patchStandaloneBasePath } from "./patch-standalone-base-path.mjs";
const SENTINEL = "BUILD_OMNIROUTE_BASE_PATH";
function readBakedBasePath(appRoot) {
const sentinelPath = path.join(appRoot, SENTINEL);
if (!fs.existsSync(sentinelPath)) return "";
return normalizeBasePath(fs.readFileSync(sentinelPath, "utf8"));
}
function writeBakedBasePath(appRoot, basePath) {
fs.writeFileSync(path.join(appRoot, SENTINEL), `${basePath}\n`);
}
/**
* @param {object} [opts]
* @param {string} [opts.appRoot]
* @param {NodeJS.ProcessEnv} [opts.env]
*/
export function ensureDockerBasePath(opts = {}) {
const appRoot = opts.appRoot || process.cwd();
const runtime = normalizeBasePath(opts.env?.OMNIROUTE_BASE_PATH ?? process.env.OMNIROUTE_BASE_PATH);
const baked = readBakedBasePath(appRoot);
if (runtime === baked) {
return { action: "noop", runtime, baked };
}
if (!runtime && baked) {
throw new Error(
`This OmniRoute image was built for subpath ${baked}, but OMNIROUTE_BASE_PATH is unset. ` +
`Set OMNIROUTE_BASE_PATH=${baked} or rebuild without the build-arg.`
);
}
const patchResult = patchStandaloneBasePath({
appRoot,
fromBasePath: baked,
toBasePath: runtime,
});
writeBakedBasePath(appRoot, runtime);
return { action: "patched", runtime, baked, patchResult };
}
function main() {
try {
const result = ensureDockerBasePath();
if (result.action === "patched") {
const { patchResult, runtime } = result;
console.log(
`[ensure-docker-base-path] Applied runtime subpath ${runtime} ` +
`(manifests=${patchResult.patchedManifests}, textFiles=${patchResult.patchedTextFiles})`
);
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`[ensure-docker-base-path] ${message}`);
console.error(
"[ensure-docker-base-path] Rebuild the image with the same subpath, e.g.\n" +
" docker compose build --build-arg OMNIROUTE_BASE_PATH=/omniroute"
);
process.exit(1);
}
}
const isEntrypoint =
Boolean(process.argv[1]) && import.meta.url === pathToFileURL(process.argv[1]).href;
if (isEntrypoint) {
main();
}