diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index c9682fb..03dcb31 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -62,10 +62,6 @@ jobs: echo "=> Full build required" fi - - name: Set up El SDK - if: steps.changetype.outputs.asset_only != 'true' - run: echo "EL_HOME=/opt/el" >> "$GITHUB_ENV" - - name: Authenticate to GCP id: auth uses: google-github-actions/auth@v2 @@ -102,12 +98,60 @@ jobs: # in the build context for Dockerfile COPY to succeed. run: touch src/index.html src/about.html src/terms.html src/enterprise-terms.html - - name: Build image (build-stage.sh) + # ── El SDK setup ────────────────────────────────────────────────────── + + - name: Extract El SDK from ci-base if: steps.changetype.outputs.asset_only != 'true' - env: - EXTRACT_JS: '1' run: | - ./build-stage.sh "${{ steps.tag.outputs.tag }}" + set -euo pipefail + docker pull us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest + CID=$(docker create us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest) + sudo mkdir -p /opt/el + docker cp "$CID:/opt/el" /opt/ + docker rm "$CID" + echo "ELB=/opt/el/dist/bin/elb" >> "$GITHUB_ENV" + echo "ELC=/opt/el/dist/platform/elc" >> "$GITHUB_ENV" + echo "EL_RUNTIME=/opt/el/el-compiler/runtime" >> "$GITHUB_ENV" + + # ── Build neuron-web binary ─────────────────────────────────────────── + + - name: Build neuron-web with elb + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + "$ELB" build \ + --elc "$ELC" \ + --runtime "$EL_RUNTIME" + echo "Binary: $(ls -lh dist/neuron-landing)" + + # ── Compile JS client sources ───────────────────────────────────────── + + - name: Compile JS El sources + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + cp "$EL_RUNTIME/el_runtime.js" src/js/ + mkdir -p dist/js + for f in src/js/*.el; do + [ -f "$f" ] || continue + name=$(basename "$f" .el) + "$ELC" --target=js --bundle --minify --obfuscate "$f" > "dist/js/${name}.js" + echo " compiled: $f -> dist/js/${name}.js" + done + rm -f src/js/el_runtime.js + + # ── Docker build + push ─────────────────────────────────────────────── + + - name: Build and tag image + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + docker build \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --cache-from us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest \ + -f Dockerfile.stage \ + -t "marketing:${{ steps.tag.outputs.tag }}" \ + . docker tag "marketing:${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.image }}" docker tag "marketing:${{ steps.tag.outputs.tag }}" "us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest" diff --git a/.gitea/workflows/stage.yaml b/.gitea/workflows/stage.yaml index 6904209..6947540 100644 --- a/.gitea/workflows/stage.yaml +++ b/.gitea/workflows/stage.yaml @@ -72,10 +72,6 @@ jobs: echo "=> Full build required" fi - - name: Set up El SDK - if: steps.changetype.outputs.asset_only != 'true' - run: echo "EL_HOME=/opt/el" >> "$GITHUB_ENV" - - name: Authenticate to GCP uses: google-github-actions/auth@v2 with: @@ -104,10 +100,60 @@ jobs: # in the build context for Dockerfile COPY to succeed. run: touch src/index.html src/about.html src/terms.html src/enterprise-terms.html - - name: Build image (build-stage.sh) + # ── El SDK setup ────────────────────────────────────────────────────── + + - name: Extract El SDK from ci-base if: steps.changetype.outputs.asset_only != 'true' run: | - ./build-stage.sh "${{ steps.tag.outputs.tag }}" + set -euo pipefail + docker pull us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest + CID=$(docker create us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest) + sudo mkdir -p /opt/el + docker cp "$CID:/opt/el" /opt/ + docker rm "$CID" + echo "ELB=/opt/el/dist/bin/elb" >> "$GITHUB_ENV" + echo "ELC=/opt/el/dist/platform/elc" >> "$GITHUB_ENV" + echo "EL_RUNTIME=/opt/el/el-compiler/runtime" >> "$GITHUB_ENV" + + # ── Build neuron-web binary ─────────────────────────────────────────── + + - name: Build neuron-web with elb + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + "$ELB" build \ + --elc "$ELC" \ + --runtime "$EL_RUNTIME" + echo "Binary: $(ls -lh dist/neuron-landing)" + + # ── Compile JS client sources ───────────────────────────────────────── + + - name: Compile JS El sources + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + cp "$EL_RUNTIME/el_runtime.js" src/js/ + mkdir -p dist/js + for f in src/js/*.el; do + [ -f "$f" ] || continue + name=$(basename "$f" .el) + "$ELC" --target=js --bundle --minify --obfuscate "$f" > "dist/js/${name}.js" + echo " compiled: $f -> dist/js/${name}.js" + done + rm -f src/js/el_runtime.js + + # ── Docker build + push ─────────────────────────────────────────────── + + - name: Build and tag image + if: steps.changetype.outputs.asset_only != 'true' + run: | + set -euo pipefail + docker build \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --cache-from us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:stage-latest \ + -f Dockerfile.stage \ + -t "marketing:${{ steps.tag.outputs.tag }}" \ + . docker tag "marketing:${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.image }}" docker tag "marketing:${{ steps.tag.outputs.tag }}" "us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:stage-latest" diff --git a/build-stage.sh b/build-stage.sh index c22f76e..af55074 100755 --- a/build-stage.sh +++ b/build-stage.sh @@ -2,111 +2,40 @@ # # build-stage.sh — Build the Stage marketing image (neuron-web + soul-demo). # -# Pipeline: -# 1. Stage the foundation El runtime into ./runtime/. -# 2. Compile client-side El sources (src/js/*.el) to dist/js/*.js using -# the JS-capable elc binary at bin/elc-linux-amd64 (CI) or the local -# elc (dev). Output is gitignored and rebuilt every run. -# 3. Concatenate src/*.el into dist/main-combined.el (component-first, -# main.el last; matches the historical order from build-local.sh). -# 4. Compile dist/main-combined.el → dist/main.c using the canonical -# native elc at foundation/el/dist/platform/elc. -# 5. Inject the host-side stub forward declarations into dist/main.c -# (sed header rewrite, same set as the prior in-Dockerfile sed). -# 6. docker buildx build --platform linux/amd64 -f Dockerfile.stage. -# -# bootstrap.py is no longer in the build path. The container image now -# expects dist/main.c to be a finished C source — it just runs cc on it. +# Thin wrapper around elb. The El build system handles compilation. +# ELB, ELC, and EL_RUNTIME must be set by the caller (extracted from ci-base +# in CI, or pointed at the local El SDK in dev). # # Usage: -# ./build-stage.sh — build marketing: +# ELB=/opt/el/dist/bin/elb ELC=... EL_RUNTIME=... ./build-stage.sh set -euo pipefail cd "$(dirname "$0")" TAG="${1:-dev}" -LANDING_DIR=$(pwd) -EL_HOME="${EL_HOME:-${LANDING_DIR}/../../foundation/el}" -ELC="${EL_HOME}/dist/platform/elc" -RUNTIME_SRC="${EL_HOME}/el-compiler/runtime" - -# JS-capable elc: prefer committed bin/elc-linux-amd64 on CI (linux/amd64), -# fall back to the local elc from the El checkout on macOS dev. -if [ -f "${LANDING_DIR}/bin/elc-linux-amd64" ] && uname -m | grep -q x86_64; then - ELC_JS="${LANDING_DIR}/bin/elc-linux-amd64" -elif [ -x "${ELC}" ]; then - ELC_JS="${ELC}" -else - echo "elc for JS compilation not found — expected bin/elc-linux-amd64 or ${ELC}" >&2 +if [ -z "${ELB:-}" ] || [ -z "${ELC:-}" ] || [ -z "${EL_RUNTIME:-}" ]; then + echo "Error: ELB, ELC, and EL_RUNTIME must be set" >&2 + echo " Extract from ci-base or point to local El SDK at foundation/el" >&2 exit 1 fi -if [ ! -x "${ELC}" ]; then - echo "elc not found at ${ELC}" >&2 - exit 1 -fi - -echo "==> Staging El runtime from ${RUNTIME_SRC}" -mkdir -p runtime dist -cp "${RUNTIME_SRC}/el_runtime.c" runtime/ -cp "${RUNTIME_SRC}/el_runtime.h" runtime/ - -# The JS compiler looks for el_runtime.js in the same directory as the -# source file being compiled. Copy it there so --bundle can inline it. -cp "${RUNTIME_SRC}/el_runtime.js" "${LANDING_DIR}/src/js/" +echo "==> Building neuron-web with elb" +"$ELB" build --elc "$ELC" --runtime "$EL_RUNTIME" +echo " Binary: $(ls -lh dist/neuron-landing)" echo "==> Compiling client-side El (src/js/*.el) → dist/js/" +cp "$EL_RUNTIME/el_runtime.js" src/js/ mkdir -p dist/js -for f in "${LANDING_DIR}/src/js/"*.el; do +for f in src/js/*.el; do + [ -f "$f" ] || continue name=$(basename "$f" .el) - "${ELC_JS}" --target=js --bundle --minify --obfuscate "$f" > "${LANDING_DIR}/dist/js/${name}.js" + "$ELC" --target=js --bundle --minify --obfuscate "$f" > "dist/js/${name}.js" echo " compiled: src/js/${name}.el → dist/js/${name}.js" done - -# Clean up the staged runtime (not a source file) -rm -f "${LANDING_DIR}/src/js/el_runtime.js" - -echo "==> Combining El sources → dist/main-combined.el" -COMPONENTS=(nav hero pillars how_it_works inference efficiency comparison - environmental enterprise mission local_first pricing marketplace viral - footer styles about founding_badge terms enterprise_terms checkout safety - gallery account) -{ - for f in "${COMPONENTS[@]}"; do - if [ -f "src/${f}.el" ]; then - grep -hv '^[[:space:]]*from\|^[[:space:]]*import' "src/${f}.el" - echo "" - fi - done - grep -v '^from\|^import' src/main.el -} > dist/main-combined.el -echo " $(wc -l < dist/main-combined.el) lines" - -echo "==> Compiling dist/main-combined.el → dist/main.c via ${ELC}" -"${ELC}" dist/main-combined.el > dist/main.c -echo " $(wc -l < dist/main.c) lines of C" - -echo "==> Injecting host-side stub forward declarations" -# GNU vs BSD sed: -i with no arg works on GNU, breaks on macOS BSD sed -# (BSD requires -i ''). Detect and branch. -SED_INPLACE=(-i) -if sed --version >/dev/null 2>&1; then - SED_INPLACE=(-i) -else - SED_INPLACE=(-i '') -fi -sed "${SED_INPLACE[@]}" \ - 's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t http_post_auth_json(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t http_delete_auth(el_val_t url, el_val_t bearer_tok, el_val_t apikey);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);\nel_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query);\nel_val_t supabase_auth_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt);\nel_val_t supabase_admin_invite(el_val_t project_url, el_val_t service_key, el_val_t body_json);\nel_val_t supabase_upsert_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt, el_val_t table_and_query, el_val_t row_json);|' \ - dist/main.c +rm -f src/js/el_runtime.js echo "==> Building Docker image marketing:${TAG}" -# Plain `docker build` — the gitea runner doesn't ship buildx, so -# `docker buildx build --platform ...` exits 125 ("unknown flag: -# --platform"). The runner host is already linux/amd64 so the -# explicit --platform is redundant. BUILDKIT_INLINE_CACHE works with -# plain docker as long as DOCKER_BUILDKIT=1 is set (default on the -# runner). docker build \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --cache-from us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest \