chore: import upstream snapshot with attribution
docmd CI verification / verify (push) Failing after 0s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:31:55 +08:00
commit 6db8fca185
437 changed files with 68762 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
# --------------------------------------------------------------------
# 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.
# --------------------------------------------------------------------
# Dependencies
node_modules
packages/*/node_modules
packages/plugins/*/node_modules
packages/engines/*/node_modules
# Build outputs
dist
*.tsbuildinfo
packages/*/dist
packages/plugins/*/dist
packages/engines/*/dist
# Generated sites
site
packages/*/site
packages/plugins/*/site
public
# Development files
.git
.gitignore
.github
.vscode
.idea
*.md
!README.md
# Test and playground files
packages/_playground
temp-*
*.log
*.tsbuildinfo
# Native build artifacts (will be downloaded at runtime)
packages/engines/rust-binaries/native/target
# Documentation
docs
*.md
!README.md
!LICENSE
# CI/CD files
.github
.travis.yml
.circleci
# Editor files
*.swp
*.swo
*~
.DS_Store
# Environment files
.env
.env.*
!.env.example
# Test files
test
tests
__tests__
coverage
.nyc_output
# Misc
*.tgz
*.bak
tmp
temp
+236
View File
@@ -0,0 +1,236 @@
# Docker Image for docmd
Official Docker image for docmd - the minimalist, zero-config documentation generator.
## Quick Start
> The examples below use `:latest` so you can copy-paste and run them immediately.
> **For production, CI, and any reproducible build**, replace `:latest` with the specific version you want — see [Available Tags](#available-tags).
### Pull the Image
```bash
# Pull from GitHub Container Registry (GHCR)
docker pull ghcr.io/docmd-io/docmd:latest
```
### Run Demo Site
The image ships with a demo template in `/template`. When you run the container with no volume mount, the entrypoint copies `/template` into `/docs` on first start, so the demo site comes up immediately.
```bash
# Start with built-in demo site — works out of the box
docker run -p 3000:3000 ghcr.io/docmd-io/docmd:latest
# Visit http://localhost:3000
```
### Initialize New Project
```bash
# Create and initialize a new project
mkdir my-docs && cd my-docs
docker run -v $(pwd):/workspace ghcr.io/docmd-io/docmd:latest init
# Start dev server
docker run -v $(pwd):/workspace -p 3000:3000 ghcr.io/docmd-io/docmd:latest dev
```
### Use Existing Docs
Mounting your own docs into `/docs` always wins — the entrypoint only seeds the demo template when `/docs` is completely empty, so your content is never overwritten.
```bash
# Mount your docs and start dev server
docker run -v $(pwd)/docs:/docs -p 3000:3000 ghcr.io/docmd-io/docmd:latest
# Build static site
docker run -v $(pwd)/docs:/docs -v $(pwd)/site:/site ghcr.io/docmd-io/docmd:latest build
```
## Available Tags
The image is published with two tags per release:
| Tag | Description | When to use |
|-----|-------------|-------------|
| `latest` | Floating alias for the most recent stable release | Quick start, local exploration, throwaway CI |
| `<X.Y.Z>` | Pinned stable release (substitute the version you want) | Production, CI/CD, anything that must be reproducible |
> **Pin a specific version in production.** The examples below use `:latest` so you can copy-paste and run them immediately. For any pipeline whose output must be reproducible (or whose contracts you don't want silently changing), replace `:latest` with the specific version you want (e.g. `0.8.7`). Check the [package versions page](https://github.com/orgs/docmd-io/packages/container/docmd/versions) for the full list.
## Multi-Platform Support
The image is built for multiple architectures:
- `linux/amd64` - Standard x86_64 servers
- `linux/arm64` - ARM-based servers (AWS Graviton, Raspberry Pi, Apple Silicon)
Docker automatically pulls the correct image for your platform.
## Usage Examples
### Docker Compose
```yaml
# Replace `:latest` with a specific version (e.g. `0.8.7`) for reproducible
# production builds. See the GitHub releases page for available versions.
services:
docmd:
image: ghcr.io/docmd-io/docmd:latest
ports:
- "3000:3000"
volumes:
- ./docs:/docs
- ./site:/site
# The dev server defaults to 127.0.0.1 (loopback). For LAN access from
# other devices, set DOCMD_HOST=0.0.0.0 or pass --host 0.0.0.0 explicitly.
command: dev
```
### GitHub Actions CI/CD
```yaml
name: Build Docs
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build documentation
# Pin to a specific version (replace `:latest`) for reproducible CI runs.
run: |
docker run --rm \
-v ${{ github.workspace }}/docs:/docs \
-v ${{ github.workspace }}/site:/site \
ghcr.io/docmd-io/docmd:latest \
build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
```
### Kubernetes Deployment
```yaml
# Replace `:latest` with a specific version (e.g. `0.8.7`) for reproducible
# production deploys. Update the tag when you upgrade.
apiVersion: apps/v1
kind: Deployment
metadata:
name: docmd-server
spec:
replicas: 1
selector:
matchLabels:
app: docmd
template:
metadata:
labels:
app: docmd
spec:
containers:
- name: docmd
image: ghcr.io/docmd-io/docmd:latest
ports:
- containerPort: 3000
volumeMounts:
- name: docs
mountPath: /docs
# The dev server defaults to 127.0.0.1. For LAN access, set
# DOCMD_HOST=0.0.0.0 or pass --host 0.0.0.0 explicitly.
command: ["docmd", "dev"]
volumes:
- name: docs
configMap:
name: docs-content
```
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `NODE_ENV` | Node environment | `production` |
| `DOCMD_CONTAINER` | Container mode flag | `true` |
## Building Locally
```bash
# Clone the repository
git clone https://github.com/docmd-io/docmd.git
cd docmd
# Build the image
docker build -t docmd:local -f docker/Dockerfile .
# Test the build
docker run --rm -v $(pwd)/docs:/docs docmd:local --version
```
## Security Features
- Runs as non-root user (`docmd`)
- Minimal Alpine Linux base image
- Multi-stage build reduces attack surface
- SBOM (Software Bill of Materials) included
- OCI provenance attestation
## Health Check
The image includes a health check that verifies the dev server is running:
```bash
# Check container health
docker inspect --format='{{.State.Health.Status}}' <container-id>
```
## Troubleshooting
### Permission Issues
The entrypoint automatically remaps to the uid:gid of the mounted directory, so files are always owned by the correct host user. If you hit permission issues, verify the mounted path exists and is writable on the host.
The dev server defaults to 127.0.0.1 (loopback only). To expose it to the LAN
from inside a container, pass the host flag explicitly:
```bash
# Loopback only (default) — access via http://localhost:3000 from the host
docker run -p 3000:3000 ghcr.io/docmd-io/docmd:latest dev
# LAN access — the dev server binds to 0.0.0.0 inside the container, and
# the port mapping makes it reachable from other devices on the host's network.
docker run -p 3000:3000 ghcr.io/docmd-io/docmd:latest dev --host 0.0.0.0
```
> **Security**: when you bind to 0.0.0.0, every host that can reach the
> container's port can connect. Only do this on trusted networks. The dev
> server's WebSocket requires an Origin that matches the bind host
> (defence against CSWSH, CWE-1385), but loopback-only is the safer default.nsure you're binding to 0.0.0.0
docker run -p 3000:3000 ghcr.io/docmd-io/docmd:latest dev --host 0.0.0.0
```
### Memory Issues
```bash
# For large documentation sites
docker run --memory=2g -v $(pwd)/docs:/docs ghcr.io/docmd-io/docmd:latest build
```
## License
MIT License - see [LICENSE](../LICENSE) for details.
## Support
- **Issues**: [GitHub Issues](https://github.com/docmd-io/docmd/issues)
- **Documentation**: [docs.docmd.io](https://docs.docmd.io)
- **Website**: [docmd.io](https://docmd.io)
+185
View File
@@ -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"]
+67
View File
@@ -0,0 +1,67 @@
#!/bin/sh
# docmd Docker entrypoint
#
# Handles two cases:
#
# 1. No volume / empty /docs → seeds the demo template so `docmd dev`
# works out of the box with no arguments.
#
# 2. Host-mounted volume → detects the uid:gid that owns /docs and
# re-execs as that identity (via su-exec), so file writes from any
# subcommand — including `docmd init` — land with the correct host
# ownership without needing `-u $(id -u):$(id -g)` from the caller.
#
# This mirrors the pattern used by official images (postgres, redis, etc.).
set -e
FIRST_ARG="${1:-}"
# ── uid remapping ────────────────────────────────────────────────────────────
# If we are currently running as root (the entrypoint is exec'd as root so it
# can inspect /docs ownership), detect the uid:gid of /docs and re-exec as
# that user. This lets `docmd init` and `docmd dev` write to host-mounted
# volumes without permission errors regardless of the host user's uid.
if [ "$(id -u)" = "0" ] && [ -d /docs ]; then
DOCS_UID=$(stat -c '%u' /docs 2>/dev/null || echo "1001")
DOCS_GID=$(stat -c '%g' /docs 2>/dev/null || echo "1001")
# If /docs is owned by root (uid 0) on the host, fall back to the docmd
# user — writing as root inside the container is a security anti-pattern.
if [ "$DOCS_UID" = "0" ]; then
DOCS_UID="1001"
DOCS_GID="1001"
fi
# ── writability check (N-18) ───────────────────────────────────────────
# Catch read-only volume mounts early with a clear message instead of
# letting docmd crash mid-build with a cryptic EROFS / EACCES error.
# Only applies to write commands — `dev`, `build`, `init`.
# Read-only mounts are valid for `validate`, `mcp`, etc.
case "$FIRST_ARG" in
dev|build|init)
if ! touch /docs/.docmd_write_test 2>/dev/null; then
printf '\n[docmd] ERROR: /docs is read-only (or not writable by uid %s).\n' "$DOCS_UID" >&2
printf '[docmd] Mount a writable directory:\n' >&2
printf '[docmd] docker run -v $(pwd)/docs:/docs ghcr.io/docmd-io/docmd\n' >&2
printf '[docmd] Or check directory permissions on the host.\n' >&2
exit 1
fi
rm -f /docs/.docmd_write_test
;;
esac
# Seed demo template before dropping privileges (root can always write).
if [ "$FIRST_ARG" != "init" ] && \
[ -z "$(ls -A /docs 2>/dev/null)" ] && \
[ -d /template ]; then
echo "[docmd] /docs is empty — seeding demo template from /template"
cp -r /template/. /docs/
chown -R "$DOCS_UID:$DOCS_GID" /docs 2>/dev/null || true
fi
# Re-exec as the detected uid:gid so all subsequent writes are correctly owned.
exec su-exec "$DOCS_UID:$DOCS_GID" "$0" "$@"
fi
# ── already the right user (re-exec path lands here) ────────────────────────
exec docmd "$@"
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
# docmd Docker healthcheck
#
# The dev server may auto-increment its port (3000, 3001, ...) if the
# default port is in use, so we probe a small range instead of hard-
# coding a single port. Returns 0 as soon as any port responds.
for port in 3000 3001 3002 3003 3004 3005; do
if curl -fs --connect-timeout 2 "http://127.0.0.1:${port}/" >/dev/null 2>&1; then
exit 0
fi
done
exit 1