156 lines
13 KiB
JavaScript
156 lines
13 KiB
JavaScript
#!/usr/bin/env node
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
|
|
const requiredFiles = [
|
|
"unraid/install.sh",
|
|
"unraid/projectbrain.xml",
|
|
"unraid/projectbrain-icon.png",
|
|
"unraid/README.md",
|
|
"docker-compose.yml",
|
|
"Dockerfile.unraid",
|
|
"docker/nginx.single.conf",
|
|
"scripts/deploy-unraid.sh",
|
|
"scripts/deploy-unraid-single.sh",
|
|
"scripts/unraid-single-entrypoint.sh",
|
|
"docker/unraid/README.md",
|
|
"docker/unraid/projectbrain.env.example",
|
|
"docker/unraid/projectbrain-container.env.example",
|
|
"docker/unraid/projectbrain-compose-template.xml",
|
|
"docker/unraid/projectbrain-container-template.xml",
|
|
"docker/unraid/projectbrain-icon.png",
|
|
"docs/DOCKER_UNRAID_SETUP.md",
|
|
];
|
|
|
|
const failures = [];
|
|
const read = (file) => readFileSync(file, "utf8");
|
|
|
|
for (const file of requiredFiles) {
|
|
if (!existsSync(file)) failures.push(`Missing required file: ${file}`);
|
|
}
|
|
|
|
const compose = existsSync("docker-compose.yml") ? read("docker-compose.yml") : "";
|
|
const env = existsSync("docker/unraid/projectbrain.env.example") ? read("docker/unraid/projectbrain.env.example") : "";
|
|
const containerEnv = existsSync("docker/unraid/projectbrain-container.env.example") ? read("docker/unraid/projectbrain-container.env.example") : "";
|
|
const xml = existsSync("docker/unraid/projectbrain-compose-template.xml") ? read("docker/unraid/projectbrain-compose-template.xml") : "";
|
|
const containerXml = existsSync("docker/unraid/projectbrain-container-template.xml") ? read("docker/unraid/projectbrain-container-template.xml") : "";
|
|
const icon = existsSync("docker/unraid/projectbrain-icon.png") ? "png-icon" : "";
|
|
const readme = existsSync("docker/unraid/README.md") ? read("docker/unraid/README.md") : "";
|
|
const docs = existsSync("docs/DOCKER_UNRAID_SETUP.md") ? read("docs/DOCKER_UNRAID_SETUP.md") : "";
|
|
const deploy = existsSync("scripts/deploy-unraid.sh") ? read("scripts/deploy-unraid.sh") : "";
|
|
const singleDockerfile = existsSync("Dockerfile.unraid") ? read("Dockerfile.unraid") : "";
|
|
const singleNginx = existsSync("docker/nginx.single.conf") ? read("docker/nginx.single.conf") : "";
|
|
const singleEntry = existsSync("scripts/unraid-single-entrypoint.sh") ? read("scripts/unraid-single-entrypoint.sh") : "";
|
|
const singleDeploy = existsSync("scripts/deploy-unraid-single.sh") ? read("scripts/deploy-unraid-single.sh") : "";
|
|
const unraidInstall = existsSync("unraid/install.sh") ? read("unraid/install.sh") : "";
|
|
const unraidTemplate = existsSync("unraid/projectbrain.xml") ? read("unraid/projectbrain.xml") : "";
|
|
const unraidIcon = existsSync("unraid/projectbrain-icon.png") ? "png-icon" : "";
|
|
const unraidReadme = existsSync("unraid/README.md") ? read("unraid/README.md") : "";
|
|
|
|
const mustContain = [
|
|
[compose, "${PROJECTBRAIN_WEB_PORT:-1209}:80", "docker-compose.yml must keep the frontend host port configurable."],
|
|
[compose, "${POSTGRES_PASSWORD:?", "Production compose must require a PostgreSQL password."],
|
|
[compose, "${PROJECTBRAIN_OWNER_PASSWORD:?", "Production compose must require the owner password."],
|
|
[compose, "${PROJECTBRAIN_AUTH_SECRET:?", "Production compose must require the authentication secret."],
|
|
[unraidInstall, "templates-user", "Root Unraid installer must write a DockerMan template."],
|
|
[unraidInstall, "dockerMan/images", "Root Unraid installer must write a DockerMan icon."],
|
|
[unraidInstall, "git clone", "Root Unraid installer must support installing from Gitea."],
|
|
[unraidInstall, "deploy-unraid-single.sh", "Root Unraid installer must deploy the single-container app."],
|
|
[unraidTemplate, "<Name>projectbrain</Name>", "Root Unraid template name must match the running container name so DockerMan can show Edit."],
|
|
[unraidTemplate, "<Repository>projectbrain-unraid:latest</Repository>", "Root Unraid template must reference the built ProjectBrain image."],
|
|
[unraidTemplate, "Target=\"80\"", "Root Unraid template must expose container port 80."],
|
|
[unraidTemplate, "projectbrain-icon.png", "Root Unraid template must reference the PNG icon."],
|
|
[unraidTemplate, "<ExtraParams>--label net.unraid.docker.managed=dockerman", "Root Unraid template must create DockerMan-managed containers."],
|
|
[unraidTemplate, "Target=\"PROJECTBRAIN_OWNER_PASSWORD\" Default=\"\"", "Root Unraid template must require an explicit owner password."],
|
|
[unraidTemplate, "Target=\"PROJECTBRAIN_AUTH_SECRET\" Default=\"\"", "Root Unraid template must require an explicit authentication secret."],
|
|
[unraidTemplate, "Target=\"POSTGRES_PASSWORD\" Default=\"\"", "Root Unraid template must not ship an embedded PostgreSQL password."],
|
|
[unraidIcon, "png-icon", "Root Unraid PNG icon must exist."],
|
|
[unraidReadme, "Docker > Add Container > Template: ProjectBrain", "Root Unraid README must explain where the template appears."],
|
|
[env, "PROJECTBRAIN_WEB_PORT=1209", "Unraid env template must expose PROJECTBRAIN_WEB_PORT."],
|
|
[env, "VITE_PROJECTBRAIN_CLIENT_MODE=real", "Unraid env template must default the frontend to real mode."],
|
|
[env, "PROJECTBRAIN_DATA_SOURCE=database", "Unraid env template must default backend data source to database."],
|
|
[containerEnv, "PROJECTBRAIN_IMAGE=projectbrain-unraid:latest", "Single-container env template must expose the local image name."],
|
|
[containerEnv, "PROJECTBRAIN_DOCKER_NETWORK=auto", "Single-container env template must default Docker network detection to auto."],
|
|
[containerEnv, "PROJECTBRAIN_STOP_COMPOSE_APP_SERVICES=true", "Single-container env template must stop old compose app containers by default."],
|
|
[containerEnv, "PROJECTBRAIN_USE_EXTERNAL_DATABASE=false", "Single-container env template must default to embedded Postgres."],
|
|
[containerEnv, "PROJECTBRAIN_DEPLOY_NO_CACHE=false", "Single-container env template must default to cached deploys."],
|
|
[containerEnv, "PROJECTBRAIN_MIN_DOCKER_FREE_MB=2048", "Single-container env template must expose Docker storage threshold."],
|
|
[containerEnv, "PROJECTBRAIN_REQUIRE_FRESH_BACKUP=false", "Single-container env template must expose backup freshness gate."],
|
|
[containerEnv, "POSTGRES_PASSWORD=", "Single-container env template must expose the embedded Postgres secret input."],
|
|
[xml, "<Name>ProjectBrain</Name>", "Unraid XML template must name ProjectBrain."],
|
|
[xml, "PROJECTBRAIN_WEB_PORT", "Unraid XML template must expose the web port variable."],
|
|
[xml, "<WebUI>http://[IP]:[PORT:1209]</WebUI>", "Unraid XML template must define the WebUI URL."],
|
|
[xml, "projectbrain-icon.png", "Unraid XML template must reference the PNG icon asset."],
|
|
[xml, "POSTGRES_PASSWORD", "Unraid XML template must expose Postgres password."],
|
|
[containerXml, "<Name>projectbrain</Name>", "Single-container XML template name must match the running container name."],
|
|
[containerXml, "<Repository>projectbrain-unraid:latest</Repository>", "Single-container XML template must point at the local ProjectBrain image."],
|
|
[containerXml, "<ExtraParams>--label net.unraid.docker.managed=dockerman", "Single-container XML template must create DockerMan-managed containers."],
|
|
[containerXml, "Target=\"80\"", "Single-container XML template must expose container port 80."],
|
|
[containerXml, "Target=\"/var/lib/projectbrain\"", "Single-container XML template must map persistent appdata."],
|
|
[containerXml, "Target=\"PROJECTBRAIN_OWNER_PASSWORD\" Default=\"\"", "Single-container XML template must require an explicit owner password."],
|
|
[containerXml, "Target=\"PROJECTBRAIN_AUTH_SECRET\" Default=\"\"", "Single-container XML template must require an explicit authentication secret."],
|
|
[containerXml, "Target=\"POSTGRES_PASSWORD\" Default=\"\"", "Single-container XML template must not ship an embedded PostgreSQL password."],
|
|
[containerXml, "projectbrain-icon.png", "Single-container XML template must reference the PNG icon asset."],
|
|
[icon, "png-icon", "Unraid PNG icon must exist."],
|
|
[singleDockerfile, "nginx.single.conf", "Single-container Dockerfile must use the single-container nginx config."],
|
|
[singleDockerfile, "unraid-single-entrypoint.sh", "Single-container Dockerfile must use the single-container entrypoint."],
|
|
[singleDockerfile, "HEALTHCHECK", "Single-container Dockerfile must define a healthcheck."],
|
|
[singleNginx, "127.0.0.1:4000", "Single-container nginx config must proxy API calls to the internal backend."],
|
|
[singleEntry, "DATABASE_URL is required", "Single-container entrypoint must fail clearly without DATABASE_URL."],
|
|
[singleEntry, "POSTGRES_PASSWORD is required for embedded PostgreSQL", "Single-container entrypoint must fail closed without an embedded PostgreSQL password."],
|
|
[singleEntry, "PROJECTBRAIN_USE_EXTERNAL_DATABASE", "Single-container entrypoint must default to embedded Postgres unless external DB is requested."],
|
|
[singleEntry, "initdb", "Single-container entrypoint must initialize embedded Postgres."],
|
|
[singleEntry, "nginx -g", "Single-container entrypoint must start nginx."],
|
|
[readme, "Change The External Port", "Unraid README must explain port changes."],
|
|
[readme, "Single Container", "Unraid README must document the single-container template."],
|
|
[readme, "scripts/deploy-unraid.sh", "Unraid README must reference the deploy helper."],
|
|
[readme, "scripts/deploy-unraid-single.sh", "Unraid README must reference the single-container deploy helper."],
|
|
[readme, "docker compose up -d --build", "Unraid README must include restart command."],
|
|
[docs, "projectbrain.env.example", "Docker/Unraid docs must reference the Unraid env template."],
|
|
[docs, "projectbrain-container-template.xml", "Docker/Unraid docs must reference the single-container XML template."],
|
|
[docs, "scripts/deploy-unraid.sh", "Docker/Unraid docs must reference the deploy helper."],
|
|
[docs, "scripts/deploy-unraid-single.sh", "Docker/Unraid docs must reference the single-container deploy helper."],
|
|
[deploy, "git pull --ff-only", "Unraid deploy helper must pull the latest repository state."],
|
|
[deploy, "projectbrain.env.example", "Unraid deploy helper must apply the env template."],
|
|
[deploy, "docker compose build --no-cache", "Unraid deploy helper must rebuild the stack."],
|
|
[deploy, "api/system/readiness", "Unraid deploy helper must check readiness."],
|
|
[singleDeploy, "Dockerfile.unraid", "Single-container deploy helper must build Dockerfile.unraid."],
|
|
[singleDeploy, "PROJECTBRAIN_DEPLOY_NO_CACHE", "Single-container deploy helper must make no-cache configurable."],
|
|
[singleDeploy, "docker build -f Dockerfile.unraid", "Single-container deploy helper must allow cached builds by default."],
|
|
[singleDeploy, "Docker storage preflight", "Single-container deploy helper must show Docker storage preflight."],
|
|
[singleDeploy, "docker system df", "Single-container deploy helper must print Docker storage usage."],
|
|
[singleDeploy, "PROJECTBRAIN_MIN_DOCKER_FREE_MB", "Single-container deploy helper must expose Docker free-space threshold."],
|
|
[singleDeploy, "backup freshness preflight", "Single-container deploy helper must check backup freshness."],
|
|
[singleDeploy, "PROJECTBRAIN_REQUIRE_FRESH_BACKUP", "Single-container deploy helper must expose strict backup gate."],
|
|
[singleDeploy, "git rev-parse --short HEAD", "Single-container deploy helper must report the current git commit."],
|
|
[singleDeploy, "started ${CONTAINER_NAME}", "Single-container deploy helper must report container replacement id."],
|
|
[singleDeploy, "projectbrain-postgres", "Single-container deploy helper must auto-detect the Postgres container network."],
|
|
[singleDeploy, "projectbrain-frontend projectbrain-api", "Single-container deploy helper must remove old compose app containers for live cutover."],
|
|
[singleDeploy, "projectbrain-postgres", "Single-container deploy helper must remove the old standalone ProjectBrain Postgres container."],
|
|
[singleDeploy, "projectbrain-icon.png", "Single-container deploy helper must set the Unraid PNG icon label."],
|
|
[singleDeploy, "docker run -d", "Single-container deploy helper must run one app container."],
|
|
[singleDeploy, "projectbrain-container.env.example", "Single-container deploy helper must apply the container env template."],
|
|
[singleDeploy, "api/system/readiness", "Single-container deploy helper must check readiness."],
|
|
];
|
|
|
|
for (const [haystack, needle, message] of mustContain) {
|
|
if (!haystack.includes(needle)) failures.push(message);
|
|
}
|
|
|
|
for (const secretName of ["POSTGRES_PASSWORD", "PROJECTBRAIN_OWNER_PASSWORD", "PROJECTBRAIN_AUTH_SECRET"]) {
|
|
if (new RegExp(`^${secretName}=.+$`, "m").test(containerEnv)) failures.push(`Single-container env template must not ship a default ${secretName}.`);
|
|
}
|
|
|
|
for (const [templateName, template] of [["root", unraidTemplate], ["single-container", containerXml]]) {
|
|
if (/Target="POSTGRES_PASSWORD"\s+Default="projectbrain"/i.test(template)) failures.push(`${templateName} Unraid template must not ship the historical PostgreSQL password.`);
|
|
if (/Target="PROJECTBRAIN_OWNER_PASSWORD"\s+Default="[^"]+"/i.test(template)) failures.push(`${templateName} Unraid template must not ship an owner password.`);
|
|
if (/Target="PROJECTBRAIN_AUTH_SECRET"\s+Default="[^"]+"/i.test(template)) failures.push(`${templateName} Unraid template must not ship an authentication secret.`);
|
|
}
|
|
|
|
if (failures.length) {
|
|
console.error("ProjectBrain Unraid template readiness: FAIL");
|
|
for (const failure of failures) console.error(`- ${failure}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log("ProjectBrain Unraid template readiness: PASS");
|