From 9c6149ae4c0c199c502f0e9962e4a1e1751ad920 Mon Sep 17 00:00:00 2001 From: XingfenD Date: Tue, 29 Sep 2026 14:13:19 +0800 Subject: [PATCH] init the mono repo --- .gitignore | 5 ++ AGENTS.md | 48 +++++++++++++++++++ clone_all.sh | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100755 clone_all.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94eef74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +crearte/ +crearte-deploy/ +crearte-server/ + + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ac66a71 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,48 @@ +# AGENTS.md + +## Repository role + +Docker Compose orchestration for the crearte full stack. Three repos must stay siblings: + +``` +repos/ +├── crearte/ # frontend (build context for web-* images) +├── crearte-server/ # backend Go API (build context for api-* images) +└── crearte-deploy/ # this repo — compose only, no app code +``` + +Everything runs in containers. Do not introduce host-run workflows (no `npm run dev` / `go run` on the host) in docs, scripts, or compose. + +## Safety Rules + +- Branch naming: `{feat|fix|docs|chore}/{branch-name}` (e.g. `fix/compose-port-clash`). +- Before `git commit`: run `git branch --show-current`. If on `master`, do NOT commit — ask the user for a branch name, create it, and commit there. +- `.env` is gitignored and must stay that way. Never commit secrets. Any new secret must land in `.env.example` as an empty placeholder (with a comment) in the same commit that introduces it. +- Changes → `docs/CHANGELOG.md` (this repo is not under `web/`, so `CHANGELOG_webui.md` never applies). Higher versions on top. +- CHANGELOG entry format: same entry has English line then Chinese line on consecutive lines (no blank line between them); different entries are separated by a blank line. +- Merging into `master`: always create a merge commit — `git merge --no-ff ` (never fast-forward, the integration point must be recorded). Delete the merged branch afterwards. +- Compose changes that alter usage must update `README.md` in the same commit. + +## Compose invariants (breaking these loses data or breaks the stack) + +- Sibling layout: build contexts are `../crearte` and `../crearte-server`. Never repoint them or inline copies of the app code. +- dev and prod both publish host port `8080` — the profiles are mutually exclusive. Keep it that way unless README is updated accordingly. +- Volume names are data identity: renaming a volume orphans its data. Never rename `pgdata-*`, `bundle-keys-*`, `minio-data-*`, or the `*_node_modules` volumes. +- `db-test` (`pgdata-test`, 127.0.0.1:5432) is the integration-test database and is truncated freely. Never point `TEST_DATABASE_URL` at dev data — `db-debug` shares the `pgdata-dev` volume with `db-dev`, so a misdirected test run wipes development data. +- Never run `docker compose ... down -v` unless the user explicitly asks: it destroys postgres data, bundle keys, and MinIO objects, and the MinIO bucket init must then be redone by hand. +- Required secrets use `${VAR:?…}` so compose fails fast when missing. Keep the `:?` guard and `.env.example` in sync for every new secret. +- The one-time MinIO bucket init (`mc mb local/crearte` + anonymous download) exists because the volume is empty on first creation — do not silently automate it away; document any change in README. +- Keep `depends_on` health gating (api waits for `service_healthy` db); removing it makes first-boot races routine. + +## Verification (required before claiming a compose change works) + +1. `docker compose config -q` for each touched profile (`--profile dev|prod|debug|mock`). +2. `docker compose --profile

up -d --build`, then `docker compose ps` — healthchecks must pass. +3. Smoke-test the affected path (e.g. `docker compose exec api-dev wget -qO- http://127.0.0.1:8080/healthz`, or the upload flow through http://localhost:8080). +4. Tear down with plain `down` (never `-v`) unless the task is specifically about destroying data. + +## Layout notes + +- `docs/superpowers/` (plans/specs) is tracked in this repo — unlike in `crearte`, where it is gitignored. +- No app code, no tests, no build scripts live here; those belong to `crearte` / `crearte-server`. + diff --git a/clone_all.sh b/clone_all.sh new file mode 100755 index 0000000..14075e0 --- /dev/null +++ b/clone_all.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# +# clone_all.sh — clone (or update) every project in this workspace. +# +# Idempotent, safe to re-run: +# - directory missing / not a git repo -> git clone --branch +# - directory already a git repo -> fetch + fast-forward to +# +# Usage: +# ./clone_all.sh # clone or update all projects +# CLONE_PROTO=https ./clone_all.sh # rewrite URLs to https (no SSH key needed) + +set -uo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT_DIR" || exit 1 + +# --------------------------------------------------------------------------- +# Project list: "||" +# --------------------------------------------------------------------------- +REPOS=( + "crearte|git@github.com:XingfenD/crearte.git|feat/submission-preview" + "crearte-server|git@github.com:XingfenD/crearte-server.git|feat/submission-preview" + "crearte-deploy|git@github.com:XingfenD/crearte-deploy.git|master" +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +if [ -t 1 ]; then + C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_OFF=$'\033[0m' +else + C_OK=''; C_WARN=''; C_ERR=''; C_OFF='' +fi +info() { printf '==> %s\n' "$*"; } +ok() { printf "${C_OK} ok${C_OFF} %s\n" "$*"; } +warn() { printf "${C_WARN} warn${C_OFF} %s\n" "$*"; } +err() { printf "${C_ERR} error${C_OFF} %s\n" "$*" >&2; } + +# ssh url -> https url when the caller asked for it +resolve_url() { + local url="$1" + if [ "${CLONE_PROTO:-ssh}" = "https" ]; then + url="${url#git@}" + url="${url/://}" + url="https://$url" + fi + printf '%s' "$url" +} + +has_local_changes() { + local dir="$1" + [ -n "$(git -C "$dir" status --porcelain 2>/dev/null)" ] +} + +# --------------------------------------------------------------------------- +# Actions +# --------------------------------------------------------------------------- +clone_repo() { + local dir="$1" url="$2" branch="$3" + info "$dir: cloning $branch from $url" + if git clone --branch "$branch" "$url" "$dir"; then + ok "$dir cloned" + return 0 + fi + err "$dir clone failed" + return 1 +} + +update_repo() { + local dir="$1" branch="$2" + info "$dir: updating $branch" + + if ! git -C "$dir" fetch --prune origin; then + err "$dir fetch failed (network or credentials?)" + return 1 + fi + + local current + current="$(git -C "$dir" rev-parse --abbrev-ref HEAD)" + if [ "$current" != "$branch" ]; then + if has_local_changes "$dir"; then + warn "$dir has uncommitted changes on '$current' — skipping branch switch to '$branch'" + return 0 + fi + if ! git -C "$dir" checkout "$branch"; then + err "$dir could not switch to '$branch'" + return 1 + fi + fi + + if ! git -C "$dir" pull --ff-only origin "$branch"; then + err "$dir not fast-forwardable — resolve manually (git -C $dir status)" + return 1 + fi + ok "$dir up to date" + return 0 +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +cloned=0 updated=0 skipped=0 failed=0 + +for entry in "${REPOS[@]}"; do + IFS='|' read -r dir url branch <<< "$entry" + + if [ -d "$dir/.git" ] || git -C "$dir" rev-parse --git-dir >/dev/null 2>&1; then + if update_repo "$dir" "$branch"; then + updated=$((updated + 1)) + else + failed=$((failed + 1)) + fi + elif [ -e "$dir" ]; then + warn "$dir exists but is not a git repo — skipping (move it away or delete it)" + skipped=$((skipped + 1)) + else + if clone_repo "$dir" "$(resolve_url "$url")" "$branch"; then + cloned=$((cloned + 1)) + else + failed=$((failed + 1)) + fi + fi +done + +printf '\nSummary: %d cloned, %d updated, %d skipped, %d failed\n' \ + "$cloned" "$updated" "$skipped" "$failed" + +[ "$failed" -eq 0 ]