# P4 server release compatibility
`version_compatibility.py` creates a fresh disposable commit-server installation
for every selected release/platform pair. The required matrix is P4 **2023.1,
2023.2, 2024.1, 2024.2, 2025.1, 2025.2 and 2026.1** on UBI **8.10 and 9.8**.
These release lines come from the vendor's [server schema release table](https://help.perforce.com/helix-core/server-apps/schema/current/).
Each cell downloads its own matching `p4`, `p4d`, broker and proxy binaries and
verifies their hashes against that release's vendor `SHA256SUMS`, for example
[2023.1 x86_64](https://ftp.perforce.com/perforce/r23.1/bin.linux26x86_64/SHA256SUMS)
and [2026.1 ARM64](https://ftp.perforce.com/perforce/r26.1/bin.linux26aarch64/SHA256SUMS).
The exact patch builds, checksums and immutable image IDs are recorded with the
results; this does not test every earlier patch build in a release line.
The runner refuses an existing container. Every cell invokes the actual
`reset.sh`, `mkdirs.sh` and generated `mkdirs.cfg` with an empty database. It does
not downgrade populated databases or rehearse an existing-install migration.
Its distro Python 3.12 virtual environment and P4Python are separately labeled;
this compatibility matrix does not replace the main source-built Python matrix.
The journal is `/p4/1/journals/journal`, storage is local, mail is disabled, and
all server endpoints are confined to their container's loopback interface.
The temporary restored-database probes disable journaling with `-J off` while
comparing data; every installed live server retains the required journal path.
Their scratch database is removed only after the daemon has exited. A failed
stop or shutdown timeout fails the scenario and retains that scratch directory
inside the stopped test container for inspection.
Each cell must pass all eight named scenarios:
| Scenario | Required behavior |
| --- | --- |
| `clean_install` | Real clean SDP installation, version-matched binaries and systemd startup |
| `service_user_authentication` | Create a native service user, initialize its password under server policy, authenticate and perform a real read |
| `exact_fixture` | Independently known text/binary revision bytes, edit/delete/move history, shelf contents and label/client/group specifications |
| `restart_preserves_data` | Installed service restart preserves that exact fixture |
| `checkpoint_restore` | SDP checkpoint/restore is served separately and its fixture is compared with the original |
| `journal_rotation_restore` | A later submitted file and counter survive journal rotation and replay |
| `maintenance_queries` | Real inactivity queries, complete path inventory and limit-zero client selection preserve fixture data |
| `sendq_cleanup` | Real local configuration scope/identity/path selection, service-down enforcement, dedicated queue cleanup and exact shared partitioned-client have preservation |
The cleanup scenario runs the same twenty-four named subcases as the current-server
matrix. These cover unset/empty, global/local/other-server settings, relative and
absolute paths, root symlinks, missing/non-directory targets, P4NAME environment
and database redirects, `-In` precedence, and running-server refusal. Shared
readonly overlap in either direction, or a readonly path traversing queue
symlinks (including a chain whose configured path starts outside the queue),
must preserve every required alias, actual persistent tables and synced
partitioned-client have revisions. A queue containing an alias required by the
configured database root or by the queue path itself must be refused without
removing any queue entries. Queue cleanup runs only while the
installed service is stopped; configuration is restored before the next scenario.
Missing binaries, setup failures, failed capabilities, missing scenarios or missing
matrix cells fail acceptance. A failed prerequisite blocks later dependent
scenarios and remains a failed cell. Stopped containers and logs are retained for
inspection; remove only explicitly named test containers after reviewing results.
Older servers can lack the `p4 diskspace` features used by installer safety
checks. The report counts and exposes these skipped checks in `capability_gaps`;
passing the data scenarios does not claim that those safety checks ran.
Version-gated configurable omissions are recorded there individually with their
required and running server versions. Applicable settings must still succeed.
Unexpected configuration errors still fail the installation cell, even if the
installer exits successfully after logging a warning.
## Images and execution
Build the regular UBI images with the [main matrix runner](README.md). On an ARM
host, releases 2023.1 and 2023.2 require x86_64 emulation: their vendor ARM64 binary
directories are absent. A working Linux `binfmt` emulator is a prerequisite; record
it with the results and do not describe emulated tests as native x86 coverage.
Build the two x86 package images using the existing definitions:
```bash
docker build --platform linux/amd64 -f sdp/test/containers/Dockerfile.rhel8 -t sdp-assurance-rhel8-amd64:8.10 sdp/test/containers
docker build --platform linux/amd64 -f sdp/test/containers/Dockerfile.rhel9 -t sdp-assurance-rhel9-amd64:9.8 sdp/test/containers
```
Derive the historical images before starting containers. This masks
`systemd-binfmt.service` inside these disposable images, preventing a container
boot from resetting the dedicated VM's emulator registration. For each base/tag
pair below, run:
```bash
docker build --build-arg BASE_IMAGE= -f sdp/test/containers/Dockerfile.versions -t sdp/test/containers
```
| Base | Tag |
| --- | --- |
| `sdp-assurance-rhel8:8.10` | `sdp-version-rhel8:8.10` |
| `sdp-assurance-rhel9:9.8` | `sdp-version-rhel9:9.8` |
| `sdp-assurance-rhel8-amd64:8.10` | `sdp-version-rhel8-amd64:8.10` |
| `sdp-assurance-rhel9-amd64:9.8` | `sdp-version-rhel9-amd64:9.8` |
For the last two derived builds, add `--platform linux/amd64`. The runner pins the
resolved image ID. It does not mount host directories, publish ports or expose
credentials. The containers run privileged only inside the dedicated test VM.
```bash
python3 sdp/test/containers/version_compatibility.py \
--source /absolute/path/to/verified-source-export \
--results /absolute/path/to/new-version-results \
--name-prefix sdp-version-unique-run --jobs 2
```
A single-cell development run can add `--release 2023.1 --platform rhel8`.
`--jobs` defaults to one and permits at most two independent cells. Each uses its
own fresh container and result directory. Incremental reports retain every
completed cell, and the final report always orders cells by release and platform,
regardless of completion order. A tooling failure remains a failed cell while
other cells finish.
Within one runner, container startup/readiness and shutdown are serialized while
provisioning and the test scenarios remain parallel. This avoids overlapping
privileged systemd lifecycle operations; it does not diagnose or guarantee a fix
for emulator startup failures. Coordinate other runners' container shutdowns and
starts separately when investigating such failures.
Systemd readiness has a 180-second deadline, longer than the images' normal
90-second unit-start timeout. Only `running` or `degraded` states are accepted;
`initializing` and other unfinished states fail at the deadline. A failed boot
retains container inspection, console, manager state, pending jobs and failed-unit
diagnostics before shutdown.
The final release gate still requires all fourteen cells. Do not merge cells from
different source exports. `version-compatibility.json` identifies the exact source
manifest; each subdirectory retains installation configuration, vendor provenance,
worker assertions, server logs and its `case.json`.
These fixtures establish specific release compatibility, not production-scale
throughput, every historical role combination, network filesystem semantics,
upgrade safety or behavior across an entire fleet. Review any failed or blocked
cell before expanding the supported claim.