Skip to content

Commit c810676

Browse files
committed
Add CI check that the previous release works against the new schema
Add a backwards-compatibility CI job that runs the previous release's database tests (pytest -m db) against the schema produced by the current code, exercising the online-migration requirement that a release's database must keep working with version N-1 of the code while its webservers are still serving traffic. - docker/ci/test_backwards_compatibility.yml: compose overlay defining a webapp-prev service (the previous release's image) in external-schema mode. - docker/ci/test_backwards_compatibility.sh: resolves the previous release from the latest GitHub release, builds the new schema with the current image, then runs the previous release's db tests against it. Skips when the previous release predates the external-schema seam. - build.yml: new backwards-compatibility-test job, added to the push job's needs. This is the second of two PRs and depends on the test seam from the first. Until a release containing the seam becomes the previous release, the check skips.
1 parent 897a244 commit c810676

3 files changed

Lines changed: 156 additions & 1 deletion

File tree

.github/workflows/build.yml

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,50 @@ jobs:
422422
- name: Test migrations
423423
run: ./docker/ci/test_migrations.sh
424424

425+
backwards-compatibility-test:
426+
name: Backwards compatibility test
427+
runs-on: ubuntu-24.04
428+
needs: [build]
429+
permissions:
430+
contents: read
431+
432+
steps:
433+
- uses: actions/checkout@v6
434+
with:
435+
persist-credentials: false
436+
437+
- name: Download digests
438+
uses: actions/download-artifact@v8
439+
with:
440+
path: ${{ runner.temp }}/digests
441+
pattern: digests-amd64
442+
443+
# This sets the environment variable referenced in the docker-compose file
444+
# for the image to use for the webapp container to the digest of the image
445+
# that was built in the build job.
446+
- name: Set webapp image
447+
working-directory: ${{ runner.temp }}/digests/webapp
448+
run: |
449+
IMAGE="${{needs.build.outputs.webapp-repo}}$(printf '@sha256:%s' *)"
450+
echo "$IMAGE"
451+
echo "WEBAPP_IMAGE=$IMAGE" >> $GITHUB_ENV
452+
453+
# See comment here: https://github.com/actions/runner-images/issues/1187#issuecomment-686735760
454+
- name: Disable network offload
455+
run: sudo ethtool -K eth0 tx off rx off
456+
457+
- name: Set up Docker Buildx
458+
uses: docker/setup-buildx-action@v4
459+
460+
- name: Test backwards compatibility
461+
env:
462+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
463+
run: ./docker/ci/test_backwards_compatibility.sh
464+
425465
push:
426466
name: Tag & Push Images
427467
runs-on: ubuntu-24.04
428-
needs: [build, integration-test, unit-test, migration-test]
468+
needs: [build, integration-test, unit-test, migration-test, backwards-compatibility-test]
429469
permissions:
430470
contents: read
431471
packages: write
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/bash
2+
3+
# This script checks that the PREVIOUS released version of the application still works
4+
# against the database schema produced by the CURRENT code. It exercises our online
5+
# migration requirement: a release's database must keep working with version N-1 of the
6+
# code, because during a deploy the new migrations run while the previous version's
7+
# webservers are still serving traffic.
8+
#
9+
# It works by:
10+
# (1) Finding the previous release (the latest GitHub release) and its published
11+
# circ-webapp image.
12+
# (2) Confirming that image contains the external-schema test seam. The seam ships in a
13+
# later release than this check itself, so until a release that has it becomes the
14+
# previous release there is nothing to test and we skip.
15+
# (3) Initializing a fresh database with the CURRENT image, which builds the new schema.
16+
# (4) Running the previous release's database tests (pytest -m db) against that schema in
17+
# external-schema mode (PALACE_TEST_DATABASE_EXTERNAL_SCHEMA), so the older code
18+
# exercises the new schema. If those tests fail, the migration is not backwards
19+
# compatible.
20+
#
21+
# The current image is provided via the WEBAPP_IMAGE environment variable (as in the other
22+
# docker CI jobs). The previous release image can be overridden via PREV_RELEASE_IMAGE
23+
# (useful for running this script locally).
24+
25+
set -uo pipefail
26+
27+
COMPOSE_FILES=(-f docker-compose.yml -f docker/ci/test_backwards_compatibility.yml)
28+
29+
# Run a docker compose command for this check.
30+
compose_cmd() {
31+
docker compose "${COMPOSE_FILES[@]}" --progress quiet "$@"
32+
}
33+
34+
# Run a command in a container with the palace virtualenv activated.
35+
run_in_container() {
36+
local container="$1"
37+
shift
38+
compose_cmd run --rm --no-deps "${container}" /bin/bash -c "source env/bin/activate && $*"
39+
}
40+
41+
cleanup() {
42+
compose_cmd down --remove-orphans >/dev/null 2>&1
43+
}
44+
45+
fail() {
46+
echo "::error::$1"
47+
exit "${2:-1}"
48+
}
49+
50+
# (1) Resolve the previous release image.
51+
if [[ -z "${PREV_RELEASE_IMAGE:-}" ]]; then
52+
# In CI gh infers the repo from $GITHUB_REPOSITORY; locally it infers it from the git
53+
# remote of the current directory.
54+
if [[ -n "${GITHUB_REPOSITORY:-}" ]]; then
55+
prev_version="$(gh release view --repo "${GITHUB_REPOSITORY}" --json tagName --jq '.tagName' 2>/dev/null | sed 's/^v//')"
56+
else
57+
prev_version="$(gh release view --json tagName --jq '.tagName' 2>/dev/null | sed 's/^v//')"
58+
fi
59+
if [[ -z "${prev_version}" ]]; then
60+
echo "No previous GitHub release found; nothing to check. Skipping."
61+
exit 0
62+
fi
63+
PREV_RELEASE_IMAGE="ghcr.io/thepalaceproject/circ-webapp:${prev_version}"
64+
fi
65+
export PREV_RELEASE_IMAGE
66+
echo "Previous release image: ${PREV_RELEASE_IMAGE}"
67+
68+
if [[ -z "${WEBAPP_IMAGE:-}" ]]; then
69+
fail "WEBAPP_IMAGE is not set; it must point at the current build's webapp image."
70+
fi
71+
72+
trap cleanup EXIT
73+
74+
# (2) Make sure the previous release actually includes the external-schema test seam. It
75+
# ships in a later release than this check, so older images do not support it yet and there
76+
# is nothing meaningful to test.
77+
compose_cmd pull --quiet webapp-prev || fail "Could not pull ${PREV_RELEASE_IMAGE}."
78+
if ! run_in_container webapp-prev "grep -q 'external_schema' tests/fixtures/database.py"; then
79+
echo "Previous release image does not include the external-schema test seam; skipping."
80+
exit 0
81+
fi
82+
83+
# (3) Build the current schema by initializing a fresh database with the current image.
84+
compose_cmd up -d pg os minio redis || fail "Could not start service containers."
85+
run_in_container webapp "./bin/util/initialize_instance" \
86+
|| fail "Failed to initialize the database with the current image."
87+
88+
# (4) Run the previous release's database tests against the new schema. -n0 forces serial
89+
# execution, which external-schema mode requires (all tests share the one database).
90+
echo "Running the previous release's database tests against the current schema ..."
91+
if ! run_in_container webapp-prev \
92+
"uv sync --frozen --active && pytest --no-cov -n0 -m db --ignore=tests/migration tests"; then
93+
fail "Previous release tests failed against the current schema: the migration is not backwards compatible."
94+
fi
95+
96+
echo "The previous release works against the current schema 🎉"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Compose overlay for the backwards-compatibility check; see test_backwards_compatibility.sh.
2+
#
3+
# The `webapp` service (from docker-compose.yml, the image built by the current commit) is
4+
# used to build the new schema. `webapp-prev` runs a previously released image and re-runs
5+
# its database tests against that schema in external-schema mode.
6+
services:
7+
webapp-prev:
8+
# Inherit the full test environment (database/search/storage/redis URLs, etc.) from the
9+
# webapp service so the previous release's test suite can run, then point it at the
10+
# previous release's published image and turn on external-schema mode.
11+
extends:
12+
file: docker-compose.yml
13+
service: webapp
14+
image: "${PREV_RELEASE_IMAGE}"
15+
environment:
16+
PALACE_TEST_DATABASE_EXTERNAL_SCHEMA: "true"
17+
# External-schema mode uses the database as-is, so database creation must be disabled
18+
# (they are required to agree; see DatabaseCreationFixture).
19+
PALACE_TEST_DATABASE_CREATE_DATABASE: "false"

0 commit comments

Comments
 (0)