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

@@ -35,7 +35,7 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential git curl ca-certificates gnupg procps sudo xz-utils \
libssl-dev zlib1g-dev \
postgresql postgresql-client \
postgresql postgresql-client ffmpeg \
&& rm -rf /var/lib/apt/lists/*
# --- Node via nvm: 14 / 16 / 18 / 20 (prebuilt). Default 20 symlinked to /usr/local/bin so the

View File

@@ -4,7 +4,7 @@
"build": {
"dockerfile": "Dockerfile",
"args": {
"TOOLKIT_BUILD_ID": "1789430760274-eddbpv"
"TOOLKIT_BUILD_ID": "1790712369311-8xr6mu"
}
},
"appPort": [

View File

@@ -76,7 +76,10 @@ dnsjail_apply() {
drop_ours
# cache-size=0: every lookup goes upstream, so a jailed container sees what an unjailed
# one would rather than an answer this resolver decided to keep.
dnsmasq --no-resolv --no-hosts --listen-address=127.0.0.1 --bind-interfaces \
# -u root: dnsmasq 2.80 (buster and older bases) drops to "nobody" and calls capset to
# retain CAP_NET_ADMIN, which docker's default cap set does not grant -- so it exits and
# the jail fails open on every such image.
dnsmasq -u root --no-resolv --no-hosts --listen-address=127.0.0.1 --bind-interfaces \
--cache-size=0 --pid-file="$STATE/dnsmasq.pid" --address=/#/ $srv \
>/dev/null 2>>"$STATE/dnsmasq.err" || true
fi

View File

@@ -156,6 +156,8 @@ if (!instance)
const tk = JSON.parse(fs.readFileSync('toolkit.json', 'utf-8'));
expected = tk.explorePorts && tk.explorePorts.serverHost ? [3000, 3001] : [3000];
if (tk.explorePorts && tk.explorePorts.corpusHost) expected.push(3002);
if (tk.explorePorts && tk.explorePorts.companionHost)
expected.push(tk.explorePorts.companionHost);
} catch {}
const folder = process.cwd();

View File

@@ -44,6 +44,34 @@ _nm_link() {
ln -sfn "$root/node_modules" node_modules
}
# g++ peaks near 400MiB on this codebase's biggest translation units, and nproc reports the
# HOST's core count, so a many-core laptop with a small Docker VM OOMs mid-build. Bound the
# job count by whichever of the VM's memory and the cgroup cap is smaller.
_frepple_jobs() {
local n mem cg j
n=$(nproc)
mem=$(awk '/^MemTotal:/{print $2*1024}' /proc/meminfo)
cg=$(cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null || echo)
case "$cg" in ''|max|*[!0-9]*) ;; *) [ "$cg" -lt "$mem" ] && mem=$cg ;; esac
j=$(( mem / 734003200 ))
[ "$j" -lt 1 ] && j=1
[ "$j" -gt "$n" ] && j=$n
echo "$j"
}
# Docker Desktop's macOS bind mount can write a compiled wheel with the right length and
# the wrong bytes, so the venv imports die on a signal (132/135/139) rather than an error.
# A reinstall lands the authentic file; a Django-level failure exits 1 and is not retried.
_frepple_migrate() {
local rc=0
./frepplectl.py migrate --noinput || rc=$?
if [ "$rc" -le 128 ]; then return "$rc"; fi
echo "venv extension died on signal $rc — reinstalling requirements and retrying" >&2
python3 -m pip install --force-reinstall --no-cache-dir -r requirements.txt -q
./frepplectl.py migrate --noinput
}
case "$REPO_NAME" in
ZenBill-006)
# Install deps + create databases
@@ -313,6 +341,70 @@ case "$REPO_NAME" in
&& _nm_link flaredown-frontend \
&& (npm install --unsafe-perm --no-audit --no-fund || echo "WARNING: frontend npm install failed (explore-only)" >&2) ) || true
;;
frepple)
# The minified JS the app serves is tracked, so pnpm+grunt are for a worker who
# edits frontend source, not a prerequisite. The cmake build creates venv/ and
# pip-installs into it. odoo_addon is pinned, NOT --remote like upstream CI.
# DEBUG_JS=DEBUG, and DEBUG is true under runserver, which points the two Vue
# screens at a Vite dev server on :5173 that nothing starts. FREPPLE_PORT is where
# the BROWSER posts forecast saves, so the service has to bind 0.0.0.0 to be
# reachable. localsettings.py is upstream's own gitignored override hook.
# `demo` alone holds no forecasts, which leaves the forecast screens empty; adding
# distribution_demo and planning it reproduces upstream's own scenario1 content.
# The `doc` target is not in `all`, so without it the Help menu and the help icon on
# 89 report screens 404; its own symlink is absolute, so relink it relatively or the
# host sees a dangling link into the container's /workspace.
( cd /workspace/repo \
&& git submodule update --init freppledb/odoo/odoo_addon \
&& pnpm install --frozen-lockfile \
&& grunt \
&& cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build --parallel "$(_frepple_jobs)" \
&& { cmake --build build --target doc \
&& ln -sfn ../../../build/doc/_build/html freppledb/common/static/doc \
|| echo "NOTE: the docs did not build; in-app help links will 404" >&2; } \
&& printf 'DEBUG_JS = False\nfor _a in DATABASES:\n DATABASES[_a]["FREPPLE_PORT"] = DATABASES[_a]["FREPPLE_PORT"].replace("127.0.0.1:", "0.0.0.0:")\n' \
> localsettings.py \
&& _frepple_migrate \
&& ./frepplectl.py loaddata demo \
&& ./frepplectl.py loaddata distribution_demo \
&& ./frepplectl.py runplan --env=fcst,supply --background \
&& ./frepplectl.py shell -c "
from django.contrib.auth import get_user_model
U = get_user_model()
u, _ = U.objects.get_or_create(username='admin', defaults={'email': 'admin@example.com'})
u.is_superuser = True; u.is_staff = True; u.set_password('frepple'); u.save()
print('admin user ready')
" ) \
|| { echo "FATAL: frepple setup did not complete — the app would not serve." >&2; exit 1; }
;;
freeitsm)
# Plain PHP / Apache, no composer. config.php is left exactly as upstream tracks it
# (the image puts its Windows-path require on include_path); db_config.php is the
# doc-root stub the test scripts require, excluded locally so git status stays clean.
# Apache writes attachments and imports as www-data, but a bind mount can force those
# dirs root-owned and swallow the chown, so the directory mode is what has to give.
( cd /workspace/repo \
&& cp docker/db_config.php db_config.php \
&& _fi_ex="$(git rev-parse --git-path info/exclude)" \
&& mkdir -p "$(dirname "$_fi_ex")" \
&& { grep -qxF 'db_config.php' "$_fi_ex" 2>/dev/null \
|| echo 'db_config.php' >> "$_fi_ex"; } \
&& for _fi_d in tickets/attachments change-management/attachments \
uploads/asset-imports uploads/documents; do \
mkdir -p "$_fi_d"; \
chown -R www-data:www-data "$_fi_d" 2>/dev/null || true; \
find "$_fi_d" -type d -exec chmod a+rwx {} +; \
done \
&& if [ "$(mysql -u root -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='freeitsm'" 2>/dev/null || echo 0)" -lt 10 ]; then
mysql -u root freeitsm < database/freeitsm.sql \
|| { echo "FATAL: could not load database/freeitsm.sql — is the freeitsm database present?" >&2; exit 1; }
fi \
&& apache2ctl -k start 2>/dev/null \
&& /usr/local/bin/freeitsm-seed.sh \
&& apache2ctl -k stop 2>/dev/null ) \
|| { echo "FATAL: freeitsm setup did not complete — the app would not log in." >&2; exit 1; }
;;
breezy-complete)
# Monorepo: Rails 7.0 / Ruby 3.2.0 API (backend/) + Next.js 14 frontend
# (frontend/); Postgres + Redis baked in the image. The offline Clerk-bypass
@@ -389,6 +481,16 @@ CALL_METADATA="$CALL_METADATA" node -e '
`${home}/.claude/settings.json`,
JSON.stringify({ env }, null, 2) + "\n"
);
// Onboarding preflights api.anthropic.com + platform.claude.com, neither from
// ANTHROPIC_BASE_URL, and exits 1 unresolved, so a jailed container never records it done.
const cfgPath = `${home}/.claude.json`;
let cfg = {};
try {
cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
} catch {}
cfg.hasCompletedOnboarding = true;
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
'
# Reference-data corpus: expose it at the stable /data/zeta-corpus path (the same path a trial
@@ -426,8 +528,8 @@ bash /workspace/welcome.sh explore 2>/dev/null
_AK="fde503c3bdb6e5cc9c48b1f8e4c2abeb"
_DK="e966e45af5ad1a18005f9fdb831186ea"
_WID="w-mu1wvj9k-gtnk"
_VER="037cfcf94b"
_WID="w-mun3wr6n-v83f"
_VER="1.0.0"
_CT="explore"
_RP=$(node -e "try{process.stdout.write(require('/workspace/toolkit.json').repo)}catch{}" 2>/dev/null)
_SID="$(date +%s)-$$"

View File

@@ -209,6 +209,20 @@ case "$REPO_NAME" in
wait_for_pg
for _ in $(seq 1 60); do mongo_up && break; sleep 0.5; done
;;
frepple)
# Provisioned at image build time (the CLI overrides ENTRYPOINT); just start it.
pg_ctlcluster "$(ls /etc/postgresql | head -1)" main start 2>/dev/null || true
for i in $(seq 1 60); do pg_isready -h 127.0.0.1 -q && break; sleep 0.5; done
;;
freeitsm)
# MySQL 8 (Percona). The database and app user are created at image BUILD time —
# the devcontainer CLI overrides ENTRYPOINT, so nothing there would run.
if ! mysqladmin ping >/dev/null 2>&1; then
sudo mkdir -p /var/run/mysqld && sudo chown -R mysql:mysql /var/run/mysqld /var/lib/mysql 2>/dev/null || true
sudo service mysql start >/dev/null 2>&1 || (sudo mysqld_safe --user=mysql >/dev/null 2>&1 &) || echo "warning: mysql start failed" >&2
fi
for i in $(seq 1 120); do mysqladmin ping >/dev/null 2>&1 && break; sleep 0.5; done
;;
breezy-complete)
# Postgres + Redis (Sidekiq). Start both; wait_for_pg is the gate. Trust
# auth (set in the image) — PGPASSWORD is baked but inert, no role seeding.