paths.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. import { existsSync } from "node:fs";
  2. import { resolve, isAbsolute } from "node:path";
  3. // IMPORTANT: this module imports `node:fs` / `node:path` and must NEVER be
  4. // re-exported from the main `@pipeline/shared` barrel (which client bundles
  5. // import). Reach it only via the `@pipeline/shared/node` subpath.
  6. // Files that mark the monorepo root. Used so CLI (cwd = repo root) and the web
  7. // app (cwd = apps/web) — and the Docker container (cwd = /app/apps/web) — all
  8. // resolve the same unified output directory.
  9. const ROOT_MARKERS = ["pnpm-workspace.yaml", "turbo.json"];
  10. /**
  11. * Walk up from `from` (default: cwd) until a monorepo marker file is found.
  12. * Falls back to `from` when no marker is discovered (e.g. running outside the repo).
  13. */
  14. export function findMonorepoRoot(from: string = process.cwd()): string {
  15. let dir = resolve(from);
  16. for (let i = 0; i < 8; i++) {
  17. if (ROOT_MARKERS.some((m) => existsSync(resolve(dir, m)))) {
  18. return dir;
  19. }
  20. const parent = resolve(dir, "..");
  21. if (parent === dir) break;
  22. dir = parent;
  23. }
  24. return resolve(from);
  25. }
  26. /**
  27. * Resolve the unified output base directory.
  28. *
  29. * Resolution order: `OUTPUT_DIR` env var > `configured` (config/default.yaml
  30. * `output.dir`) > `./output`. Relative paths are resolved against the monorepo
  31. * root (not cwd), so CLI / HTTP / web all land in the same physical directory.
  32. */
  33. export function resolveOutputDir(configured?: string): string {
  34. const raw = process.env.OUTPUT_DIR || configured || "./output";
  35. return isAbsolute(raw) ? raw : resolve(findMonorepoRoot(), raw);
  36. }