67 lines
3.0 KiB
Bash
67 lines
3.0 KiB
Bash
#!/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 "$@" |