Files
3dealer/bin/core-mode
T

147 lines
5.7 KiB
Bash
Raw Normal View History

2026-09-30 12:17:52 +03:00
#!/usr/bin/env bash
# Switch how this app consumes boboko-core, then restart the dev stack so the
# entrypoints re-resolve it.
#
# bin/core-mode local ../boboko-core checkout (Composer path repo + npm file:)
# bin/core-mode repo tagged releases from the forge (Composer VCS repo + npm git+semver)
# bin/core-mode print the current mode
#
# Running it for the mode you're already in skips the file edits but still
# restarts the stack — that's how repo mode picks up a newly pushed tag.
#
# The toggle lives in composer.json ("repositories" <-> "_repositories") and
# package.json (@boboko/core in "dependencies" <-> "_dependencies"). The dev
# entrypoints run `composer update boboko/*` and `npm update @boboko/core` on
# every boot, which rewrites composer.lock / package-lock.json for the mode.
set -euo pipefail
cd "$(dirname "$0")/.."
fail() { echo "!!! $*" >&2; exit 1; }
# Escape a string for use as a sed regex / replacement with "|" as delimiter.
sed_pattern() { printf '%s' "$1" | sed 's/[][\.*^$|]/\\&/g'; }
sed_replacement() { printf '%s' "$1" | sed 's/[\&|]/\\&/g'; }
current_mode() {
local composer_type npm_dep composer_mode npm_mode
# The live block is "repositories" — the leading quote keeps "_repositories" out.
composer_type=$(sed -n '/"repositories": \[/,/"type"/p' composer.json | grep -o '"type": "[a-z]*"' | head -1)
npm_dep=$(sed -n '/"dependencies": {/,/}/p' package.json | grep -o '"@boboko/core": "[^"]*"' | head -1)
case "$composer_type" in
*'"path"') composer_mode=local ;;
*'"vcs"') composer_mode=repo ;;
*) fail "Can't tell the mode from composer.json's \"repositories\" block." ;;
esac
case "$npm_dep" in
*'"file:'*) npm_mode=local ;;
*'"git+'*) npm_mode=repo ;;
*) fail "Can't tell the mode from package.json's @boboko/core dependency." ;;
esac
[ "$composer_mode" = "$npm_mode" ] ||
fail "composer.json ($composer_mode) and package.json ($npm_mode) disagree — fix by hand first."
echo "$composer_mode"
}
swap_files() {
# composer.json: swap the key names, values stay where they are.
sed -i \
-e 's/"_repositories":/"__core_mode_swap__":/' \
-e 's/"repositories":/"_repositories":/' \
-e 's/"__core_mode_swap__":/"repositories":/' \
composer.json
# package.json: swap the two @boboko/core values (live and parked).
local a b
a=$(grep -o '"@boboko/core": "[^"]*"' package.json | sed -n 1p)
b=$(grep -o '"@boboko/core": "[^"]*"' package.json | sed -n 2p)
[ -n "$a" ] && [ -n "$b" ] || fail "Expected two @boboko/core entries in package.json."
sed -i \
-e "s|$(sed_pattern "$a")|__core_mode_swap__|" \
-e "s|$(sed_pattern "$b")|$(sed_replacement "$a")|" \
-e "s|__core_mode_swap__|$(sed_replacement "$b")|" \
package.json
}
wrapper_for() {
if [ "$1" = local ]; then echo bin/dc-core.sh; else echo bin/dc; fi
}
# The app healthcheck (php-fpm -t) passes before the entrypoint finishes, so
# wait on the entrypoints' own log lines instead: the app's composer block is
# done once it clears caches, the vite container's npm block once Vite is up.
wait_for_log() {
local dc=$1 service=$2 pattern=$3 label=$4 waited=0
printf '>>> Waiting for %s' "$label"
until $dc logs --no-color "$service" 2>&1 | grep -q "$pattern"; do
if [ -z "$($dc ps -q --status running "$service")" ]; then
echo
$dc logs --no-color --tail 40 "$service"
fail "$service stopped before finishing (logs above)."
fi
if [ "$waited" -ge 600 ]; then
echo
$dc logs --no-color --tail 40 "$service"
fail "Timed out after 600s waiting for $service (logs above)."
fi
printf '.'
sleep 3
waited=$((waited + 3))
done
echo " done"
}
mode=$(current_mode)
if [ $# -eq 0 ]; then
echo "$mode"
exit 0
fi
target=$1
[ "$target" = local ] || [ "$target" = repo ] || fail "Usage: bin/core-mode [local|repo]"
if [ "$target" = local ] && [ ! -d ../boboko-core ]; then
fail "../boboko-core not found — local mode needs the core checkout next to this repo."
fi
old_dc=$(wrapper_for "$mode")
new_dc=$(wrapper_for "$target")
echo ">>> Stopping stack ($mode mode)..."
$old_dc down
if [ "$mode" != "$target" ]; then
echo ">>> Switching composer.json / package.json: $mode -> $target"
swap_files
[ "$(current_mode)" = "$target" ] || fail "Switch didn't take — check composer.json / package.json."
else
echo ">>> Already in $target mode, restarting to re-resolve boboko-core"
fi
# The entrypoints run `composer install` / `npm install` from the lockfiles
# BEFORE re-resolving core. Local-mode lockfiles point at ../boboko-core,
# which isn't mounted in repo mode, so the install fails. The committed
# lockfiles are always repo mode — restore those and let the entrypoints bump
# them to the newest tag.
if [ "$target" = repo ]; then
if grep -A14 '"name": "boboko/core"' composer.lock | grep -q '"type": "path"' ||
grep -A6 '"node_modules/@boboko/core": {' package-lock.json | grep -q '"link": true'; then
echo ">>> Lockfiles still point at ../boboko-core — restoring the committed ones"
git checkout HEAD -- composer.lock package-lock.json
fi
fi
echo ">>> Starting stack ($target mode)..."
if ! $new_dc up -d; then
$new_dc logs --no-color --tail 40 app
fail "Stack failed to start (app logs above)."
fi
wait_for_log "$new_dc" app '\[entrypoint\] Caches cleared' "composer to re-resolve boboko/core"
wait_for_log "$new_dc" vite 'ready in' "npm to re-resolve @boboko/core"
version=$(grep -A1 '"name": "boboko/core"' composer.lock | grep -o '"version": "[^"]*"' | cut -d'"' -f4)
echo ">>> Now in $target mode — boboko/core $version"