Skip to content

docs(web/blog): Beyond findAll: Scopes, Enums, and the Chainable Quer… #2215

docs(web/blog): Beyond findAll: Scopes, Enums, and the Chainable Quer…

docs(web/blog): Beyond findAll: Scopes, Enums, and the Chainable Quer… #2215

Workflow file for this run

name: Wheels Snapshots
on:
push:
branches:
- develop
permissions:
contents: write
checks: write
pull-requests: write
env:
WHEELS_PRERELEASE: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
LUCLI_VERSION: "0.3.7"
jobs:
#############################################
# Fast Test: Lucee 7 + SQLite via LuCLI
#############################################
fast-test:
name: "Lucee 7 + SQLite (LuCLI)"
runs-on: ubuntu-latest
env:
WHEELS_CI: "true"
WHEELS_BROWSER_TEST_BASE_URL: "http://localhost:60007"
WHEELS_BROWSER_CI_ENABLE: "true"
steps:
- uses: actions/checkout@v5
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
- name: Install LuCLI
run: |
curl -sL "https://github.com/cybersonic/LuCLI/releases/download/v${LUCLI_VERSION}/lucli-${LUCLI_VERSION}-linux" \
-o /usr/local/bin/lucli
chmod +x /usr/local/bin/lucli
lucli --version
- name: Create test databases
run: |
sudo apt-get update -y && sudo apt-get install -y --no-install-recommends sqlite3
sqlite3 wheelstestdb.db "SELECT 1;"
sqlite3 wheelstestdb_tenant_b.db "SELECT 1;"
- name: Cache Playwright
id: playwright-cache
uses: actions/cache@v5
with:
path: |
~/.wheels/browser/lib
~/.cache/ms-playwright
key: playwright-${{ hashFiles('vendor/wheels/browser-manifest.json') }}
restore-keys: |
playwright-
- name: Install Playwright
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: |
mkdir -p ~/.wheels/browser/lib
# Download JARs from manifest
for row in $(jq -c '.classpath[]' vendor/wheels/browser-manifest.json); do
URL=$(echo "$row" | jq -r '.url')
FILE=$(echo "$row" | jq -r '.filename')
SHA=$(echo "$row" | jq -r '.sha256')
echo "Downloading ${FILE}..."
curl -sL "$URL" -o ~/.wheels/browser/lib/"$FILE"
ACTUAL=$(sha256sum ~/.wheels/browser/lib/"$FILE" | cut -d' ' -f1)
if [ "$ACTUAL" != "$SHA" ]; then
echo "::error::SHA-256 mismatch for ${FILE}: expected ${SHA}, got ${ACTUAL}"
exit 1
fi
done
# Build classpath and install Chromium + system deps
CP=$(ls ~/.wheels/browser/lib/*.jar | tr '\n' ':')
java -cp "$CP" com.microsoft.playwright.CLI install --with-deps chromium
- name: Download SQLite JDBC driver
run: |
curl -sL "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.50.3.0/sqlite-jdbc-3.50.3.0.jar" \
-o /tmp/sqlite-jdbc.jar
- name: Start Lucee server
run: |
cp tools/ci/lucee.ci.json lucee.json
nohup lucli server run --port=60007 > /tmp/lucli-server.log 2>&1 &
echo $! > /tmp/lucli-server.pid
echo "Waiting for Lucee to start..."
for i in $(seq 1 120); do
if curl -s -o /dev/null --connect-timeout 2 --max-time 3 "http://localhost:60007/" 2>/dev/null; then
echo "Lucee is up (attempt ${i})"
break
fi
if ! kill -0 $(cat /tmp/lucli-server.pid) 2>/dev/null; then
echo "::error::LuCLI server process died"
cat /tmp/lucli-server.log
exit 1
fi
sleep 2
done
- name: Install SQLite JDBC into Lucee
run: |
LUCEE_LIB=$(find ~/.lucli/express -path "*/lib/ext" -type d 2>/dev/null | head -1)
if [ -z "$LUCEE_LIB" ]; then
LUCEE_LIB=$(find ~/.lucli/express -maxdepth 2 -name "lib" -type d 2>/dev/null | head -1)
fi
if [ -n "$LUCEE_LIB" ]; then
cp /tmp/sqlite-jdbc.jar "$LUCEE_LIB/"
echo "SQLite JDBC installed to: $LUCEE_LIB"
else
echo "::error::Could not find Lucee lib directory"
exit 1
fi
kill $(cat /tmp/lucli-server.pid) 2>/dev/null || true
lucli server stop 2>/dev/null || true
for i in $(seq 1 15); do
if ! curl -s -o /dev/null --connect-timeout 1 "http://localhost:60007/" 2>/dev/null; then
break
fi
sleep 1
done
fuser -k 60007/tcp 2>/dev/null || true
sleep 2
nohup lucli server run --port=60007 --force > /tmp/lucli-server.log 2>&1 &
echo $! > /tmp/lucli-server.pid
echo "Waiting for Lucee to restart..."
for i in $(seq 1 60); do
if curl -s -o /dev/null --connect-timeout 2 --max-time 3 "http://localhost:60007/" 2>/dev/null; then
echo "Lucee restarted with SQLite JDBC"
break
fi
sleep 2
done
- name: Run tests
run: |
mkdir -p /tmp/test-results /tmp/junit-results
PORT=60007 \
RESULT_DIR=/tmp/test-results \
JUNIT_DIR=/tmp/junit-results \
bash tools/ci/run-tests.sh
- name: Upload test results
if: always()
uses: actions/upload-artifact@v6
with:
name: test-results-lucee7-sqlite
path: /tmp/test-results/
if-no-files-found: warn
- name: Upload JUnit results
if: always()
uses: actions/upload-artifact@v6
with:
name: junit-lucee7-sqlite
path: /tmp/junit-results/
if-no-files-found: warn
- name: Generate API docs JSON (snapshot)
run: |
mkdir -p docs/api
VERSION=$(jq -r '.version' wheels.json)
# Lowercase "-snapshot" matches the URL slug convention used by
# web/scripts/generate-api-docs.mjs (version.replace(/\./g, '-')).
SNAPSHOT_FILE="docs/api/v${VERSION}-snapshot.json"
curl -sf --max-time 60 \
"http://localhost:60007/wheels/api?format=json&type=core" \
-o "$SNAPSHOT_FILE"
if ! jq -e '.functions and .sections' "$SNAPSHOT_FILE" > /dev/null 2>&1; then
echo "::error::API docs JSON invalid or empty"
head -c 500 "$SNAPSHOT_FILE" || true
exit 1
fi
echo "Generated $SNAPSHOT_FILE ($(wc -c < "$SNAPSHOT_FILE") bytes)"
- name: Upload API docs snapshot
uses: actions/upload-artifact@v6
with:
name: api-docs-snapshot
path: docs/api/v*-snapshot.json
if-no-files-found: error
retention-days: 7
- name: Debug server logs
if: failure()
run: cat /tmp/lucli-server.log 2>/dev/null || true
- name: Stop server
if: always()
run: |
if [ -f /tmp/lucli-server.pid ]; then
kill $(cat /tmp/lucli-server.pid) 2>/dev/null || true
fi
lucli server stop 2>/dev/null || true
#############################################
# Build Snapshot Release (gates on fast-test)
#############################################
build:
needs: fast-test
uses: ./.github/workflows/release.yml
secrets:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
FORGEBOX_API_TOKEN: ${{ secrets.FORGEBOX_API_TOKEN }}
FORGEBOX_PASS: ${{ secrets.FORGEBOX_PASS }}
DOWNSTREAM_DISPATCH_TOKEN: ${{ secrets.DOWNSTREAM_DISPATCH_TOKEN }}
# Optional: when present, the build job's "Dispatch Linux repo buckets"
# step fires repository_dispatch on apt-wheels / yum-wheels so
# the bleeding-edge channel of apt.wheels.dev / yum.wheels.dev tracks
# every develop push. When absent, the step skips silently. See #2605.
LINUX_REPO_DISPATCH_TOKEN: ${{ secrets.LINUX_REPO_DISPATCH_TOKEN }}
# Required: the snapshot-publish step in release.yml writes the GitHub
# Release to wheels-dev/wheels-snapshots, which needs a PAT with
# Contents:write on that repo. Stable (main) builds don't use this and
# rely on the default GITHUB_TOKEN.
WHEELS_SNAPSHOTS_TOKEN: ${{ secrets.WHEELS_SNAPSHOTS_TOKEN }}
#############################################
# Deploy API docs snapshot to Cloudflare Pages
#############################################
# Regenerates the v{version}-snapshot markdown from the JSON produced by
# fast-test, builds the Starlight api site, and deploys to the wheels-api
# Cloudflare Pages project. Runs on every develop push so the snapshot
# version on api.wheels.dev tracks the current framework state.
deploy-api-snapshot:
needs: fast-test
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
concurrency:
group: deploy-api-snapshot-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download API docs snapshot artifact
uses: actions/download-artifact@v6
with:
name: api-docs-snapshot
path: docs/api/
- name: Set up pnpm
uses: pnpm/action-setup@v5
with:
version: 10.23.0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Generate snapshot markdown from JSON
run: |
VERSION=$(jq -r '.version' wheels.json)
node web/scripts/generate-api-docs.mjs "${VERSION}-snapshot"
- name: Build api site
working-directory: web
run: pnpm --filter @wheels-dev/site-api build
- name: Deploy to Cloudflare Pages
working-directory: web
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
DEPLOY_BRANCH: ${{ github.ref_name }}
run: |
# Pass --commit-message explicitly. Without it, wrangler auto-derives
# the commit message from git, and Cloudflare's API rejected this
# repo's auto-generated GitHub merge commit body in run 25641189988
# with "Invalid commit message, it must be a valid UTF-8 string"
# (despite the body being valid UTF-8 — CF appears to have a stricter
# rule than the error message implies). The fix is to send a short
# ASCII-only message derived from the version + run; that always works.
VERSION=$(jq -r '.version' ../wheels.json)
pnpm dlx wrangler@4.78.0 pages deploy "sites/api/dist" \
--project-name="wheels-api" \
--branch="${DEPLOY_BRANCH}" \
--commit-hash="${GITHUB_SHA}" \
--commit-message="snapshot deploy: ${VERSION}-snapshot (run ${GITHUB_RUN_ID})"