From 76840d8a570eadc0f6e64d822ff577e89eab9cbd Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Thu, 28 May 2026 11:36:07 +0100 Subject: [PATCH] Add CI DB migration smoke test against v2.0/v2.5/v2.10 updates (#6453) --- .github/config/.files.yaml | 2 + .github/workflows/build.yml | 14 ++ .github/workflows/db-migration-test.yml | 93 +++++++++ .gitignore | 4 + app/proprietary/.gitignore | 2 + .../resources/db-migration-fixtures/README.md | 110 ++++++++++ .../stirling-pdf-v2.0.0.mv.db | Bin 0 -> 45056 bytes .../stirling-pdf-v2.10.0.mv.db | Bin 0 -> 73728 bytes .../stirling-pdf-v2.5.0.mv.db | Bin 0 -> 57344 bytes scripts/db-migration/run-migration-test.sh | 194 ++++++++++++++++++ 10 files changed, 419 insertions(+) create mode 100644 .github/workflows/db-migration-test.yml create mode 100644 app/proprietary/src/test/resources/db-migration-fixtures/README.md create mode 100644 app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.0.0.mv.db create mode 100644 app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.10.0.mv.db create mode 100644 app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.5.0.mv.db create mode 100644 scripts/db-migration/run-migration-test.sh diff --git a/.github/config/.files.yaml b/.github/config/.files.yaml index 8066cc3620..5343246c25 100644 --- a/.github/config/.files.yaml +++ b/.github/config/.files.yaml @@ -38,6 +38,8 @@ project: &project - frontend/** - docker/** - scripts/RestartHelper.java + - scripts/db-migration/** + - .github/workflows/db-migration-test.yml frontend: &frontend - frontend/** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 05029d2aab..ac47fe1341 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,6 +68,18 @@ jobs: uses: ./.github/workflows/backend-build.yml secrets: inherit + db-migration-test: + # Boots the current bootJar against H2 fixtures captured from past + # releases (v2.0.0 / v2.5.0 / v2.10.0) and verifies admin login still + # works after Hibernate's ddl-auto=update migrates the schema. Gated on + # the `project` filter so doc-only PRs skip this ~5-minute job. + if: needs.files-changed.outputs.project == 'true' + needs: [files-changed] + permissions: + contents: read + uses: ./.github/workflows/db-migration-test.yml + secrets: inherit + check-generateOpenApiDocs: if: needs.files-changed.outputs.openapi == 'true' needs: [files-changed] @@ -184,6 +196,7 @@ jobs: needs: - files-changed - build + - db-migration-test - check-generateOpenApiDocs - frontend-validation - playwright-e2e @@ -208,6 +221,7 @@ jobs: RESULTS: | files-changed=${{ needs.files-changed.result }} build=${{ needs.build.result }} + db-migration-test=${{ needs.db-migration-test.result }} check-generateOpenApiDocs=${{ needs.check-generateOpenApiDocs.result }} frontend-validation=${{ needs.frontend-validation.result }} playwright-e2e=${{ needs.playwright-e2e.result }} diff --git a/.github/workflows/db-migration-test.yml b/.github/workflows/db-migration-test.yml new file mode 100644 index 0000000000..2a386d1a5a --- /dev/null +++ b/.github/workflows/db-migration-test.yml @@ -0,0 +1,93 @@ +name: DB migration smoke test + +# Boots the current Stirling-PDF JAR against H2 fixtures captured from past +# releases (v2.0.0 / v2.5.0 / v2.10.0) and verifies admin login still works. +# Catches schema changes that would break existing user databases under +# Hibernate's `ddl-auto=update` upgrade path. + +on: + workflow_call: + +permissions: + contents: read + +jobs: + pick: + uses: ./.github/workflows/_runner-pick.yml + + migration-test: + needs: pick + runs-on: ${{ needs.pick.outputs.is_fork == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-8' }} + timeout-minutes: 30 + env: + DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up JDK 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + java-version: 25 + distribution: temurin + + - name: Cache Gradle dependency artifacts + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ~/.gradle/wrapper + ~/.gradle/caches/modules-2/files-2.1 + ~/.gradle/caches/modules-2/metadata-2.* + key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }} + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 + with: + gradle-version: 9.3.1 + cache-disabled: true + + # No `-PnoSpotless` here yet because the upstream cache layer matches the + # backend build's; reuse keeps cold-cache cost identical. + - name: Build Stirling-PDF JAR + env: + MAVEN_USER: ${{ secrets.MAVEN_USER }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} + run: ./gradlew :stirling-pdf:bootJar -PnoSpotless --no-daemon + + - name: Locate built JAR + id: jar + run: | + jar=$(find app/core/build/libs -maxdepth 1 -name 'Stirling-PDF*.jar' -o -name 'stirling-pdf*.jar' 2>/dev/null \ + | grep -vE '(-plain|-sources)\.jar$' | head -n 1) + if [[ -z "$jar" ]]; then + echo "::error::No JAR under app/core/build/libs" + ls -lah app/core/build/libs || true + exit 1 + fi + # Absolute path - the migration script pushd's into a temp workdir + # before invoking java, which would dangle a relative path. + jar=$(realpath "$jar") + echo "path=$jar" >> "$GITHUB_OUTPUT" + echo "Built JAR: $jar" + + - name: Run migration smoke test + env: + STIRLING_JAR: ${{ steps.jar.outputs.path }} + run: bash scripts/db-migration/run-migration-test.sh + + - name: Upload app logs on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: db-migration-app-logs + # Path matches the preserved workdir in run-migration-test.sh - + # only failing fixtures leave a directory behind. + path: /tmp/stirling-migration-failed-*/app.log + retention-days: 7 + if-no-files-found: warn diff --git a/.gitignore b/.gitignore index c67ec629c2..f4558af4c7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,10 @@ customFiles/ configs/ watchedFolders/ clientWebUI/ +# Scratch dir used by local fixture-regeneration runs (see +# app/proprietary/src/test/resources/db-migration-fixtures/README.md). +# Holds downloaded JARs and disposable workdirs. Never committed. +.alpha-local/ !cucumber/ !cucumber/exampleFiles/ !cucumber/exampleFiles/example_html.zip diff --git a/app/proprietary/.gitignore b/app/proprietary/.gitignore index 5156eb74ad..1a5610dc1a 100644 --- a/app/proprietary/.gitignore +++ b/app/proprietary/.gitignore @@ -123,6 +123,8 @@ SwaggerDoc.json *.tar.gz *.rar *.db +# Whitelist the H2 fixtures that feed the version-migration CI smoke test. +!src/test/resources/db-migration-fixtures/*.mv.db /build /app/proprietary/build/ diff --git a/app/proprietary/src/test/resources/db-migration-fixtures/README.md b/app/proprietary/src/test/resources/db-migration-fixtures/README.md new file mode 100644 index 0000000000..25450cf7f7 --- /dev/null +++ b/app/proprietary/src/test/resources/db-migration-fixtures/README.md @@ -0,0 +1,110 @@ +# DB migration test fixtures + +These `.mv.db` files are H2 databases captured from past Stirling-PDF releases. +They feed the CI smoke test that verifies a fresh build can still boot and +authenticate against a database created by an older version. + +| File | Source release | Tables | Notes | +|---|---|---|---| +| `stirling-pdf-v2.0.0.mv.db` | [v2.0.0](https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v2.0.0) | users, authorities, teams, sessions, audit_events, persistent_logins, invite_tokens, user_license_settings, user_settings | Pre-storage/workflow schema. | +| `stirling-pdf-v2.5.0.mv.db` | [v2.5.0](https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v2.5.0) | same as v2.0.0 | Schema unchanged from v2.0.0; intentionally kept as a separate fixture to exercise the "skip every other minor" upgrade path. | +| `stirling-pdf-v2.10.0.mv.db` | [v2.10.0](https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v2.10.0) | v2.5.0 tables + file_shares, file_share_accesses, stored_files, stored_file_blobs, storage_cleanup_entries, user_server_certificates, workflow_sessions, workflow_participants, participant_notifications | Adds the file-sharing and workflow signing schema. | + +All three were generated against H2 `2.3.232` and use the same on-disk file +format, so the runtime driver can open any of them without conversion. + +## What's in each fixture + +* `admin` user with the default password `stirling` (BCrypt `$2a$10$...`). +* The internal API user `STIRLING-PDF-BACKEND-API-USER`. +* `ROLE_ADMIN` authority row for the admin user. +* `Default` and `Internal` teams. +* `user_license_settings` row (singleton). + +`audit_events`, `sessions`, and `user_settings` are empty in the OSS-flavored +fixtures: those tables are written only on Enterprise builds (audit) or +require an HTTP-session-creating flow (sessions / settings) that the OSS form +login no longer exposes. The migration test only depends on the admin user +existing, so leaving these empty is intentional. + +## What the CI test checks + +`.github/workflows/db-migration-test.yml` runs `scripts/db-migration/run-migration-test.sh`, +which for each fixture: + +1. Copies the fixture into `configs/stirling-pdf-DB-2.3.232.mv.db` of a clean + working directory. +2. Boots the current `:stirling-pdf:bootJar` against it on a free port. +3. Waits for Spring to start (no `SchemaManagementException` in the log). +4. POSTs `{"username":"admin","password":"stirling"}` to `/api/v1/auth/login` + and asserts the response is `200 OK`. + +A red CI on this job means a schema change in the PR is not backwards +compatible with an existing user database. Common causes: + +* Adding a non-nullable column without a default. +* Renaming a column (Hibernate's `update` strategy adds the new column and + leaves the old one orphaned with the data still in it). +* Changing a column type in an incompatible way. +* Dropping or renaming a foreign-key target. + +## Regenerating fixtures + +There's no automated regenerator script - fixtures are rare to refresh and the +manual steps are short. For each version you want to capture: + +```bash +# 1. Download the JAR for that release (requires `gh` authenticated against +# github.com/Stirling-Tools/Stirling-PDF). +gh release download v2.10.0 \ + --repo Stirling-Tools/Stirling-PDF \ + --pattern 'Stirling-PDF-with-login.jar' \ + --output /tmp/stirling-v2.10.0.jar + +# 2. Boot the JAR in a clean working directory. DB_CLOSE_ON_EXIT=TRUE is +# the only override that matters - it makes the H2 file flush on JVM exit +# even if you Ctrl-C instead of going through a graceful shutdown. +workdir=$(mktemp -d) +mkdir -p "$workdir/configs" +cd "$workdir" +java -jar /tmp/stirling-v2.10.0.jar \ + --server.port=8089 \ + --spring.datasource.url='jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;MODE=PostgreSQL' \ + & + +# 3. Wait until http://localhost:8089/login responds, then log in once to +# materialize whatever rows the app writes on first boot. +curl -sf -X POST -H 'Content-Type: application/json' \ + -d '{"username":"admin","password":"stirling"}' \ + http://localhost:8089/api/v1/auth/login + +# 4. Shut it down (any kill works - DB_CLOSE_ON_EXIT=TRUE handles the flush). +kill -TERM %1 && wait %1 + +# 5. Copy the .mv.db here, renamed for the version. +cp "$workdir/configs/stirling-pdf-DB-2.3.232.mv.db" \ + app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.10.0.mv.db +``` + +Requirements: Java 21+ (the historical JARs target Java 17 / 21). + +## Adding a new fixture + +When a new minor release ships, repeat the steps above for the new tag and +add a row to the table at the top of this file. Keep the historical fixtures - +the test gets stronger with each schema generation it covers. + +## Inspecting a fixture by hand + +The H2 driver bundled with the build ships an interactive shell: + +```bash +h2_jar=$(find ~/.gradle/caches/modules-2 -name 'h2-2.3.232.jar' | head -1) +cd app/proprietary/src/test/resources/db-migration-fixtures +java -cp "$h2_jar" org.h2.tools.Shell \ + -url 'jdbc:h2:file:./stirling-pdf-v2.10.0;ACCESS_MODE_DATA=r;MODE=PostgreSQL' \ + -user sa +``` + +`ACCESS_MODE_DATA=r` keeps the inspection read-only so you can't accidentally +mutate a committed fixture. diff --git a/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.0.0.mv.db b/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.0.0.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..5402eb037553ed9c1a4ba06908d9d9c14e9ad597 GIT binary patch literal 45056 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 k*d9y)pYDhEa6E9308+;FbN~PV literal 0 HcmV?d00001 diff --git a/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.10.0.mv.db b/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.10.0.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..56c00405567d6bb6e6ca8ff48519f844e5e2eb95 GIT binary patch literal 73728 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≦s)mXV-Yxcw@Y?hohgnE4m^N$KZHpf8PKf*AQ2icuzmi z5Kl)R&>TW&0J>7o;CS$T?D0PS?w)?=vi=}n#UqXGBaP}q9Rzaa+#vZVvW0@4N*)~*#WW$YuSOh$qT9sl+EHzT$EUB6cl(L z5}4h9@4H0VstPURk&1WtB!Yrdq=Jj9n`5X?h=OCVf~N~8yM{z65HY1m-0C$yf5?<6 zG{r$`@pw?mS5kn?YB>A*`nvjsxCVh*MXoNMA^u<%sD0!bgxNxJ^>y?l)J1fS2=EMY z4UTsVLG2?#9pU2`91;&r`&bnak?4^EINsMa#1YcB^$Pa)gIWSU?;<|L-`f@I^olWj zkymhtV@PN)EHqqPoqat0TwOqc0XYZ)!VYltbMf?Z2ML2_A|Zm{Fv2qL14@wK+z*)! zg}MlyIH22FkyIep%C@An5s6C%D47?%jmToBpvW5vDf@}&6{8=>0rwYDoPhE!sAI#x zz+fe;$;`vVz{td4=m$!7@!$a#L!-pt5YHeVPd|6v02eo1Cr4**S3ehB#{f@VQ1BTs zFfb?xXR=5_bi(FZjC?%vN>YpR5_1?B7*d6KSf#+TGpfA0rdDZ|RVn4!=2hmoCc3_b z6#*_TL4jq-z9snnj4P$|vLO$sWpEY0>RcF!?QbO-(TgZ3a!Ls@j!b1>U_dg^GchGMGtbb7S0&XT z#mLaaAVt?8+1OCm#L(PAH__BAO*b{k$j~Ik$i&njDG}zH@4I)Y9%vPIbZ~G$xF*RY zxWH7utjx>IKQlPBJUk=Q&&XJ>GCb1EHKaHt#HTRW+2700u`r{wLeJ4H$IUXoAiEUm znqzx*sV?|sN;v7riB94)WU%@YWf!mgefzgl& zlr9;BMgQ_RfTE7cklBa@EW|JRiPsUXf+a;LgO5Rk@O&V6&|1$F$Gjk9LeMY~N+(0< z6eyhvnJ9$M8$yJQO`$YYnTY{}Z(;~hZVI82M{kdVOpTFsdmIUC?#;k+04URo#uTi% zH-*m=V4Yq}P6MsEAJo%}Gof>zrqH?1H1L!OV#y?EniDejX$THF@bXEJAZYG089Fg* zVho)nF@Vl}f+rx+msCPx7%};Y2s~5pC#Yj=C@~usJZWTt2|Sz}CRPa{B}W13Lo) z%U3oA26hJqR&F;22KEL9R_Q+QtSoC%0|NuE0|RRo3j+hc0|RTB0|SEq0|RRn0|SGA z0|RT_0Z=K(z}hs;ILyS%GTh7D$tchR?c@YdwGC?1U^b;k&;N4_a)ks9>;RV0^Z)!p zkY-)nyhqRfbMisntb`b3#kcSi&l!bixf9$U8qVkcftylRu=#h`{J$Au{vUk*Ukc6U F{{j1W6(9fr literal 0 HcmV?d00001 diff --git a/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.5.0.mv.db b/app/proprietary/src/test/resources/db-migration-fixtures/stirling-pdf-v2.5.0.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..1032bf42d942e390647230eb9c77c5b047c430c9 GIT binary patch literal 57344 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 literal 0 HcmV?d00001 diff --git a/scripts/db-migration/run-migration-test.sh b/scripts/db-migration/run-migration-test.sh new file mode 100644 index 0000000000..953f58712f --- /dev/null +++ b/scripts/db-migration/run-migration-test.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash +# +# DB migration smoke test: for each H2 fixture under +# app/proprietary/src/test/resources/db-migration-fixtures/, copy it into a +# fresh working directory, boot the current Stirling-PDF JAR against it, then +# POST /api/v1/auth/login with the fixture's admin credentials. A 200 means +# Hibernate's ddl-auto=update migrated the legacy schema without breaking +# existing data. +# +# Inputs: +# STIRLING_JAR - path to a pre-built Stirling-PDF .jar (defaults to the +# :stirling-pdf:bootJar output) +# FIXTURE_DIR - override the fixture directory (rarely needed) +# +# Exits non-zero on any fixture failure and writes a summary to stderr. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +FIXTURE_DIR="${FIXTURE_DIR:-$REPO_ROOT/app/proprietary/src/test/resources/db-migration-fixtures}" +STIRLING_JAR="${STIRLING_JAR:-}" +ADMIN_USERNAME="${ADMIN_USERNAME:-admin}" +ADMIN_PASSWORD="${ADMIN_PASSWORD:-stirling}" +STARTUP_TIMEOUT_SEC="${STARTUP_TIMEOUT_SEC:-300}" + +log() { printf '[migration-test] %s\n' "$*" >&2; } +fail() { printf '[migration-test][FAIL] %s\n' "$*" >&2; exit 1; } + +find_jar() { + local candidate + if [[ -n "$STIRLING_JAR" ]]; then + [[ -f "$STIRLING_JAR" ]] || fail "STIRLING_JAR='$STIRLING_JAR' not found" + candidate="$STIRLING_JAR" + else + candidate=$(find "$REPO_ROOT/app/core/build/libs" -maxdepth 1 -name 'Stirling-PDF*.jar' -o -name 'stirling-pdf*.jar' 2>/dev/null \ + | grep -vE '(-plain|-sources)\.jar$' | head -n 1 || true) + [[ -n "$candidate" ]] || fail "No JAR under app/core/build/libs - run './gradlew :stirling-pdf:bootJar' first" + fi + # Resolve to an absolute path: test_fixture pushd's into a temp workdir + # before launching java, so a relative path here would dangle. + realpath "$candidate" +} + +free_port() { + python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()' +} + +wait_for_url() { + local url="$1" deadline=$(( $(date +%s) + STARTUP_TIMEOUT_SEC )) + while (( $(date +%s) < deadline )); do + local code + code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "$url" || true) + # Any HTTP response (200, 302, 401, 403, 404) means Spring is serving. + if [[ "$code" =~ ^[1-5][0-9][0-9]$ ]]; then return 0; fi + sleep 2 + done + return 1 +} + +test_fixture() { + local fixture_path="$1" + local label + label=$(basename "$fixture_path" .mv.db) + log "=== $label ===" + + local jar; jar=$(find_jar) + local workdir; workdir=$(mktemp -d) + local configsdir="$workdir/configs" + mkdir -p "$configsdir" + cp "$fixture_path" "$configsdir/stirling-pdf-DB-2.3.232.mv.db" + + local port; port=$(free_port) + local base_url="http://127.0.0.1:$port" + local log_file="$workdir/app.log" + + log " jar=$jar" + log " workdir=$workdir" + log " port=$port" + + # Force DB_CLOSE_ON_EXIT=TRUE so the H2 file flushes via shutdown hook even + # on a hard kill. show-sql off (output is enormous) but schema-tool INFO + # is on so any migration failure is visible in CI logs. + # + # pushd into the workdir so the JVM's `.` resolves to the isolated + # workdir - InstallationPathConfig reads `./configs/settings.yml` relative + # to cwd, and we want to make sure we hit the fixture's configs/ and not + # whatever happens to live at the runner's working directory. + pushd "$workdir" >/dev/null + java -Xmx1g -jar "$jar" \ + "--server.port=$port" \ + "--spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;MODE=PostgreSQL" \ + "--spring.jpa.show-sql=false" \ + "--logging.level.root=WARN" \ + "--logging.level.stirling=INFO" \ + "--logging.level.org.hibernate.tool.schema=INFO" \ + > "$log_file" 2>&1 & + local pid=$! + popd >/dev/null + + local rc=0 + if ! wait_for_url "$base_url/login"; then + log " app did not start within $STARTUP_TIMEOUT_SEC sec; last 60 lines of stdout:" + tail -n 60 "$log_file" >&2 + rc=1 + else + log " app started" + + if grep -E -i 'SchemaManagementException|GenerationTarget encountered exception|liquibase|Flyway.*FAILED' "$log_file" >&2; then + log " Hibernate reported schema migration errors (see log above)" + rc=1 + fi + + if (( rc == 0 )); then + local login_body; login_body=$(printf '{"username":"%s","password":"%s"}' "$ADMIN_USERNAME" "$ADMIN_PASSWORD") + local resp_file="$workdir/login.resp" + local code + code=$(curl -s -o "$resp_file" -w '%{http_code}' \ + --max-time 30 \ + -H 'Content-Type: application/json' \ + -d "$login_body" \ + "$base_url/api/v1/auth/login" || echo "000") + log " POST /api/v1/auth/login -> HTTP $code" + if [[ "$code" != "200" ]]; then + log " response body:" + head -c 500 "$resp_file" >&2 || true; echo >&2 + log " last 40 lines of app log:" + tail -n 40 "$log_file" >&2 + rc=1 + else + # Cheap sanity check on a second endpoint -- proves the app is + # not just stubbing the login response. + local status_code + status_code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$base_url/api/v1/info/status" || echo "000") + log " GET /api/v1/info/status -> HTTP $status_code" + if [[ "$status_code" != "200" ]]; then + log " sanity check failed: /api/v1/info/status returned non-200" + rc=1 + else + log " PASS: $label migrated and admin login succeeded" + fi + fi + fi + fi + + # H2 flushes via DB_CLOSE_ON_EXIT=TRUE so a forced kill is safe. + kill -TERM "$pid" 2>/dev/null || true + local i + for i in $(seq 1 15); do + kill -0 "$pid" 2>/dev/null || break + sleep 1 + done + kill -KILL "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + + if (( rc == 0 )); then + rm -rf "$workdir" 2>/dev/null || true + else + # Preserve workdir so CI can upload the app log as an artifact for + # post-mortem. Move it to a stable name so the upload path is fixed. + local preserved + preserved="${MIGRATION_TEST_LOG_DIR:-/tmp}/stirling-migration-failed-$label" + rm -rf "$preserved" + mv "$workdir" "$preserved" 2>/dev/null || true + log " preserved failing workdir at $preserved" + fi + + return $rc +} + +main() { + [[ -d "$FIXTURE_DIR" ]] || fail "Fixture dir not found: $FIXTURE_DIR" + local fixtures + mapfile -t fixtures < <(find "$FIXTURE_DIR" -maxdepth 1 -name '*.mv.db' | sort) + [[ ${#fixtures[@]} -gt 0 ]] || fail "No fixtures under $FIXTURE_DIR" + + local failed=0 + local failed_names=() + for f in "${fixtures[@]}"; do + # set -e would abort the whole run on the first failure; we want to + # report every fixture's status, so guard with ||. + if ! test_fixture "$f"; then + failed=$(( failed + 1 )) + failed_names+=("$(basename "$f" .mv.db)") + fi + done + + if (( failed > 0 )); then + log "${failed}/${#fixtures[@]} fixture(s) failed: ${failed_names[*]}" + exit 1 + fi + log "All ${#fixtures[@]} fixtures migrated cleanly." +} + +main "$@"