Project-2 baseline

This commit is contained in:
2026-10-04 21:19:23 -04:00
parent 0f04889edf
commit 213eb3c403
861 changed files with 1710 additions and 3363322 deletions

View File

@@ -29,6 +29,9 @@ REPO_NAME=$(node -e "try{process.stdout.write(require('/workspace/toolkit.json')
# $EXPLORE_CLIENT_PORT; prefer it, falling back to toolkit.json then 3000 for
# older containers built before this var existed.
CLIENT_HOST_PORT="${EXPLORE_CLIENT_PORT:-$(node -e "try{process.stdout.write(String(require('/workspace/toolkit.json').explorePorts.clientHost))}catch{process.stdout.write('3000')}" 2>/dev/null || echo 3000)}"
# Companion services publish host:container IDENTICAL (see package-worker-toolkit), so this one
# value is both what the service binds and what the browser reaches.
COMPANION_PORT="${EXPLORE_COMPANION_PORT:-$(node -e "try{process.stdout.write(String(require('/workspace/toolkit.json').explorePorts.companionHost||4201))}catch{process.stdout.write('4201')}" 2>/dev/null || echo 4201)}"
# --- process helpers ---------------------------------------------------------
@@ -82,6 +85,10 @@ stop_app() {
_kill_pidfile "$pf"
stopped=1
done
# frePPLe's planning engine daemonizes itself, so no pidfile covers it.
if [ "${REPO_NAME:-}" = "frepple" ] && [ -x /workspace/repo/frepplectl.py ]; then
( cd /workspace/repo && ./frepplectl.py stopwebservice ) >/dev/null 2>&1 || true
fi
if [ "$stopped" = 1 ]; then printf "${GRAY}Stopped the app.${RESET}\n"; else printf "${GRAY}Nothing to stop.${RESET}\n"; fi
}
@@ -500,17 +507,20 @@ setup_repo() {
printf " ${GRAY}first-time setup for %s (%s) \xe2\x80\x94 runs once\xe2\x80\xa6${RESET}\n" "$repo" "${runtime:-explore-only}"
case "$kind" in
elixir)
# asdf-only (no rbenv/nvm estate has elixir). Version comes from the member's
# .tool-versions; shims are already on PATH. deps + a MIX_ENV=test compile so the
# Version comes from the member's .tool-versions where asdf manages it, else from
# the image. dev.secret.exs is seeded BEFORE any mix task: every task evaluates
# config/<env>.exs, so a member whose config imports it can't even run local.hex
# without it. deps + a MIX_ENV=test compile so the
# suite is warm and compile errors surface at setup, not mid-explore. bootEnv covers
# any compile-time env a member reads (e.g. epihub's ZOOM_* module attributes). DB/ecto
# prep is member-specific → leave it to setupCmd; a worker runs `mix test` with it.
( cd "$dir" \
&& for kv in $bootenv; do export "$kv"; done \
&& mix local.hex --force >/dev/null 2>&1 \
&& mix local.rebar --force >/dev/null 2>&1 \
&& { [ -f config/dev.secret.exs.example ] && [ ! -f config/dev.secret.exs ] && cp config/dev.secret.exs.example config/dev.secret.exs; true; } \
&& mix deps.get \
&& { mix local.hex --force >/dev/null 2>&1 \
|| printf " ${GRAY}(hex not refreshed \xe2\x80\x94 using the image's copy)${RESET}\n"; } \
&& { mix local.rebar --force >/dev/null 2>&1 || true; } \
&& mix deps.get </dev/null \
&& MIX_ENV=test mix compile ) || return 1 ;;
ruby)
if _asdf_ok; then
@@ -855,19 +865,61 @@ start_poly() {
cmd="env $bootenv CARGO_TARGET_DIR=/opt/raccoon-cargo-target/$repo $startcmd" ;;
*) printf "${YELLOW}runtime '%s' for %s isn't runnable here \xe2\x80\x94 explore-only.${RESET}\n" "$runtime" "$repo"; return 0 ;;
esac
# Companion services a member cannot work without. strongsuit-app is a front end to the
# strongsuit_phx Phoenix backend, and it does not degrade when that is absent:
# getImportantDateRecommendations rethrows the connection failure and none of its four
# callers catch it, so those pages throw instead of rendering an empty list.
case "$repo" in
strongsuit-app)
local phx_dir=/workspace/repos/strongsuit_phx
# Every absolute link the app renders is built from DOMAIN, so it has to carry the
# port the worker's browser reaches — which instance.js moves per named instance.
cmd="env DOMAIN=http://localhost:$CLIENT_HOST_PORT PHX_DOMAIN=localhost:$COMPANION_PORT $cmd"
# Members are set up lazily, and nothing else ever runs setup_repo for a member that
# is never itself the target. After strongsuit-app's own setup, so the database it
# creates and seeds exists before phx's migration baseline and seed read it.
setup_repo strongsuit_phx \
|| printf " ${YELLOW}\xe2\x9a\xa0 strongsuit_phx setup failed \xe2\x80\x94 ${RESET}${GRAY}run-app --logs${RESET}\n"
# The DB-dependent half runs on EVERY boot, not once: setup_repo's marker is set even
# when its work was skipped, so a worker who ran `run-app strongsuit_phx` first (no
# database yet) would otherwise be stuck with phx 503ing on unbaselined migrations.
# Both scripts are idempotent and cheap once prepared.
( bash /workspace/scripts/seed/setup-strongsuit-phx.sh "$phx_dir" \
&& bash /workspace/scripts/seed/seed-important-date-recommendations.sh ) \
>> "$RUN_DIR/setup-strongsuit_phx.log" 2>&1 \
|| printf " ${YELLOW}\xe2\x9a\xa0 strongsuit_phx database prep failed \xe2\x80\x94 ${RESET}${GRAY}run-app --logs${RESET}\n"
if [ -d "$phx_dir/deps" ] && [ -d "$phx_dir/_build" ]; then
printf " ${CYAN}\xe2\x96\xb6${RESET} starting strongsuit_phx (elixir)\xe2\x80\xa6\n"
# Derived, not a second env var: the app sends PHX_AUTH_TOKEN and the phx plug
# compares against API_AUTH_TOKEN, and a drift shows up only as silent 401s.
_spawn phx "$phx_dir" "env MIX_ENV=dev PHX_PORT=$COMPANION_PORT API_AUTH_TOKEN=${PHX_AUTH_TOKEN:-dummy} mix phx.server"
_wait_tcp "$COMPANION_PORT" 45 || printf " ${YELLOW}\xe2\x9a\xa0 strongsuit_phx didn't come up \xe2\x80\x94 ${RESET}${GRAY}run-app --logs${RESET}\n"
else
printf " ${GRAY}strongsuit_phx isn't built \xe2\x80\x94 the backend on :%s will be absent.\n" "$COMPANION_PORT"
printf " build it: ${RESET}${GRAY}bash /workspace/scripts/seed/setup-strongsuit-phx.sh${RESET}\n"
fi ;;
esac
printf " ${CYAN}\xe2\x96\xb6${RESET} starting %s (%s)\xe2\x80\xa6\n" "$repo" "$runtime"
_spawn app "$dir" "$cmd"
if _wait_tcp 3000; then
printf " ${CYAN}\xe2\x9c\x85 %s is up${RESET} open ${CYAN}http://localhost:%s${RESET}\n" "$repo" "$CLIENT_HOST_PORT"
# Members whose usable entry point isn't "/". strongsuit-app's is the local dev-login
# route: "/" redirects to a hosted Auth0 tenant that can't be reached offline, so the
# URL printed here — the one a terminal makes clickable — has to be the one that works.
local landing=""
case "$repo" in
strongsuit-app) landing="/dev-login" ;;
esac
printf " ${CYAN}\xe2\x9c\x85 %s is up${RESET} open ${CYAN}http://localhost:%s%s${RESET}\n" "$repo" "$CLIENT_HOST_PORT" "$landing"
# Per-member "how do I actually get in" notes. Only members whose landing page needs
# more than the URL need an entry here (e.g. an app whose real sign-in is a hosted
# third-party login that can't be reached offline).
case "$repo" in
strongsuit-app)
printf " ${GRAY}Sign-in normally goes through a hosted Auth0 page, which isn't reachable\n"
printf " offline, so this app ships a local-only dev-login route. Open\n"
printf " ${RESET}${CYAN}http://localhost:%s/dev-login${RESET}${GRAY} to sign in as a seeded admin\n" "$CLIENT_HOST_PORT"
printf " (${RESET}${GRAY}?role=MSS${RESET}${GRAY} or ${RESET}${GRAY}?role=MEMBER${RESET}${GRAY} for the other roles). The DB was seeded during setup.${RESET}\n"
printf " offline; the link above is a local-only dev-login route that signs you in\n"
printf " as a seeded admin (${RESET}${GRAY}?role=MSS${RESET}${GRAY} or ${RESET}${GRAY}?role=MEMBER${RESET}${GRAY} for the other roles).\n"
printf " The DB was seeded during setup. Plain ${RESET}${GRAY}http://localhost:%s/${RESET}${GRAY} redirects to\n" "$CLIENT_HOST_PORT"
printf " Auth0 and cannot work offline.${RESET}\n"
;;
ABDM-FE)
printf " ${GRAY}This app is served under a ${RESET}${GRAY}/app${RESET}${GRAY} basename, so the bare URL above renders\n"
@@ -902,6 +954,53 @@ start_poly() {
# passes the demo login to print. Optional $2 is a one-line note printed above the
# login (e.g. a subdomain caveat).
# start_rails <login-hint> [url-note]
start_frepple() {
# Django dev server; the C++ engine is already built in-tree by post-create.
_spawn app /workspace/repo "./frepplectl.py runserver 0.0.0.0:3000"
printf " ${YELLOW}\xe2\x96\xb6${RESET} starting the frePPLe web app (Django)\xe2\x80\xa6\n"
printf " ${GRAY}\xe2\x8f\xb3 waiting for the app to come up\xe2\x80\xa6${RESET}\n"
if _wait_tcp 3000; then
printf " ${YELLOW}\xe2\x9c\x85 app is up${RESET}\n"
# Django serves plan data read-only; the forecast editor and the plan screens save by
# POSTing from the browser to the engine's web service on 8002. Django starts it on
# first login, so start it here instead and the first save can't race the plan load.
printf " ${YELLOW}\xe2\x96\xb6${RESET} loading the plan into the planning engine\xe2\x80\xa6\n"
( cd /workspace/repo && ./frepplectl.py runwebservice --daemon --forcerestart \
>>"$RUN_DIR/webservice.log" 2>&1 ) || true
if _wait_tcp 8002 300; then
printf " ${YELLOW}\xe2\x9c\x85 planning engine is up${RESET}\n"
else
printf " ${RED}\xe2\x9a\xa0 the planning engine didn't come up \xe2\x80\x94 editing a forecast won't save${RESET}\n"
printf " check the logs: ${GRAY}%s/webservice.log${RESET}\n" "$RUN_DIR"
fi
printf " open ${YELLOW}http://localhost:%s${RESET}\n" "$CLIENT_HOST_PORT"
printf " login ${GRAY}admin / frepple${RESET}\n"
else
printf " ${RED}\xe2\x9a\xa0 the app didn't come up in time${RESET}\n"
printf " check the logs: ${GRAY}run-app --logs${RESET}\n"
fi
printf " logs ${GRAY}%s/app.log${RESET}\n" "$RUN_DIR"
printf " stop ${GRAY}run-app --stop${RESET}\n"
}
start_freeitsm() {
# Plain PHP behind Apache — no dev server. -DFOREGROUND keeps it in _spawn's
# process group so --logs and --stop behave like every other repo.
_spawn app /workspace/repo "apache2ctl -DFOREGROUND"
printf " ${YELLOW}\xe2\x96\xb6${RESET} starting Apache (mod_php)\xe2\x80\xa6\n"
printf " ${GRAY}\xe2\x8f\xb3 waiting for the app to come up\xe2\x80\xa6${RESET}\n"
if _wait_tcp 3000; then
printf " ${YELLOW}\xe2\x9c\x85 app is up${RESET}\n"
printf " open ${YELLOW}http://localhost:%s${RESET}\n" "$CLIENT_HOST_PORT"
printf " login ${GRAY}admin / freeitsm123 (staff sign-in; setup already cleared the forced change)${RESET}\n"
else
printf " ${RED}\xe2\x9a\xa0 the app didn't come up in time${RESET}\n"
printf " check the logs: ${GRAY}run-app --logs${RESET}\n"
fi
printf " logs ${GRAY}%s/app.log${RESET}\n" "$RUN_DIR"
printf " stop ${GRAY}run-app --stop${RESET}\n"
}
start_rails() {
local login_hint="${1:-}" url_note="${2:-}"
_spawn app /workspace/repo "bin/rails server -b 0.0.0.0 -p 3000"
@@ -952,6 +1051,8 @@ start_app() {
# welcome page. No login to print. The app grows over time.
start_rails "" "young app — no routes defined yet, so this shows the default Rails welcome page" ;;
breezy-complete) start_breezy_complete ;;
frepple) start_frepple ;;
freeitsm) start_freeitsm ;;
*)
printf "${YELLOW}run-app isn't configured for repo '%s'.${RESET}\n" "${REPO_NAME:-unknown}"
printf "Start the app with the project's own dev command from ${GRAY}/workspace/repo${RESET}.\n"