mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Source rename across the monorepo. Every publishable package now ships
under its @hexclave/* name natively, no rewrite-at-publish indirection.
Workflow + tooling:
- Delete scripts/rewrite-packages-to-hexclave.ts (one-shot mirror).
- Remove the mirror-publish block from .github/workflows/npm-publish.yaml.
The remaining `pnpm publish -r` step publishes @hexclave/* natively.
- Flip the auto-bump changeset target from @stackframe/stack to
@hexclave/next so 'Update package versions on dev' keeps working.
- Delete packages/template/src/internal/deprecation-warning.ts and its
imports — @hexclave/* never warns about itself, and after PR 3 no
@stackframe/* artifact is ever built from source again.
Package renames (publishable):
@stackframe/react → @hexclave/react
@stackframe/stack → @hexclave/next
@stackframe/js → @hexclave/js
@stackframe/stack-shared → @hexclave/shared
@stackframe/stack-ui → @hexclave/ui
@stackframe/stack-sc → @hexclave/sc
@stackframe/stack-cli → @hexclave/cli
@stackframe/tanstack-start → @hexclave/tanstack-start
@stackframe/dashboard-ui-components → @hexclave/dashboard-ui-components
Internal monorepo packages (private, never published) also renamed for
brand consistency: backend, dashboard, docs, mcp, skills, e2e-tests,
example apps, the swift-sdk, the monorepo root, etc. Cost is mechanical;
payoff is no stray @stackframe/* names left under apps/, examples/, sdks/.
Carve-outs intentionally kept under their legacy names:
- @stackframe/emails — virtual module imported by customer-stored email
templates; the renderer in apps/backend/src/lib/email-rendering.tsx
dual-aliases both names to the same backing module indefinitely.
- @stackframe/template — internal codegen source, never published; per
docs-mintlify/migration.mdx 'internal packages keep names'.
- @stackframe/init-stack — deprecated; now marked private: true so the
last published version on npm continues to serve old install commands
but the workspace stops publishing it.
Backward-compat detection (so projects still on the last @stackframe/*
release keep working):
- packages/stack-shared/src/config-rendering.ts — CONFIG_IMPORT_PACKAGES
table includes both @hexclave/* (canonical, first match wins) and
legacy @stackframe/* names. Function renamed
detectStackframeImportPackage → detectConfigImportPackage.
- apps/dashboard/src/lib/github-config-push.ts — import detection regex
now matches both @hexclave/<name> and @stackframe/<name>, hexclave
preferred.
Versions: every renamed package reset to 1.0.0 in source. The repo's
existing 'bump versions before merging to main' flow will move them to
1.0.1 on the first publish run, so the dual-publish 1.0.0 from PR 2 is
not overwritten.
Other touch-ups discovered during sweep:
- Root package.json: 'fern' script filter was @stackframe/docs (legacy
typo, never resolved) → @hexclave/docs.
- README.md contributor note: @stackframe/XYZ → @hexclave/XYZ.
- packages/stack-cli/package.json: register `hexclave` bin alongside
the legacy `stack` bin so `npx @hexclave/cli init` works on the
natively-published artifact (PR 1481's rewrite script did this at
publish time; now it's in source).
- packages/template/package-template.json: per-platform names + version
flipped to hexclave + 1.0.0 to stay in sync with generated package.json.
- docs/package.json (legacy fumadocs folder, otherwise carved out of the
brand sweep): workspace deps and name updated minimally so `pnpm
install` resolves — content (MDX) intentionally untouched per the
PR 2 scoping decision.
Carve-out files (skipped entirely by the sweep, intentional history):
- docs-mintlify/migration.mdx — teaches the rename, references both.
- RENAME-TO-HEXCLAVE.md — planning doc, references both indefinitely.
- legacy docs/ folder — content untouched per PR 2 carve-out.
generate-sdks regenerated packages/{react,stack,js} from template.
pnpm-lock.yaml regenerated. Typecheck green on stack-shared, stack, js,
react. Dashboard typecheck has pre-existing 'X is of type unknown'
errors that need to be investigated separately (likely a local
node_modules build state issue, not source).
291 lines
12 KiB
Docker
291 lines
12 KiB
Docker
# Hexclave Local Emulator — All-in-One Image
|
|
# Packages: PostgreSQL 16, Redis 7, Inbucket, Svix, ClickHouse, MinIO, QStash
|
|
# + built Hexclave backend and dashboard
|
|
|
|
ARG NODE_VERSION=22.21.1
|
|
|
|
# ── Node.js build stages ──────────────────────────────────────────────────────
|
|
|
|
FROM node:${NODE_VERSION} AS node-base
|
|
|
|
WORKDIR /app
|
|
|
|
RUN apt-get update && \
|
|
apt-get upgrade -y && \
|
|
rm -rf /var/lib/apt/lists
|
|
|
|
ENV PNPM_HOME=/pnpm
|
|
ENV PATH=$PNPM_HOME:$PATH
|
|
|
|
RUN corepack enable
|
|
RUN corepack prepare pnpm@10.23.0 --activate
|
|
RUN pnpm add -g turbo
|
|
RUN pnpm add -g tsx
|
|
|
|
|
|
FROM node-base AS pruner
|
|
|
|
COPY . .
|
|
|
|
RUN tsx ./scripts/generate-sdks.ts
|
|
|
|
# https://turbo.build/repo/docs/guides/tools/docker
|
|
RUN turbo prune --scope=@hexclave/backend --scope=@hexclave/dashboard --docker
|
|
|
|
|
|
FROM node-base AS builder
|
|
|
|
# copy over package.json files and install dependencies
|
|
COPY --from=pruner /app/out/json/ .
|
|
COPY --from=pruner /app/out/pnpm-lock.yaml .
|
|
COPY .gitignore .
|
|
COPY pnpm-workspace.yaml .
|
|
COPY turbo.json .
|
|
COPY configs ./configs
|
|
COPY --from=pruner /app/scripts/postinstall-patch-next-async-debug-info.mjs ./scripts/
|
|
RUN --mount=type=cache,id=pnpm,target=/pnpm/store STACK_SKIP_TEMPLATE_GENERATION=true pnpm install --frozen-lockfile
|
|
|
|
# copy over the rest of the code for the build
|
|
COPY --from=pruner /app/out/full/ .
|
|
|
|
# docs are currently required for the NextJS backend build, but won't exist in the final image
|
|
COPY docs ./docs
|
|
|
|
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
|
ENV NEXT_CONFIG_OUTPUT=standalone
|
|
ENV NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=pk_test_mock_publishable_key_for_local_emulator
|
|
|
|
# Build the backend NextJS app
|
|
RUN pnpm turbo run docker-build --filter=@hexclave/backend... --filter=@hexclave/dashboard...
|
|
|
|
# Build the self-host seed script.
|
|
# tsdown -> rolldown is multi-threaded Rust; under qemu-user (cross-arch
|
|
# arm64-on-amd64) its futex emulation occasionally deadlocks and the build
|
|
# hangs forever. Bound each attempt and retry to ride out the race.
|
|
RUN cd apps/backend && \
|
|
attempt=1; \
|
|
while :; do \
|
|
timeout --kill-after=30s 600s pnpm build-self-host-migration-script && break; \
|
|
rc=$?; \
|
|
if [ "$attempt" -ge 3 ]; then \
|
|
echo "build-self-host-migration-script failed after $attempt attempts (last rc=$rc)" >&2; \
|
|
exit "$rc"; \
|
|
fi; \
|
|
echo "build-self-host-migration-script attempt $attempt failed (rc=$rc); retrying..." >&2; \
|
|
attempt=$((attempt + 1)); \
|
|
done
|
|
|
|
|
|
# Prune node_modules for runtime: remove dev tools, heavy UI packages,
|
|
# duplicate framework copies, and native binaries not needed by the
|
|
# migration script or server at runtime.
|
|
FROM builder AS migration-pruner
|
|
RUN cp -a /app/node_modules /pruned-node_modules && \
|
|
cd /pruned-node_modules/.pnpm && \
|
|
rm -rf \
|
|
# Dev tools (never needed at runtime)
|
|
typescript@* eslint@* eslint-*@* @typescript-eslint+*@* \
|
|
prettier@* vitest@* jsdom@* turbo@* turbo-*@* \
|
|
tsdown@* @changesets+*@* codebuff@* \
|
|
@testing-library+*@* vite@* vite-*@* @vitejs+*@* \
|
|
# Heavy UI packages (already traced into Next.js standalone bundles)
|
|
monaco-editor@* \
|
|
three@* three-globe@* globe.gl@* react-globe*@* \
|
|
react-icons@* lucide-react@* @phosphor-icons+*@* \
|
|
# Large optional packages not needed by migration script
|
|
posthog-js@* \
|
|
@prisma+studio-core@* @prisma+dev@* @prisma+query-plan-executor@* \
|
|
convex@* @electric-sql+*@* \
|
|
next@14* @next+swc-*@14* \
|
|
# Native build binaries not needed at runtime
|
|
@esbuild+*@* esbuild@* @rolldown+*@* \
|
|
# Duplicate date-fns versions (keep v4 only)
|
|
date-fns@2* date-fns@3*
|
|
|
|
|
|
# ── Freestyle mock build ─────────────────────────────────────────────────────
|
|
|
|
FROM node-base AS freestyle-mock-builder
|
|
WORKDIR /freestyle-mock
|
|
COPY docker/dependencies/freestyle-mock/Dockerfile /tmp/freestyle-mock-dockerfile
|
|
# Extract the inline package.json and server.mjs from the Dockerfile's RUN cat commands,
|
|
# then install dependencies. This avoids duplicating the source.
|
|
RUN node -e " \
|
|
const fs = require('fs'); \
|
|
const df = fs.readFileSync('/tmp/freestyle-mock-dockerfile', 'utf8'); \
|
|
const pkgMatch = df.match(/cat <<'EOF' > package\\.json\\n([\\s\\S]*?)\\nEOF/); \
|
|
fs.writeFileSync('package.json', pkgMatch[1]); \
|
|
const srvMatch = df.match(/cat <<'EOF' > server\\.mjs\\n([\\s\\S]*?)\\nEOF/); \
|
|
let server = srvMatch[1]; \
|
|
server = server.replace( \
|
|
'from \"fs/promises\"', \
|
|
'from \"fs/promises\"; import { symlinkSync } from \"fs\"' \
|
|
); \
|
|
server = server.replace( \
|
|
'await mkdir(workDir, { recursive: true });', \
|
|
'await mkdir(workDir, { recursive: true }); try { symlinkSync(\"/app/freestyle-mock/node_modules\", join(workDir, \"node_modules\")); } catch {}' \
|
|
); \
|
|
fs.writeFileSync('server.mjs', server); \
|
|
"
|
|
RUN npm install
|
|
|
|
|
|
# ── Mock OAuth server build ───────────────────────────────────────────────────
|
|
|
|
FROM node-base AS mock-oauth-builder
|
|
WORKDIR /mock-oauth
|
|
COPY apps/mock-oauth-server/package.json .
|
|
RUN pnpm install && pnpm add esbuild --save-dev
|
|
COPY apps/mock-oauth-server/src ./src
|
|
RUN npx esbuild src/index.ts --bundle --platform=node --target=node22 --outfile=dist/index.cjs
|
|
|
|
|
|
# ── Service binary stages ─────────────────────────────────────────────────────
|
|
|
|
FROM stripe/stripe-mock:v0.195.0 AS stripe-mock-bin
|
|
FROM inbucket/inbucket:3.1.0 AS inbucket-bin
|
|
FROM svix/svix-server:v1.88.0 AS svix-bin
|
|
FROM clickhouse/clickhouse-server:25.10 AS clickhouse-bin
|
|
FROM minio/minio:RELEASE.2025-09-07T16-13-09Z AS minio-bin
|
|
FROM minio/mc:RELEASE.2025-02-21T16-00-46Z AS mc-bin
|
|
|
|
FROM bgodil/qstash:latest AS qstash-bin
|
|
RUN cp $(which qstash) /qstash-binary 2>/dev/null || \
|
|
cp $(find / -name 'qstash' -type f -executable 2>/dev/null | head -1) /qstash-binary || \
|
|
{ echo "ERROR: qstash binary not found" >&2; exit 1; }
|
|
|
|
|
|
# ── Strip / compress service binaries (parallel stages) ──────────────────────
|
|
|
|
FROM debian:trixie-slim AS upx-compress
|
|
RUN apt-get update && apt-get install -y --no-install-recommends upx-ucl binutils && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
COPY --from=clickhouse-bin /usr/bin/clickhouse /out/clickhouse
|
|
COPY --from=svix-bin /usr/local/bin/svix-server /out/svix-server
|
|
COPY --from=minio-bin /usr/bin/minio /out/minio
|
|
COPY --from=mc-bin /usr/bin/mc /out/mc
|
|
COPY --from=qstash-bin /qstash-binary /out/qstash
|
|
RUN chmod u+w /out/* && \
|
|
# Intentionally NOT stripping /out/clickhouse. The clickhouse binary is a
|
|
# self-extracting compressed executable (a small loader with a ZSTD
|
|
# payload appended after the section table); strip rewrites the ELF and
|
|
# can invalidate the loader's "find my payload" lookup, causing the
|
|
# decompressor to spin on garbage with zero log output — the exact
|
|
# symptom seen on cross-arch TCG runs. Savings from stripping would be
|
|
# only the tiny bootstrap anyway since the payload isn't in any section.
|
|
strip --strip-all /out/minio /out/svix-server /out/mc /out/qstash && \
|
|
upx -9 /out/minio /out/svix-server /out/mc /out/qstash
|
|
|
|
|
|
# ── Final image ───────────────────────────────────────────────────────────────
|
|
|
|
FROM debian:trixie-slim
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends \
|
|
gnupg2 \
|
|
lsb-release \
|
|
curl \
|
|
ca-certificates \
|
|
&& echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
|
|
> /etc/apt/sources.list.d/pgdg.list \
|
|
&& curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
|
|
| gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
postgresql-16 \
|
|
postgresql-client-16 \
|
|
redis-server \
|
|
supervisor \
|
|
gosu \
|
|
procps \
|
|
libssl3 \
|
|
openssl \
|
|
socat \
|
|
&& apt-get purge -y --auto-remove gnupg2 lsb-release \
|
|
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man /usr/share/i18n
|
|
|
|
# Node.js runtime (binary only — app bundles include all JS dependencies)
|
|
COPY --from=node-base /usr/local/bin/node /usr/local/bin/node
|
|
|
|
# Inbucket
|
|
COPY --from=inbucket-bin /opt/inbucket /opt/inbucket
|
|
|
|
# Stripe mock
|
|
COPY --from=stripe-mock-bin /bin/stripe-mock /usr/local/bin/stripe-mock
|
|
|
|
# Svix (UPX-compressed)
|
|
COPY --from=upx-compress /out/svix-server /usr/local/bin/svix-server
|
|
|
|
# ClickHouse (stripped only)
|
|
COPY --from=upx-compress /out/clickhouse /usr/bin/clickhouse
|
|
RUN ln -sf /usr/bin/clickhouse /usr/bin/clickhouse-server && \
|
|
ln -sf /usr/bin/clickhouse /usr/bin/clickhouse-client
|
|
|
|
# MinIO (UPX-compressed)
|
|
COPY --from=upx-compress /out/minio /usr/local/bin/minio
|
|
COPY --from=upx-compress /out/mc /usr/local/bin/mc
|
|
|
|
# QStash (UPX-compressed)
|
|
COPY --from=upx-compress --chmod=755 /out/qstash /usr/local/bin/qstash
|
|
|
|
# App
|
|
WORKDIR /app
|
|
COPY --from=builder /app/apps/backend/.next/standalone ./
|
|
COPY --from=builder /app/apps/backend/.next/static ./apps/backend/.next/static
|
|
COPY --from=builder /app/apps/backend/prisma ./apps/backend/prisma
|
|
COPY --from=builder /app/apps/backend/dist ./apps/backend/dist
|
|
COPY --from=builder /app/apps/backend/node_modules ./apps/backend/node_modules
|
|
COPY --from=builder /app/apps/dashboard/.next/standalone ./
|
|
COPY --from=builder /app/apps/dashboard/.next/static ./apps/dashboard/.next/static
|
|
COPY --from=builder /app/apps/dashboard/public ./apps/dashboard/public
|
|
# Save the standalone-traced node_modules (runtime deps only) before the full
|
|
# migration-pruner copy overwrites it. The slim-docker-image step in the QEMU
|
|
# build restores this after migrations are baked in.
|
|
RUN cp -a /app/node_modules /app/node_modules.standalone 2>/dev/null || mkdir -p /app/node_modules.standalone
|
|
COPY --from=migration-pruner /pruned-node_modules ./node_modules
|
|
COPY --from=builder /app/packages ./packages
|
|
|
|
# Mock OAuth server (bundled single file)
|
|
COPY --from=mock-oauth-builder /mock-oauth/dist/index.cjs /app/mock-oauth-server/index.cjs
|
|
|
|
# Freestyle mock (JS execution for email rendering)
|
|
COPY --from=freestyle-mock-builder /freestyle-mock /app/freestyle-mock
|
|
COPY --from=node-base /usr/local/bin/npm /usr/local/bin/npm
|
|
COPY --from=node-base /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/npm
|
|
|
|
RUN mkdir -p \
|
|
/data/postgres \
|
|
/data/redis \
|
|
/data/clickhouse \
|
|
/data/clickhouse/access \
|
|
/data/clickhouse/tmp \
|
|
/data/clickhouse/user_files \
|
|
/data/clickhouse/format_schemas \
|
|
/data/minio \
|
|
/data/inbucket \
|
|
/var/log/supervisor \
|
|
/var/log/clickhouse \
|
|
/etc/clickhouse-server \
|
|
&& chown -R postgres:postgres /data/postgres
|
|
|
|
COPY docker/local-emulator/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
|
COPY docker/local-emulator/run-cron-jobs.sh /run-cron-jobs.sh
|
|
COPY docker/local-emulator/entrypoint.sh /entrypoint.sh
|
|
COPY docker/local-emulator/init-services.sh /init-services.sh
|
|
COPY docker/local-emulator/start-app.sh /start-app.sh
|
|
COPY docker/local-emulator/rotate-secrets.sh /usr/local/bin/rotate-secrets
|
|
COPY docker/local-emulator/clickhouse-config.xml /etc/clickhouse-server/config.xml
|
|
COPY docker/local-emulator/clickhouse-users.xml /etc/clickhouse-server/users.xml
|
|
COPY docker/server/entrypoint.sh /app-entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh /init-services.sh /start-app.sh /app-entrypoint.sh /run-cron-jobs.sh /usr/local/bin/rotate-secrets
|
|
|
|
# PostgreSQL: 5432, Redis: 6379, Inbucket: 2500/9001/1100,
|
|
# Svix: 8071, ClickHouse: 8123/9009, MinIO: 9090, QStash: 8080
|
|
# Backend: 8102, Dashboard: 8101, Mock OAuth: 8114
|
|
EXPOSE 5432 6379 2500 9001 1100 8071 8123 9009 9090 8080 8101 8102 8114
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|