build(container): use turbo pruned web workspace #36
5 changed files with 58 additions and 37 deletions
|
|
@ -5,6 +5,7 @@ coverage
|
|||
data
|
||||
dist
|
||||
node_modules
|
||||
out
|
||||
playwright-report
|
||||
storybook-static
|
||||
test-results
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
node_modules/
|
||||
out/
|
||||
.svelte-kit/
|
||||
build/
|
||||
dist/
|
||||
|
|
|
|||
|
|
@ -139,10 +139,11 @@ the checked-in `apps/web/drizzle/` directory so startup migrations can run.
|
|||
|
||||
### Internal Container
|
||||
|
||||
The checked-in `apps/web/Containerfile` builds the React client and Bun server
|
||||
from the workspace root into a runtime image. For the Dimension Lab internal
|
||||
host, run it behind Caddy on a loopback port and mount persistent state at
|
||||
`/data`:
|
||||
The checked-in `apps/web/Containerfile` runs
|
||||
`turbo prune @dimensionlab/web --docker`, installs the pruned manifest set, and
|
||||
builds the React client plus Bun server from the pruned workspace source. For the
|
||||
Dimension Lab internal host, run it behind Caddy on a loopback port and mount
|
||||
persistent state at `/data`:
|
||||
|
||||
```sh
|
||||
podman build -f apps/web/Containerfile -t localhost/dimensionlab-website:latest .
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
FROM docker.io/oven/bun:1.3.14 AS deps
|
||||
FROM docker.io/oven/bun:1.3.14 AS base
|
||||
|
||||
WORKDIR /repo
|
||||
ENV PATH=/repo/apps/web/node_modules/.bin:/repo/packages/ui/node_modules/.bin:/repo/node_modules/.bin:$PATH
|
||||
COPY package.json bun.lock turbo.json tsconfig.base.json ./
|
||||
COPY apps/web/package.json apps/web/package.json
|
||||
COPY packages/dashboard-model/package.json packages/dashboard-model/package.json
|
||||
COPY packages/ui/package.json packages/ui/package.json
|
||||
ENV PATH=/repo/apps/web/node_modules/.bin:/repo/packages/dashboard-model/node_modules/.bin:/repo/packages/ui/node_modules/.bin:/repo/node_modules/.bin:$PATH
|
||||
|
||||
FROM base AS pruner
|
||||
|
||||
COPY . .
|
||||
RUN bunx turbo prune @dimensionlab/web --docker
|
||||
|
||||
FROM base AS deps
|
||||
|
||||
COPY --from=pruner /repo/out/json/ ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
FROM deps AS build
|
||||
|
||||
COPY . .
|
||||
RUN bun install --frozen-lockfile && bun run build
|
||||
COPY --from=pruner /repo/out/full/ ./
|
||||
COPY --from=pruner /repo/tsconfig.base.json /repo/tsconfig.json ./
|
||||
RUN bun run build
|
||||
|
||||
FROM deps AS runtime
|
||||
FROM base AS runtime
|
||||
|
||||
WORKDIR /repo/apps/web
|
||||
ENV NODE_ENV=production
|
||||
|
|
|
|||
|
|
@ -114,35 +114,47 @@ describe("workspace boundaries", () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("stages web workspace dependency manifests before container install", () => {
|
||||
const webPackage = JSON.parse(
|
||||
readFileSync(join(root, "apps/web/package.json"), "utf8"),
|
||||
) as { dependencies?: Record<string, string> };
|
||||
const workspacePackages = [
|
||||
"packages/ui/package.json",
|
||||
"packages/dashboard-model/package.json",
|
||||
].map((manifestPath) => {
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(join(root, manifestPath), "utf8"),
|
||||
) as { name?: string };
|
||||
|
||||
return [packageJson.name, manifestPath] as const;
|
||||
});
|
||||
const manifestsByPackageName = new Map(workspacePackages);
|
||||
test("builds the internal container from a turbo-pruned web workspace", () => {
|
||||
const containerfile = readFileSync(
|
||||
join(root, "apps/web/Containerfile"),
|
||||
"utf8",
|
||||
);
|
||||
const pruneCommand = "turbo prune @dimensionlab/web --docker";
|
||||
const jsonCopy = "COPY --from=pruner /repo/out/json/ ./";
|
||||
const sourceCopy = "COPY --from=pruner /repo/out/full/ ./";
|
||||
const tsconfigCopy =
|
||||
"COPY --from=pruner /repo/tsconfig.base.json /repo/tsconfig.json ./";
|
||||
const installCommand = "RUN bun install --frozen-lockfile";
|
||||
|
||||
const workspaceDependencyManifests = Object.entries(
|
||||
webPackage.dependencies ?? {},
|
||||
)
|
||||
.filter(([, version]) => version.startsWith("workspace:"))
|
||||
.map(([packageName]) => manifestsByPackageName.get(packageName));
|
||||
expect(containerfile).toContain("AS pruner");
|
||||
expect(containerfile).toContain(pruneCommand);
|
||||
expect(containerfile).toContain(jsonCopy);
|
||||
expect(containerfile).toContain(sourceCopy);
|
||||
expect(containerfile).toContain(tsconfigCopy);
|
||||
expect(containerfile).not.toContain(
|
||||
"COPY packages/dashboard-model/package.json",
|
||||
);
|
||||
expect(containerfile).not.toContain("COPY packages/ui/package.json");
|
||||
|
||||
expect(workspaceDependencyManifests).not.toContain(undefined);
|
||||
for (const manifestPath of workspaceDependencyManifests) {
|
||||
expect(containerfile).toContain(`COPY ${manifestPath} ${manifestPath}`);
|
||||
}
|
||||
const jsonCopyIndex = containerfile.indexOf(jsonCopy);
|
||||
const installIndex = containerfile.indexOf(installCommand);
|
||||
const sourceCopyIndex = containerfile.indexOf(sourceCopy);
|
||||
const tsconfigCopyIndex = containerfile.indexOf(tsconfigCopy);
|
||||
|
||||
expect(jsonCopyIndex).toBeGreaterThan(-1);
|
||||
expect(installIndex).toBeGreaterThan(jsonCopyIndex);
|
||||
expect(sourceCopyIndex).toBeGreaterThan(installIndex);
|
||||
expect(tsconfigCopyIndex).toBeGreaterThan(sourceCopyIndex);
|
||||
expect(containerfile.indexOf("RUN bun run build")).toBeGreaterThan(
|
||||
tsconfigCopyIndex,
|
||||
);
|
||||
});
|
||||
|
||||
test("keeps local turbo prune output out of git and container contexts", () => {
|
||||
const gitignore = readFileSync(join(root, ".gitignore"), "utf8");
|
||||
const containerignore = readFileSync(join(root, ".containerignore"), "utf8");
|
||||
|
||||
expect(gitignore).toContain("out/");
|
||||
expect(containerignore).toContain("out");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue