chore: import upstream snapshot with attribution
docmd CI verification / verify (push) Failing after 0s
docmd CI verification / verify (push) Failing after 0s
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
# --------------------------------------------------------------------
|
||||
# docmd : the minimalist, zero-config documentation generator.
|
||||
#
|
||||
# @package @docmd/core (and ecosystem)
|
||||
# @website https://docmd.io
|
||||
# @repository https://github.com/docmd-io/docmd
|
||||
# @license MIT
|
||||
# @copyright Copyright (c) 2025-present docmd.io
|
||||
#
|
||||
# [docmd-source] - Please do not remove this header.
|
||||
# --------------------------------------------------------------------
|
||||
#
|
||||
# Multi-stage Dockerfile for docmd
|
||||
# Creates a minimal production image with docmd CLI pre-installed
|
||||
#
|
||||
# Usage:
|
||||
# Quick Start (with demo site):
|
||||
# docker run -p 3000:3000 docmd:latest
|
||||
#
|
||||
# Initialize new project in current directory:
|
||||
# docker run -v $(pwd):/workspace docmd:latest init
|
||||
#
|
||||
# Run dev server with your docs:
|
||||
# docker run -v $(pwd)/my-docs:/docs -p 3000:3000 docmd:latest dev
|
||||
#
|
||||
# Build static site:
|
||||
# docker run -v $(pwd)/docs:/docs -v $(pwd)/site:/site docmd:latest build
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Stage 1: Build stage - compiles TypeScript and prepares the application
|
||||
# -----------------------------------------------------------------------------
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Install build dependencies for native modules
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make \
|
||||
g++ \
|
||||
git \
|
||||
curl
|
||||
|
||||
# Install pnpm directly — avoids corepack version resolution issues in CI.
|
||||
# Bump the pinned version here whenever the monorepo's packageManager field changes.
|
||||
RUN npm install -g pnpm@10.33.2
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy everything (excluding files via .dockerignore)
|
||||
COPY . .
|
||||
|
||||
# Install dependencies
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Stub out packages that can't build in Docker before running the full build:
|
||||
# - engine-rust-binaries: requires cargo; pre-built .node files are in the repo
|
||||
# - _playground: dev sandbox, not part of the production image
|
||||
RUN sh tools/docker-prebuild.sh
|
||||
|
||||
# Build all packages
|
||||
RUN pnpm -r run build
|
||||
|
||||
# Resolve workspace:* deps to real version numbers for standalone use
|
||||
RUN node tools/resolve-ws-deps.js
|
||||
|
||||
# Pack all packages into tarballs (mimics npm publish)
|
||||
RUN pnpm -r pack --pack-destination /tarballs
|
||||
|
||||
# Generate a package.json listing every external (non-@docmd) runtime dep of
|
||||
# @docmd/core's transitive tree. The production stage installs from this file
|
||||
# instead of a hand-rolled list in the Dockerfile, so adding a dep to any
|
||||
# @docmd/* package automatically propagates to the Docker image.
|
||||
RUN node tools/print-okf-runtime-deps.js --emit-pkg=/tarballs/runtime-deps.json
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Stage 2: Production stage - minimal image with only runtime dependencies
|
||||
# -----------------------------------------------------------------------------
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
curl \
|
||||
bash \
|
||||
su-exec
|
||||
|
||||
# Create non-root user for security
|
||||
RUN addgroup -g 1001 -S docmd && \
|
||||
adduser -S -D -H -u 1001 -h /app -s /bin/bash -G docmd -g docmd docmd
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy packed tarballs from builder
|
||||
COPY --from=builder /tarballs /tarballs
|
||||
|
||||
# Install only @docmd/* packages (the transitive deps of @docmd/core).
|
||||
# We extract each docmd-*.tgz tarball into /usr/local/lib/node_modules/@docmd/<name>.
|
||||
# This bypasses npm/pnpm registry lookups (packages aren't published yet at
|
||||
# build time). Legacy wrappers (doc.md, @mgks/docmd) and the monorepo root
|
||||
# tarball are excluded by only matching the docmd-* prefix.
|
||||
RUN mkdir -p /usr/local/lib/node_modules && \
|
||||
for tarball in /tarballs/docmd-*.tgz; do \
|
||||
fname=$(basename "$tarball" .tgz) && \
|
||||
pkg="${fname%-*}" && \
|
||||
scoped="@docmd/${pkg#docmd-}" && \
|
||||
mkdir -p "/usr/local/lib/node_modules/$scoped" && \
|
||||
tar -xzf "$tarball" -C "/usr/local/lib/node_modules/$scoped" --strip-components=1; \
|
||||
done
|
||||
|
||||
# Install external deps from the auto-generated runtime-deps.json produced by
|
||||
# the builder stage. We extract the dep list with node and pass it straight to
|
||||
# `npm install -g`, which puts packages into /usr/local/lib/node_modules —
|
||||
# exactly where Node's global resolution path looks. No hand-rolled list in the
|
||||
# Dockerfile: adding a dep to any @docmd/* package propagates automatically on
|
||||
# the next build because the builder stage regenerates runtime-deps.json fresh.
|
||||
RUN node -e "\
|
||||
const p=JSON.parse(require('fs').readFileSync('/tarballs/runtime-deps.json','utf8')); \
|
||||
const args=Object.entries(p.dependencies).map(([k,v])=>k+'@'+v); \
|
||||
const {execFileSync}=require('child_process'); \
|
||||
execFileSync('npm',['install','-g','--omit=dev','--no-audit','--no-fund',...args],{stdio:'inherit'}); \
|
||||
"
|
||||
|
||||
# Symlink the docmd CLI onto PATH (chmod +x on the script too — some
|
||||
# base images strip the executable bit during COPY).
|
||||
RUN ln -sf /usr/local/lib/node_modules/@docmd/core/dist/bin/docmd.js /usr/local/bin/docmd \
|
||||
&& chmod +x /usr/local/bin/docmd /usr/local/lib/node_modules/@docmd/core/dist/bin/docmd.js
|
||||
|
||||
# Clean up tarballs to reduce image size
|
||||
RUN rm -rf /tarballs
|
||||
|
||||
# Create directories for user content
|
||||
RUN mkdir -p /docs /site && chown -R docmd:docmd /docs /site
|
||||
|
||||
# Seed the default template by running docmd init non-interactively.
|
||||
# This ensures the image ships exactly what a real `docmd init` produces.
|
||||
#
|
||||
# Build does NOT fail on init failure: the image still publishes (so users
|
||||
# who mount their own /docs are unaffected), but the demo experience is
|
||||
# degraded and a clear warning is printed. Full stderr from `docmd init`
|
||||
# is in the build log above this block for diagnosis.
|
||||
RUN mkdir -p /template && \
|
||||
cd /template && \
|
||||
if docmd init --yes; then \
|
||||
if [ ! -f /template/docmd.config.json ]; then \
|
||||
printf '\n[docmd-docker] WARNING: docmd init exited 0 but /template/docmd.config.json is missing.\n' >&2; \
|
||||
printf '[docmd-docker] Demo seeding will not work. Mount your own /docs.\n' >&2; \
|
||||
fi; \
|
||||
else \
|
||||
printf '\n[docmd-docker] WARNING: docmd init failed during image build.\n' >&2; \
|
||||
printf '[docmd-docker] The `docker run` demo experience will not work. Mount your own /docs.\n' >&2; \
|
||||
printf '[docmd-docker] Full error from docmd init is in the build log above this block.\n' >&2; \
|
||||
fi
|
||||
|
||||
# Install entrypoint + healthcheck scripts (see docker/docker-entrypoint.sh
|
||||
# and docker/docker-healthcheck.sh). The entrypoint seeds /docs from
|
||||
# /template when /docs is empty, so `docker run` with no volume mount
|
||||
# still works out of the box. The healthcheck probes a port range because
|
||||
# `docmd dev` may auto-increment past 3000 if the port is already taken.
|
||||
COPY docker/docker-entrypoint.sh /usr/local/bin/docmd-entrypoint.sh
|
||||
COPY docker/docker-healthcheck.sh /usr/local/bin/docmd-healthcheck.sh
|
||||
RUN chmod +x /usr/local/bin/docmd-entrypoint.sh /usr/local/bin/docmd-healthcheck.sh
|
||||
|
||||
# Set environment variables
|
||||
ENV NODE_ENV=production
|
||||
ENV DOCMD_CONTAINER=true
|
||||
|
||||
# Run as root so the entrypoint can inspect /docs ownership and use
|
||||
# su-exec to re-exec as the correct uid:gid. The entrypoint never stays
|
||||
# as root — it always drops to the detected uid before exec-ing docmd.
|
||||
# This is the same pattern used by postgres, redis, and other official images.
|
||||
|
||||
# Set working directory to docs (where user content will be mounted)
|
||||
WORKDIR /docs
|
||||
|
||||
# Expose default port (dev server may pick a higher one if 3000 is in use)
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check — probes 3000-3005 to follow the dev server's actual port
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD ["/usr/local/bin/docmd-healthcheck.sh"]
|
||||
|
||||
# Default command — entrypoint detects /docs uid, drops privileges, then
|
||||
# seeds /docs if empty and execs the supplied command.
|
||||
ENTRYPOINT ["/usr/local/bin/docmd-entrypoint.sh"]
|
||||
CMD ["dev", "--host", "0.0.0.0"]
|
||||
Reference in New Issue
Block a user