publish docker package

This commit is contained in:
2026-07-28 02:39:19 -04:00
parent b19b5b1bd2
commit 03e82380db
4 changed files with 125 additions and 3 deletions
+18
View File
@@ -0,0 +1,18 @@
node_modules
dist
dist-ssr
.git
.gitignore
*.log
npm-debug.log*
pnpm-debug.log*
.vscode
.idea
.DS_Store
Dockerfile
.dockerignore
README.md
+53
View File
@@ -0,0 +1,53 @@
name: docker
on:
push:
branches: [master]
tags: ["v*"]
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha
type=ref,event=tag
type=semver,pattern={{version}}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# keeps the package listing clean - attestations show up in ghcr as
# extra untagged "unknown/unknown" entries
provenance: false
# add linux/arm64 here if the host running this is arm
platforms: linux/amd64
+35
View File
@@ -0,0 +1,35 @@
# syntax=docker/dockerfile:1
# --- build: needs devDependencies (vite) to produce dist/client + dist/server
FROM node:22-alpine AS build
WORKDIR /app
RUN npm install -g pnpm@10
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# --- deps: runtime dependencies only (express, sirv, compression, react)
FROM node:22-alpine AS deps
WORKDIR /app
RUN npm install -g pnpm@10
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile --prod
# --- runtime
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=5173
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json server.js ./
EXPOSE 5173
USER node
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -qO- "http://127.0.0.1:${PORT}/" >/dev/null || exit 1
CMD ["node", "server.js"]
+19 -3
View File
@@ -1,12 +1,18 @@
import fs from "node:fs/promises";
import path from "node:path";
import express from "express";
const isProduction = process.env.NODE_ENV === "production";
const port = process.env.PORT || 5173;
const base = process.env.BASE || "/";
// resolve against this file rather than the working directory, so the server
// boots the same whether it is started from the repo root or anywhere else
const root = import.meta.dirname;
const clientDir = path.resolve(root, "dist/client");
const templateHtml = isProduction
? await fs.readFile("./dist/client/index.html", "utf-8")
? await fs.readFile(path.resolve(clientDir, "index.html"), "utf-8")
: "";
const app = express();
@@ -24,7 +30,17 @@ if (!isProduction) {
const compression = (await import("compression")).default;
const sirv = (await import("sirv")).default;
app.use(compression());
app.use(base, sirv("./dist/client", { extensions: [] }));
app.use(base, sirv(clientDir, { extensions: [] }));
// a hashed asset that sirv did not find is genuinely gone, usually a stale
// cached document asking for a previous build. 404 it here, otherwise it
// falls through to the catch-all and answers a .js request with html
app.use(base, (req, res, next) => {
if (req.path.startsWith("/assets/")) {
res.status(404).end();
return;
}
next();
});
}
app.use("*all", async (req, res) => {
@@ -34,7 +50,7 @@ app.use("*all", async (req, res) => {
let template;
let render;
if (!isProduction) {
template = await fs.readFile("./index.html", "utf-8");
template = await fs.readFile(path.resolve(root, "index.html"), "utf-8");
template = await vite.transformIndexHtml(url, template);
render = (await vite.ssrLoadModule("/src/entry-server.tsx")).render;
} else {