fix: resolve project env from active container runtime path
This commit is contained in:
1 parent
8a27446643
commit
248053844a
3 files changed
+158
-53
No files matched your search
@@ -787,6 +787,46 @@ function getInspectWorkingDir(inspect: unknown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getProjectEnvRuntimeCandidatePaths(
|
||||
inspect: unknown,
|
||||
mounts: DockerInspectMount[],
|
||||
processWorkingDirs: string[] = [],
|
||||
) {
|
||||
const candidates = new Set<string>();
|
||||
const workingDir = getInspectWorkingDir(inspect);
|
||||
|
||||
processWorkingDirs.forEach((path) => {
|
||||
if (isUsableMountPath(path)) {
|
||||
candidates.add(pathPosix.join(normalizeRemotePath(path), ".env"));
|
||||
}
|
||||
});
|
||||
|
||||
if (workingDir) {
|
||||
candidates.add(pathPosix.join(normalizeRemotePath(workingDir), ".env"));
|
||||
}
|
||||
|
||||
mounts.forEach((mount) => {
|
||||
if (isUsableMountPath(mount.Destination)) {
|
||||
candidates.add(
|
||||
pathPosix.join(normalizeRemotePath(mount.Destination!), ".env"),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
[
|
||||
"/app",
|
||||
"/workspace",
|
||||
"/var/www/html",
|
||||
"/bitnami/laravel",
|
||||
"/laravel/laravel",
|
||||
"/srv/app",
|
||||
].forEach((path) => {
|
||||
candidates.add(pathPosix.join(path, ".env"));
|
||||
});
|
||||
|
||||
return Array.from(candidates).slice(0, 12);
|
||||
}
|
||||
|
||||
function redactSecret(message: string, secret?: string | null) {
|
||||
if (!secret) return message;
|
||||
return message.split(secret).join("[REDACTED]");
|
||||
@@ -1967,8 +2007,9 @@ export async function containerRoutes(app: FastifyInstance) {
|
||||
const runtimeId = container.dockerId || container.name;
|
||||
|
||||
try {
|
||||
const containerCandidatePaths = new Set<string>();
|
||||
const checkedContainerPaths: string[] = [];
|
||||
let inspect: unknown = null;
|
||||
const processWorkingDirs: string[] = [];
|
||||
let projectMounts = parseStoredVolumeMounts(container.volumes);
|
||||
|
||||
try {
|
||||
@@ -1977,27 +2018,57 @@ export async function containerRoutes(app: FastifyInstance) {
|
||||
// Keep stored deployment candidates and common fallback paths.
|
||||
}
|
||||
|
||||
if (inspect) {
|
||||
projectMounts = [...getInspectMounts(inspect), ...projectMounts];
|
||||
const workingDir = getInspectWorkingDir(inspect);
|
||||
if (workingDir) {
|
||||
containerCandidatePaths.add(pathPosix.join(workingDir, ".env"));
|
||||
if (container.status === "RUNNING") {
|
||||
const mainProcessWorkingDir =
|
||||
await ssh.getContainerMainProcessWorkingDirectory(
|
||||
container.server,
|
||||
runtimeId,
|
||||
);
|
||||
|
||||
if (mainProcessWorkingDir) {
|
||||
processWorkingDirs.push(mainProcessWorkingDir);
|
||||
}
|
||||
}
|
||||
|
||||
projectMounts.forEach((mount) => {
|
||||
if (isUsableMountPath(mount.Destination)) {
|
||||
containerCandidatePaths.add(
|
||||
pathPosix.join(normalizeRemotePath(mount.Destination!), ".env"),
|
||||
);
|
||||
if (inspect) {
|
||||
projectMounts = [...getInspectMounts(inspect), ...projectMounts];
|
||||
}
|
||||
|
||||
const containerCandidatePaths = getProjectEnvRuntimeCandidatePaths(
|
||||
inspect,
|
||||
projectMounts,
|
||||
processWorkingDirs,
|
||||
);
|
||||
|
||||
if (container.status === "RUNNING") {
|
||||
for (const candidatePath of containerCandidatePaths) {
|
||||
checkedContainerPaths.push(candidatePath);
|
||||
try {
|
||||
const file = await ssh.readContainerFile(
|
||||
container.server,
|
||||
runtimeId,
|
||||
candidatePath,
|
||||
);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: {
|
||||
found: true,
|
||||
path: file.path,
|
||||
content: file.content,
|
||||
checkedPaths: checkedContainerPaths,
|
||||
source: "container",
|
||||
message:
|
||||
"Project .env file loaded from the active container filesystem.",
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// Try the next runtime project path.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
containerCandidatePaths.add("/app/.env");
|
||||
containerCandidatePaths.add("/workspace/.env");
|
||||
containerCandidatePaths.add("/var/www/html/.env");
|
||||
|
||||
const mountCandidatePaths = Array.from(containerCandidatePaths)
|
||||
const mountCandidatePaths = containerCandidatePaths
|
||||
.map((path) =>
|
||||
resolveContainerPathToHostMountPath(path, projectMounts),
|
||||
)
|
||||
@@ -2020,46 +2091,11 @@ export async function containerRoutes(app: FastifyInstance) {
|
||||
});
|
||||
}
|
||||
|
||||
const checkedContainerPaths: string[] = [];
|
||||
if (container.status === "RUNNING") {
|
||||
for (const candidatePath of containerCandidatePaths) {
|
||||
checkedContainerPaths.push(candidatePath);
|
||||
try {
|
||||
const file = await ssh.readContainerFile(
|
||||
container.server,
|
||||
runtimeId,
|
||||
candidatePath,
|
||||
);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: {
|
||||
found: true,
|
||||
path: file.path,
|
||||
content: file.content,
|
||||
checkedPaths: [
|
||||
...data.checkedPaths,
|
||||
...checkedContainerPaths,
|
||||
],
|
||||
source: "container",
|
||||
message:
|
||||
"Project .env file loaded from container filesystem.",
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// Try the next common app path.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: {
|
||||
...data,
|
||||
checkedPaths: [
|
||||
...data.checkedPaths,
|
||||
...checkedContainerPaths,
|
||||
],
|
||||
checkedPaths: [...data.checkedPaths, ...checkedContainerPaths],
|
||||
source: "missing",
|
||||
message:
|
||||
"No project .env file was found at the checked deployment, mounted project, or container paths.",
|
||||
|
||||
@@ -1140,6 +1140,35 @@ export async function listContainerFiles(
|
||||
};
|
||||
}
|
||||
|
||||
export async function getContainerMainProcessWorkingDirectory(
|
||||
server: Server,
|
||||
containerId: string,
|
||||
): Promise<string | null> {
|
||||
const script = [
|
||||
'CWD=$(readlink -f /proc/1/cwd 2>/dev/null || true)',
|
||||
'if [ -z "$CWD" ] || [ ! -d "$CWD" ]; then exit 0; fi',
|
||||
'CWD_B64=$(printf "%s" "$CWD" | base64 | tr -d "\\n")',
|
||||
'printf "__CWD__\t%s\n" "$CWD_B64"',
|
||||
].join("\n");
|
||||
|
||||
try {
|
||||
const stdout = await execContainerShell(
|
||||
server,
|
||||
containerId,
|
||||
script,
|
||||
DOCKER_FILE_LIST_TIMEOUT_MS,
|
||||
);
|
||||
const line = stdout
|
||||
.split("\n")
|
||||
.find((entry) => entry.startsWith("__CWD__\t"));
|
||||
const encodedPath = line?.split("\t")[1];
|
||||
|
||||
return encodedPath ? decodeBase64Value(encodedPath) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function readContainerFile(
|
||||
server: Server,
|
||||
containerId: string,
|
||||
|
||||
@@ -3,6 +3,7 @@ import test from "node:test";
|
||||
|
||||
import {
|
||||
clearContainerMetricsCacheForTest,
|
||||
getProjectEnvRuntimeCandidatePaths,
|
||||
invalidateContainerMetricsCache,
|
||||
parseStoredVolumeMounts,
|
||||
readContainerMetricsWithCache,
|
||||
@@ -174,3 +175,42 @@ test("project env path resolver can use stored container volume bindings", () =>
|
||||
);
|
||||
assert.equal(mounts[0]?.Destination, "/bitnami/laravel");
|
||||
});
|
||||
|
||||
test("project env runtime candidates prefer inspect paths before common fallbacks", () => {
|
||||
const mounts = parseStoredVolumeMounts([
|
||||
"/srv/project:/runtime/project",
|
||||
]);
|
||||
const paths = getProjectEnvRuntimeCandidatePaths(
|
||||
{
|
||||
Config: {
|
||||
WorkingDir: "/runtime/project",
|
||||
},
|
||||
},
|
||||
mounts,
|
||||
);
|
||||
|
||||
assert.deepEqual(paths.slice(0, 4), [
|
||||
"/runtime/project/.env",
|
||||
"/app/.env",
|
||||
"/workspace/.env",
|
||||
"/var/www/html/.env",
|
||||
]);
|
||||
});
|
||||
|
||||
test("project env runtime candidates prefer the main process working directory", () => {
|
||||
const paths = getProjectEnvRuntimeCandidatePaths(
|
||||
{
|
||||
Config: {
|
||||
WorkingDir: "/image/default",
|
||||
},
|
||||
},
|
||||
[],
|
||||
["/app"],
|
||||
);
|
||||
|
||||
assert.deepEqual(paths.slice(0, 3), [
|
||||
"/app/.env",
|
||||
"/image/default/.env",
|
||||
"/workspace/.env",
|
||||
]);
|
||||
});
|
||||
Reference in new issue
Block a user