ci: add asset-only fast path (<5min for PNG changes)
Deploy marketing to Cloud Run / deploy (push) Has been cancelled

- Detect asset-only changes (src/assets/, src/shares/, static HTML, llms.txt)
  and skip El compilation, C build deps, and Docker full build entirely
- Fast path pulls :latest as base and rebuilds only the assets layer
- Gate clone-el, install-C-deps, elc-cache, build-elc, build-image, push-image
  behind asset_only != 'true'; deploy steps run unconditionally
- Switch build-stage.sh from registry cache driver (requires docker-container
  buildx driver) to inline cache backed by :latest — compatible with default
  docker driver on the runner
This commit is contained in:
Will Anderson
2026-05-02 18:08:43 -05:00
parent b83504c837
commit baba6fbb71
2 changed files with 53 additions and 4 deletions
+51 -2
View File
@@ -31,7 +31,24 @@ jobs:
- name: Checkout neuron-web
uses: actions/checkout@v4
with:
fetch-depth: 1
fetch-depth: 2
- name: Detect change type
id: changetype
run: |
set -euo pipefail
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD 2>/dev/null || echo "unknown")
echo "Changed files:"
echo "$CHANGED"
# Asset-only: only src/assets/, src/shares/, src/index.html, src/about.html, src/terms.html, src/enterprise-terms.html, src/llms.txt
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^$' || true)
if [ -z "$NON_ASSET" ] && [ "$CHANGED" != "unknown" ]; then
echo "asset_only=true" >> "$GITHUB_OUTPUT"
echo "=> Asset-only change detected, will use fast path"
else
echo "asset_only=false" >> "$GITHUB_OUTPUT"
echo "=> Full build required"
fi
# foundation/el contains the elc compiler that build-stage.sh shells out
# to. We clone el directly with git rather than a second
@@ -41,6 +58,7 @@ jobs:
# workspace so the path build-stage.sh expects (../../foundation/el)
# resolves correctly.
- name: Clone el (foundation/el — provides the elc compiler)
if: steps.changetype.outputs.asset_only != 'true'
env:
CHECKOUT_TOKEN: ${{ secrets.CHECKOUT_TOKEN }}
run: |
@@ -75,11 +93,13 @@ jobs:
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
- name: Install C build deps for elc
if: steps.changetype.outputs.asset_only != 'true'
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends build-essential libcurl4-openssl-dev libssl-dev
- name: Restore elc binary from GCS cache
if: steps.changetype.outputs.asset_only != 'true'
id: elc-cache
run: |
set -euo pipefail
@@ -99,7 +119,7 @@ jobs:
fi
- name: Build elc (cache miss only)
if: steps.elc-cache.outputs.cache_hit != 'true'
if: steps.changetype.outputs.asset_only != 'true' && steps.elc-cache.outputs.cache_hit != 'true'
run: |
set -euo pipefail
# The committed dist/platform/elc is arm64 from Will's mac. We
@@ -127,6 +147,7 @@ jobs:
echo "Will build and push: ${IMAGE}"
- name: Build image (build-stage.sh)
if: steps.changetype.outputs.asset_only != 'true'
env:
EXTRACT_JS: '1'
run: |
@@ -135,10 +156,38 @@ jobs:
docker tag "marketing:${{ steps.tag.outputs.tag }}" "us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest"
- name: Push image
if: steps.changetype.outputs.asset_only != 'true'
run: |
docker push "${{ steps.tag.outputs.image }}"
docker push "us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest"
- name: Asset-only fast build
if: steps.changetype.outputs.asset_only == 'true'
env:
IMAGE: ${{ steps.tag.outputs.image }}
run: |
set -euo pipefail
# Pull existing image as base - no El compilation needed
LATEST="us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest"
docker pull "$LATEST"
# Build tiny patch image: just replace the assets layer
cat > /tmp/Dockerfile.assets <<'EOF'
ARG BASE
FROM ${BASE}
COPY src/assets /srv/landing/assets
COPY src/shares /srv/landing/shares
EOF
docker buildx build --platform linux/amd64 --load \
--build-arg BASE="$LATEST" \
-f /tmp/Dockerfile.assets \
-t "marketing:${{ steps.tag.outputs.tag }}" \
-t "$IMAGE" \
-t "${LATEST%:*}:latest" \
.
docker push "$IMAGE"
docker push "${LATEST%:*}:latest"
echo "Fast asset build complete"
- name: Deploy to all marketing prod regions in parallel
env:
IMAGE: ${{ steps.tag.outputs.image }}
+2 -2
View File
@@ -89,8 +89,8 @@ sed "${SED_INPLACE[@]}" \
echo "==> Building Docker image marketing:${TAG} for linux/amd64"
docker buildx build --platform linux/amd64 --load \
--cache-from type=registry,ref=us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:cache \
--cache-to type=registry,ref=us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:cache,mode=max \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--cache-from us-central1-docker.pkg.dev/neuron-785695/neuron-marketing/marketing:latest \
-f Dockerfile.stage \
-t "marketing:${TAG}" \
.