The sync walks the working tree rather than git, so deploy/windows/installer/ bundle came through despite being gitignored - about 100MB of build output containing a copy of the whole application tree, the wheels and the vendor installers. Its copies of config.py and requirements.txt then tripped the scrub gate, which is the only reason it was noticed. Excluded along with the installer's other generated files. Note that rsync --exclude also protects a path from --delete, so a copy already in the publication tree has to be removed by hand once.
147 lines
5.8 KiB
Bash
Executable File
147 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Export the working repo to the GitHub publication repo and emit a bundle.
|
|
#
|
|
# Pipeline: working repo (full history, internal refs) -> pruned/scrubbed
|
|
# tree -> commit in ~/projects/shopdb-flask-pub (the local mirror of what
|
|
# enterprise GitHub holds) -> full git bundle in /home/camp/pxe-images/ for
|
|
# transfer to the work PC, which pushes it to GitHub.
|
|
#
|
|
# Usage:
|
|
# tools/export-github.sh "Commit message for the publication commit"
|
|
# tools/export-github.sh --dist # also rebuild BOTH frontend dists
|
|
# # (/ops = dev, /shopdb = prod)
|
|
#
|
|
# This script lives in tools/, which is itself excluded from publication.
|
|
set -euo pipefail
|
|
|
|
WORK=/home/camp/projects/shopdb-flask
|
|
PUB=/home/camp/projects/shopdb-flask-pub
|
|
OUT=/home/camp/pxe-images/github
|
|
|
|
BUILD_DIST=0
|
|
MSG=""
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--dist) BUILD_DIST=1 ;;
|
|
*) MSG="$arg" ;;
|
|
esac
|
|
done
|
|
[ -n "$MSG" ] || { echo "usage: $0 [--dist] \"commit message\""; exit 1; }
|
|
[ -d "$PUB/.git" ] || { echo "publication repo missing at $PUB"; exit 1; }
|
|
|
|
# --- 1. sync the tree (working -> pub), minus everything never published ---
|
|
#
|
|
# NOTE the deploy/windows/installer/bundle exclude below. That is ~100MB of build
|
|
# output holding a COPY of the whole application tree plus the wheels and vendor
|
|
# installers. It is gitignored, but this sync walks the WORKING TREE rather than
|
|
# git, so it came through anyway - and its copies of config.py and
|
|
# requirements.txt then tripped the scrub gate. Anything else generated into the
|
|
# working tree needs excluding here too, for the same reason.
|
|
rsync -a --delete \
|
|
--exclude '.git' \
|
|
--exclude '.gitea' \
|
|
--exclude 'docs' \
|
|
--exclude 'tools' \
|
|
--exclude 'mcp' \
|
|
--exclude 'start-api.sh' \
|
|
--exclude 'start-ui.sh' \
|
|
--exclude 'CLAUDE.md' \
|
|
--exclude 'frontend/CLAUDE.md' \
|
|
--exclude 'tests/test_docs_contract.py' \
|
|
--exclude 'tests/test_plugins/test_geenforce_parity.py' \
|
|
--exclude 'tests/test_plugins/test_zabbix_live.py' \
|
|
--exclude 'venv' \
|
|
--exclude 'node_modules' \
|
|
--exclude 'deploy/windows/installer/bundle' \
|
|
--exclude 'deploy/windows/installer/Output' \
|
|
--exclude 'deploy/windows/installer/plugins.iss' \
|
|
--exclude 'deploy/windows/installer/version.iss' \
|
|
--exclude 'frontend/dist*' \
|
|
--exclude 'frontend/src/.plugins-staged' \
|
|
--exclude 'frontend/src/router/routes.gen.js' \
|
|
--exclude 'instance' \
|
|
--exclude '.env' \
|
|
--exclude '__pycache__' \
|
|
--exclude '*.pyc' \
|
|
--exclude 'scripts/site_imports/wjf/idmap.json' \
|
|
"$WORK/" "$PUB/"
|
|
|
|
cd "$PUB"
|
|
|
|
# --- 2. re-apply the publication wording (idempotent) ---
|
|
# docs/ lives only in the wiki on the GitHub side.
|
|
grep -rlZ 'docs/' --include='*.py' --include='*.md' --include='*.sh' \
|
|
--include='*.tmpl' --include='*.vue' --include='*.example' . 2>/dev/null |
|
|
while IFS= read -r -d '' f; do
|
|
case "$f" in ./CHANGELOG.md) continue ;; esac
|
|
sed -i -E \
|
|
-e 's/\[`?docs\/([A-Za-z0-9_-]+)\.md`?\]\((\.\.\/)*docs\/[A-Za-z0-9_-]+\.md\)/the \1 page in the project wiki/g' \
|
|
-e 's/docs\/proposals\/ge-enforce-plugin\.md/the ge-enforce-plugin proposal in the project wiki/g' \
|
|
-e 's/`docs\/([A-Za-z0-9_-]+)\.md`/the \1 wiki page/g' \
|
|
-e 's/docs\/([A-Za-z0-9_-]+)\.md/the \1 wiki page/g' "$f"
|
|
done
|
|
sed -i 's|<code>docs/GE-ENFORCE.md</code>|the GE-ENFORCE page in the project wiki|' \
|
|
frontend/src/views/geenforce/ManifestEditor.vue 2>/dev/null || true
|
|
# internal infra never named on GitHub; this repo's own URL maps to the
|
|
# real GitHub home, anything else degrades to a placeholder.
|
|
GITHUB_URL='https://github.com/ge-aero/shopdb-flask'
|
|
grep -rlZ 'gitea\.proudtech\.net' . 2>/dev/null | while IFS= read -r -d '' f; do
|
|
sed -i -e "s|https://gitea\.proudtech\.net/ge-aerospace/shopdb-flask|$GITHUB_URL|g" \
|
|
-e 's|gitea\.proudtech\.net|<git-host>|g' "$f"
|
|
done
|
|
grep -rlZi 'gitea' --exclude-dir=.git . 2>/dev/null | while IFS= read -r -d '' f; do
|
|
sed -i -e 's/the GE Aerospace Gitea/the internal GE Aerospace git server/g' \
|
|
-e 's/Gitea Actions CI/CI/g' \
|
|
-e 's/Gitea Actions/CI/g' "$f"
|
|
done
|
|
# CHANGELOG compare/release link definitions reference tags that do not
|
|
# exist on GitHub (history is squashed there) - drop them.
|
|
sed -i '/^\[[^]]*\]: .*\/\(compare\|releases\)\//d' CHANGELOG.md
|
|
# frontend/CLAUDE.md publishes under a neutral name
|
|
if [ -f "$WORK/frontend/CLAUDE.md" ]; then
|
|
cp "$WORK/frontend/CLAUDE.md" frontend/DEVELOPMENT-STANDARDS.md
|
|
fi
|
|
sed -i "s/rootpassword/changeme/g" shopdb/config.py 2>/dev/null || true
|
|
|
|
# --- 3. scrub gate: refuse to commit if anything internal leaks ---
|
|
LEAKS=$(grep -rlIiE 'claude|anthropic|fable 5|gitea|proudtech|home/camp|rootpassword' \
|
|
--exclude-dir=.git . || true)
|
|
if [ -n "$LEAKS" ]; then
|
|
echo "SCRUB GATE FAILED - internal references in:"; echo "$LEAKS"; exit 1
|
|
fi
|
|
|
|
# --- 4. commit (no-op safe) ---
|
|
git add -A
|
|
if git diff --cached --quiet; then
|
|
echo "no changes vs publication tree - nothing to export"
|
|
else
|
|
git commit -m "$MSG"
|
|
fi
|
|
git log --oneline -3
|
|
|
|
# --- 5. bundle for transfer (full bundle: stateless, fetch takes only new) ---
|
|
git bundle create "$OUT/shopdb-flask-pub.bundle" HEAD main --tags
|
|
git bundle verify "$OUT/shopdb-flask-pub.bundle" >/dev/null
|
|
echo "bundle: $OUT/shopdb-flask-pub.bundle"
|
|
|
|
# --- 6. optional per-instance dists (both bases: /ops = dev, /shopdb = prod) ---
|
|
# Two instances run on the box; each needs its own base-path build. Always
|
|
# rebuild BOTH so prod never ships a stale frontend.
|
|
if [ "$BUILD_DIST" = 1 ]; then
|
|
cd "$WORK/frontend"
|
|
for base in ops shopdb; do
|
|
VITE_BASE_PATH=/$base/ npm run build --silent
|
|
rm -rf "$OUT/frontend-dist-subpath-$base"
|
|
cp -r dist "$OUT/frontend-dist-subpath-$base"
|
|
echo "dist: $OUT/frontend-dist-subpath-$base/ (base /$base/)"
|
|
done
|
|
fi
|
|
|
|
cat <<'EOF'
|
|
|
|
On the work PC (in the shopdb-flask-pub clone):
|
|
git fetch <path-to>\shopdb-flask-pub.bundle main
|
|
git merge --ff-only FETCH_HEAD
|
|
git push origin main
|
|
EOF
|