|
1 | 1 | name: Smoke (non-dev environments) |
2 | 2 |
|
3 | | -# Lucee-leg regression net for discussion #3023 / issues #3030, #3031, #3053 |
4 | | -# (and any future engine-agnostic non-dev request regression): boots the demo |
5 | | -# app with config/environment.cfm forced to production and testing and asserts |
| 3 | +# Regression net for discussion #3023 / issues #3029, #3030, #3031, #3053 (and |
| 4 | +# any future non-dev request regression): boots the demo app with |
| 5 | +# config/environment.cfm forced to production and testing and asserts |
6 | 6 | # non-development request behavior (clean 404s, no stack traces, reload refused |
7 | 7 | # without the password, authorized reload answers 302) via |
8 | 8 | # tools/ci/smoke-env.sh. |
9 | 9 | # |
10 | | -# Scope note: this job runs Lucee 7 only. The Adobe-only failure class from |
11 | | -# issue #3029 (engine error-template leakage in non-dev environments) is NOT |
12 | | -# gated here — the cfabort idiom itself is pinned structurally by |
13 | | -# vendor/wheels/tests/specs/security/BareCfabortGuardSpec.cfc, and the Adobe |
14 | | -# smoke leg is tracked in issue #3047. |
| 10 | +# Two engine families, same probes: |
| 11 | +# smoke — Lucee 7 via the setup-wheels-test-env composite (LuCLI). |
| 12 | +# smoke-adobe — Adobe 2023 via the compat-matrix Docker engine |
| 13 | +# (compose service adobe2023, tools/docker/adobe2023/). |
| 14 | +# This is the executed gate for the Adobe-only failure class |
| 15 | +# from issue #3029 (engine error-template leakage in non-dev |
| 16 | +# environments — issue #3047); the bare-cfabort idiom itself |
| 17 | +# is additionally pinned structurally by |
| 18 | +# vendor/wheels/tests/specs/security/BareCfabortGuardSpec.cfc. |
15 | 19 |
|
16 | 20 | on: |
17 | 21 | pull_request: |
|
26 | 30 | - '.github/workflows/smoke-env.yml' |
27 | 31 | - 'tools/ci/lucee.ci.json' |
28 | 32 | - '.github/actions/setup-wheels-test-env/**' |
| 33 | + - 'compose.yml' |
| 34 | + - 'tools/docker/adobe2023/**' |
29 | 35 |
|
30 | 36 | permissions: |
31 | 37 | contents: read |
@@ -79,3 +85,170 @@ jobs: |
79 | 85 | if [ -f /tmp/lucli-server.pid ]; then |
80 | 86 | kill $(cat /tmp/lucli-server.pid) 2>/dev/null || true |
81 | 87 | fi |
| 88 | +
|
| 89 | + # Adobe leg (issue #3047): the #3029 failure class (bare `cfabort;` → Adobe |
| 90 | + # error template + HTTP 500 on `/` and `/wheels/info` in non-dev |
| 91 | + # environments) only manifests on Adobe engines — the Lucee job above stays |
| 92 | + # green on the pre-fix tree. Boots the same adobe2023 Docker engine the |
| 93 | + # weekly compat-matrix builds (engine images are not registry-pullable, but |
| 94 | + # the `docker compose build` is ~90s in CI and the engine is serving in |
| 95 | + # ~4 min — measured on compat-matrix run 27081098861). No database |
| 96 | + # containers: the probes never touch the datasource, and the harness |
| 97 | + # datasource is pointed at SQLite (driver jar ships in |
| 98 | + # tools/docker/adobe2023/lib/) so nothing depends on an absent DB server. |
| 99 | + smoke-adobe: |
| 100 | + name: "Smoke: ${{ matrix.wheels_env }} (Adobe 2023)" |
| 101 | + runs-on: ubuntu-latest |
| 102 | + timeout-minutes: 25 |
| 103 | + strategy: |
| 104 | + fail-fast: false |
| 105 | + matrix: |
| 106 | + wheels_env: [production, testing] |
| 107 | + env: |
| 108 | + WHEELS_CI: "true" |
| 109 | + steps: |
| 110 | + - uses: actions/checkout@v5 |
| 111 | + |
| 112 | + # Must run before `docker compose up`: config/environment.cfm reaches the |
| 113 | + # container through the ./:/wheels-test-suite bind mount and is read once |
| 114 | + # at application start, so the app cold-boots directly into the matrix |
| 115 | + # environment. (Same step as the Lucee job above.) |
| 116 | + - name: Force ${{ matrix.wheels_env }} environment |
| 117 | + run: | |
| 118 | + sed -i 's/set(environment = "[a-z]*")/set(environment = "${{ matrix.wheels_env }}")/' config/environment.cfm |
| 119 | + grep -qF 'set(environment = "${{ matrix.wheels_env }}")' config/environment.cfm || { |
| 120 | + echo "::error::environment.cfm substitution failed — pattern drift?"; exit 1; } |
| 121 | + grep -n 'set(environment' config/environment.cfm |
| 122 | +
|
| 123 | + # The adobe2023 CFConfig's wheelstestdb_sqlite datasource points at |
| 124 | + # /wheels-test-suite/wheelstestdb.db — i.e. these files at the repo root |
| 125 | + # via the bind mount. Same recipe as setup-wheels-test-env. |
| 126 | + - name: Create SQLite test databases |
| 127 | + run: | |
| 128 | + sudo apt-get update -y && sudo apt-get install -y --no-install-recommends sqlite3 |
| 129 | + sqlite3 wheelstestdb.db "SELECT 1;" |
| 130 | + sqlite3 wheelstestdb_tenant_b.db "SELECT 1;" |
| 131 | +
|
| 132 | + # compose.yml bind-mounts tools/docker/adobe2023/settings.cfm over |
| 133 | + # config/settings.cfm, and that harness file defaults the app datasource |
| 134 | + # to wheelstestdb_sqlserver — a container this job does not start. The |
| 135 | + # probes never query the database, but the app's default datasource |
| 136 | + # should exist and be reachable so no lazily-triggered DB touch can 500 |
| 137 | + # a probe. Must run before `docker compose up` (sed -i replaces the |
| 138 | + # inode that the bind mount captures at container start). |
| 139 | + - name: Point the Adobe harness datasource at SQLite |
| 140 | + run: | |
| 141 | + sed -i 's/wheelstestdb_sqlserver/wheelstestdb_sqlite/' tools/docker/adobe2023/settings.cfm |
| 142 | + # The harness settings ship an EMPTY reloadPassword, which leaves |
| 143 | + # ?reload=true open to anonymous restarts — probe 4 would see a 302. |
| 144 | + # Stage a real password so probes 4/5 (refusal) and 6 (authorized |
| 145 | + # reload answers 302 — the #3053 Adobe regression path) all engage. |
| 146 | + sed -i 's/set(reloadPassword="")/set(reloadPassword="wheels-dev")/' tools/docker/adobe2023/settings.cfm |
| 147 | + grep -qF 'set(reloadPassword="wheels-dev")' tools/docker/adobe2023/settings.cfm || { |
| 148 | + echo "::error::settings.cfm reloadPassword substitution failed — pattern drift?"; exit 1; } |
| 149 | + grep -qF 'set(dataSourceName="wheelstestdb_sqlite")' tools/docker/adobe2023/settings.cfm || { |
| 150 | + echo "::error::settings.cfm datasource substitution failed — pattern drift?"; exit 1; } |
| 151 | + grep -in 'datasourcename' tools/docker/adobe2023/settings.cfm |
| 152 | +
|
| 153 | + # Build + boot mirrors compat-matrix.yml's "Start CF engine" step |
| 154 | + # (retry absorbs transient external download failures; the build is |
| 155 | + # idempotent, only `up -d` runs after success). No --no-cache: hosted |
| 156 | + # runners start with an empty build cache anyway. |
| 157 | + - name: Build and start Adobe 2023 |
| 158 | + run: | |
| 159 | + set -e |
| 160 | + MAX_ATTEMPTS=3 |
| 161 | + ATTEMPT=1 |
| 162 | + until docker compose build adobe2023; do |
| 163 | + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then |
| 164 | + echo "::error::docker compose build failed after ${MAX_ATTEMPTS} attempts" |
| 165 | + exit 1 |
| 166 | + fi |
| 167 | + echo "::warning::Build attempt ${ATTEMPT} failed — sleeping 30s before retry" |
| 168 | + ATTEMPT=$((ATTEMPT + 1)) |
| 169 | + sleep 30 |
| 170 | + done |
| 171 | + docker compose up -d adobe2023 |
| 172 | +
|
| 173 | + # Adapted from compat-matrix.yml's readiness loop: restart on container |
| 174 | + # crash, accept 200/302/404 (in non-dev the root route is a clean 404 — |
| 175 | + # the public welcome page is development-only), keep the last HTTP code |
| 176 | + # so timeout diagnostics distinguish "never bound" from "5xx". |
| 177 | + - name: Wait for Adobe 2023 to be ready |
| 178 | + run: | |
| 179 | + CONTAINER="wheels-adobe2023-1" |
| 180 | + PORT=62023 |
| 181 | + MAX_WAIT=60 |
| 182 | + WAIT_COUNT=0 |
| 183 | + RESTARTS=0 |
| 184 | + MAX_RESTARTS=3 |
| 185 | + LAST_HTTP_CODE="000" |
| 186 | + READY=0 |
| 187 | + while [ "$WAIT_COUNT" -lt "$MAX_WAIT" ]; do |
| 188 | + WAIT_COUNT=$((WAIT_COUNT + 1)) |
| 189 | +
|
| 190 | + CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' "$CONTAINER" 2>/dev/null || echo "missing") |
| 191 | + if [ "$CONTAINER_STATUS" = "exited" ] || [ "$CONTAINER_STATUS" = "dead" ] || [ "$CONTAINER_STATUS" = "missing" ]; then |
| 192 | + RESTARTS=$((RESTARTS + 1)) |
| 193 | + if [ "$RESTARTS" -le "$MAX_RESTARTS" ]; then |
| 194 | + echo "Container $CONTAINER has status '$CONTAINER_STATUS' — restarting (attempt $RESTARTS/$MAX_RESTARTS)..." |
| 195 | + docker compose up -d adobe2023 |
| 196 | + sleep 10 |
| 197 | + continue |
| 198 | + else |
| 199 | + echo "::error::Container $CONTAINER failed to start after $MAX_RESTARTS restart attempts" |
| 200 | + docker logs "$CONTAINER" 2>&1 | tail -100 |
| 201 | + exit 1 |
| 202 | + fi |
| 203 | + fi |
| 204 | +
|
| 205 | + # curl -w "%{http_code}" always prints a code (000 if no response); |
| 206 | + # no `|| echo` fallback or codes concatenate to "000000". |
| 207 | + LAST_HTTP_CODE=$(curl -s -o /dev/null --connect-timeout 2 --max-time 5 -w "%{http_code}" "http://localhost:${PORT}/" 2>/dev/null || true) |
| 208 | + LAST_HTTP_CODE=${LAST_HTTP_CODE:-000} |
| 209 | + if echo "$LAST_HTTP_CODE" | grep -qE "^(200|302|404)$"; then |
| 210 | + echo "Adobe 2023 is ready (HTTP $LAST_HTTP_CODE on attempt $WAIT_COUNT)" |
| 211 | + READY=1 |
| 212 | + break |
| 213 | + fi |
| 214 | +
|
| 215 | + if [ $((WAIT_COUNT % 10)) -eq 0 ]; then |
| 216 | + echo " attempt $WAIT_COUNT/$MAX_WAIT: container=$CONTAINER_STATUS, http=$LAST_HTTP_CODE" |
| 217 | + fi |
| 218 | + sleep 5 |
| 219 | + done |
| 220 | +
|
| 221 | + if [ "$READY" -ne 1 ]; then |
| 222 | + echo "::error::Adobe 2023 not ready after ${MAX_WAIT} attempts (last HTTP code: $LAST_HTTP_CODE)" |
| 223 | + if [ "$LAST_HTTP_CODE" = "000" ]; then |
| 224 | + echo "::notice::No HTTP response received — engine likely never bound to port $PORT." |
| 225 | + else |
| 226 | + echo "::notice::HTTP $LAST_HTTP_CODE received — engine bound but the app is returning errors." |
| 227 | + echo "=== Final response body (first 500 bytes) ===" |
| 228 | + curl -s --max-time 5 "http://localhost:${PORT}/" 2>/dev/null | head -c 500 || true |
| 229 | + echo |
| 230 | + echo "=== /end response body ===" |
| 231 | + fi |
| 232 | + echo "=== Container logs (last 200 lines) ===" |
| 233 | + docker logs "$CONTAINER" 2>&1 | tail -200 |
| 234 | + echo "=== /end logs ===" |
| 235 | + exit 1 |
| 236 | + fi |
| 237 | +
|
| 238 | + # Warm-up: make sure Wheels onApplicationStart has fully completed |
| 239 | + # before probing (compat-matrix does the same before its test runs). |
| 240 | + curl -s -o /dev/null --max-time 60 "http://localhost:${PORT}/" || true |
| 241 | + sleep 2 |
| 242 | +
|
| 243 | + - name: Run smoke probes |
| 244 | + run: | |
| 245 | + BASE_URL="http://localhost:62023" SMOKE_ENV="${{ matrix.wheels_env }}" \ |
| 246 | + SMOKE_RELOAD_PASSWORD="wheels-dev" bash tools/ci/smoke-env.sh |
| 247 | +
|
| 248 | + - name: Debug container logs |
| 249 | + if: failure() |
| 250 | + run: docker logs wheels-adobe2023-1 2>&1 | tail -200 || true |
| 251 | + |
| 252 | + - name: Stop container |
| 253 | + if: always() |
| 254 | + run: docker compose stop adobe2023 2>/dev/null || true |
0 commit comments