#!/bin/bash # Split lecoffre_ng (branch test) into five Git repos with history (git subtree split). # Does NOT read or write /home/ncantu/code/lecoffre_ng: work is done in a disposable clone # from Gitea (or SPLIT_CLONE_URL). See docs/split-lecoffre-repos.md # # Env: # SPLIT_CLONE_URL clone source (default: git@git.4nkweb.com:4nk/lecoffre_ng.git) # SOURCE_BRANCH branch to split (default: test) # OUTPUT_BASE parent directory for output repos (default: /home/ncantu/code) # REPLACE_OUTPUT=1 remove existing lecoffre-io-*_ng dirs before rebuilding # PUSH=1 git push -u origin main for each sub-repo after commits # MAIN_BRANCH default: main # # Usage: # REPLACE_OUTPUT=1 ./split-lecoffre-ng-to-five-repos.sh # REPLACE_OUTPUT=1 PUSH=1 ./split-lecoffre-ng-to-five-repos.sh set -euo pipefail CLONE_URL="${SPLIT_CLONE_URL:-git@git.4nkweb.com:4nk/lecoffre_ng.git}" SOURCE_BRANCH="${SOURCE_BRANCH:-test}" OUT="${OUTPUT_BASE:-/home/ncantu/code}" MAIN="${MAIN_BRANCH:-main}" PUSH="${PUSH:-0}" REPLACE_OUTPUT="${REPLACE_OUTPUT:-0}" die() { echo "ERROR: $*" >&2 exit 1 } declare -A REMOTES=( [docs]="git@git.4nkweb.com:4nk/lecoffre-io-docs_ng.git" [deploy]="git@git.4nkweb.com:4nk/lecoffre-io-deploy_ng.git" [shared]="git@git.4nkweb.com:4nk/lecoffre-io-shared_ng.git" [frontend]="git@git.4nkweb.com:4nk/lecoffre-io-frontend_ng.git" [backend]="git@git.4nkweb.com:4nk/lecoffre-io-backend_ng.git" ) declare -A DIRS=( [docs]="lecoffre-io-docs_ng" [deploy]="lecoffre-io-deploy_ng" [shared]="lecoffre-io-shared_ng" [frontend]="lecoffre-io-frontend_ng" [backend]="lecoffre-io-backend_ng" ) declare -A PREFIX=( [docs]="docs" [deploy]="deploy" [shared]="lecoffre-ressources-dev" [frontend]="lecoffre-front-main" [backend]="lecoffre-back-main" ) WORK="$(mktemp -d)" MONO="${WORK}/mono" cleanup() { rm -rf "${WORK}" } trap cleanup EXIT echo "Clone: ${CLONE_URL} (branch ${SOURCE_BRANCH})" echo "Output: ${OUT}" echo "Replace existing: ${REPLACE_OUTPUT}" echo "Push: ${PUSH}" echo "" git clone --branch "${SOURCE_BRANCH}" --single-branch "${CLONE_URL}" "${MONO}" cd "${MONO}" git rev-parse --verify "${SOURCE_BRANCH}" >/dev/null || die "Branch ${SOURCE_BRANCH} missing after clone" split_branch() { local key="$1" local br="split-lecoffre-${key}" cd "${MONO}" git branch -D "${br}" 2>/dev/null || true git subtree split -P "${PREFIX[${key}]}" -b "${br}" "${SOURCE_BRANCH}" >/dev/null } # On branch test, docs/ may be absent: ship top-level *.md into the docs repo instead. init_docs_repo() { local dest="${OUT}/${DIRS[docs]}" if [ "${REPLACE_OUTPUT}" = "1" ] && [ -d "${dest}" ]; then rm -rf "${dest}" fi if [ -d "${dest}" ]; then die "Destination exists: ${dest} (set REPLACE_OUTPUT=1 to overwrite)" fi mkdir -p "${dest}" cd "${dest}" git init -b "${MAIN}" shopt -s nullglob local f for f in "${MONO}"/*.md; do cp -a "${f}" "${dest}/$(basename "${f}")" done shopt -u nullglob if [ -z "$(git status --porcelain)" ]; then die "No *.md at monorepo root; cannot build docs repo without docs/" fi git add . git commit -m "Import root markdown from lecoffre_ng (branch ${SOURCE_BRANCH})" git remote add origin "${REMOTES[docs]}" } init_from_split() { local key="$1" local dest="${OUT}/${DIRS[${key}]}" split_branch "${key}" local br="split-lecoffre-${key}" if [ "${REPLACE_OUTPUT}" = "1" ] && [ -d "${dest}" ]; then rm -rf "${dest}" fi if [ -d "${dest}" ]; then die "Destination exists: ${dest} (set REPLACE_OUTPUT=1 to overwrite)" fi mkdir -p "${dest}" cd "${dest}" git init -b "${MAIN}" git pull "${MONO}" "${br}" git remote add origin "${REMOTES[${key}]}" cd "${MONO}" git branch -D "split-lecoffre-${key}" 2>/dev/null || true } add_root_docs() { local dest="${OUT}/${DIRS[docs]}" local f for f in CHANGELOG.md; do if [ -f "${MONO}/${f}" ]; then cp -a "${MONO}/${f}" "${dest}/${f}" fi done cd "${dest}" if git status --porcelain | grep -q .; then git add CHANGELOG.md 2>/dev/null || true git commit -m "Add CHANGELOG from monorepo root (branch ${SOURCE_BRANCH})" fi } add_root_deploy() { local dest="${OUT}/${DIRS[deploy]}" local files=( VERSION package.json package-lock.json .dockerignore .editorconfig .gitattributes .gitmessage .prettierignore README.md ) local f for f in "${files[@]}"; do if [ -e "${MONO}/${f}" ]; then cp -a "${MONO}/${f}" "${dest}/${f}" fi done cd "${dest}" if ! git status --porcelain | grep -q .; then return 0 fi # git add with a missing path fails the whole command and stages nothing; add only present files. for f in "${files[@]}"; do if [ -e "${f}" ]; then git add "${f}" fi done if git diff --cached --quiet; then return 0 fi git commit -m "Add monorepo root metadata for deploy repo (branch ${SOURCE_BRANCH})" } do_push() { local key="$1" local dest="${OUT}/${DIRS[${key}]}" cd "${dest}" git push -u origin "${MAIN}" } echo "=== docs -> ${DIRS[docs]} ===" if [ -d "${MONO}/docs" ]; then init_from_split "docs" add_root_docs else echo "(no docs/ on ${SOURCE_BRANCH}: using root *.md)" init_docs_repo fi for key in deploy shared frontend backend; do echo "=== ${key} -> ${DIRS[${key}]} (${PREFIX[${key}]}) ===" init_from_split "${key}" done add_root_deploy if [ "${PUSH}" = "1" ]; then for key in docs deploy shared frontend backend; do echo "=== push ${key} ===" do_push "${key}" done fi echo "" echo "Done. Repos under ${OUT}/lecoffre-io-*_ng (from ${SOURCE_BRANCH}, lecoffre_ng local untouched)."