Read the ecosystem's own definition of the two test-outcome rules #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Go | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| branches: [master] | |
| jobs: | |
| # Everything that needs no database and no library: the formatting, and that | |
| # every build tag combination compiles and vets. No test runs here, because a | |
| # test collected without the database it reads can only skip, and a skip is a | |
| # test nobody has run reported inside a green job. | |
| build: | |
| name: Build and vet, every tag | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Formatting | |
| run: | | |
| unformatted=$(gofmt -l .) | |
| if [ -n "$unformatted" ]; then | |
| echo "gofmt reports:"; echo "$unformatted"; exit 1 | |
| fi | |
| # A file behind a build tag no job ever passes is invisible to every check | |
| # the repository has: it is not compiled, so it is not vetted, and it | |
| # carries no test a count could miss. Compiling each tag set here is what | |
| # keeps backend_duck.go and backend_spark.go from rotting; the meos tag | |
| # needs the library and is compiled in the job below, which builds it. | |
| - name: Vet and build, default tags | |
| run: go vet ./... && go build ./... | |
| - name: Vet and build, duckdb backend | |
| run: go vet -tags duckdb ./... && go build -tags duckdb ./... | |
| - name: Vet and build, spark backend | |
| run: go vet -tags spark ./... && go build -tags spark ./... | |
| # EVERY TEST THIS REPOSITORY CARRIES RUNS HERE, AND NONE OF THEM SKIPS. | |
| # A test skips when a precondition is missing from the job that collects it, | |
| # so this job supplies all three: the fixture database the Annex A data tests | |
| # read, the libmeos the streaming engine links, and the network the published | |
| # OGC schemas are fetched over. It then refuses a skip outright, so a | |
| # precondition that stops being supplied fails the job instead of decaying | |
| # into a green run that exercised nothing. | |
| # | |
| # THE DATABASE IS MOBILITYDB MASTER, BUILT HERE FROM SOURCE. A job pinned to a | |
| # release measures a MobilityDB the project no longer develops, so a defect fixed | |
| # upstream reads here as a live one and blocks work that is not blocked: deleting | |
| # a temporal value that spans a whole composing sequence segfaults the backend on | |
| # 1.3.0 and answers correctly on master. Naming a version somebody can install is | |
| # a reason to ALSO run a release, never a reason to develop against one. | |
| # | |
| # AND A PUBLISHED IMAGE IS NOT THE SOURCE. The mobilitydb/mobilitydb tags are | |
| # built by MobilityDB/MobilityDB-docker, a separate repository on its own schedule, | |
| # so what they carry is that repository's packaging rather than the source this | |
| # tier is written against: `18-3.6-master` fails to load the library on a missing | |
| # libgdal.so.39, and `17-3.5-master` exits its own entrypoint because the | |
| # pointcloud extension it asks for is not available. Pinning whichever tag happens | |
| # to work makes the packaging the source of truth and dates the measurement to | |
| # whenever that tag was pushed. The recipe below is MobilityDB's own | |
| # .github/workflows/pgversion.yml, read from the checkout under test. | |
| conformance: | |
| name: Every test, against MobilityDB master | |
| runs-on: ubuntu-24.04 | |
| env: | |
| PGVERSION: "18" | |
| POSTGISVERSION: "3" | |
| MFAPI_DSN: postgres://postgres:conformance@127.0.0.1:5432/mfapi?sslmode=disable | |
| # The vendored OGC schema bundle is compared against what OGC publishes. | |
| MFAPI_SCHEMA_FRESHNESS: "1" | |
| # The floor is every result line `go test -v` writes, subtests included: | |
| # 77 top-level tests and 64 subtests. Subtests belong in it because a | |
| # t.Run whose body skips is as unrun as its parent would be, and deleting | |
| # a case from a table-driven test moves nothing else. Raise it when the | |
| # suite grows. Lowering it belongs in the same commit as the removal it | |
| # accounts for, because deleting a test is what a skip refusal cannot see. | |
| MFAPI_TEST_FLOOR: "141" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check out MobilityDB master | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: MobilityDB/MobilityDB | |
| ref: master | |
| path: mobilitydb | |
| - name: Name the commit under test | |
| run: git -C mobilitydb log -1 --format='MobilityDB master %H %s' | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Remove the runner's PostgreSQL, so apt.postgresql.org answers | |
| run: | | |
| sudo service postgresql stop || true | |
| sudo apt-get --purge remove postgresql* -y || true | |
| sudo rm -rf /var/lib/postgresql/ /etc/postgresql/ /var/log/postgresql/ || true | |
| # MobilityDB's own apt hardening, taken from the checkout above rather than | |
| # copied: the runner image ships two package sources that answer 403 from the | |
| # Azure runners, which fails an apt-get update asking only for Ubuntu packages. | |
| - uses: ./mobilitydb/.github/actions/apt-resilient | |
| - name: Add the PostgreSQL APT repository | |
| run: | | |
| sudo apt-get install -y curl ca-certificates gnupg | |
| curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - | |
| codename=$(lsb_release -cs) | |
| echo "deb http://apt.postgresql.org/pub/repos/apt/ ${codename}-pgdg main ${PGVERSION}" \ | |
| | sudo tee /etc/apt/sources.list.d/pgdg.list | |
| - name: Install the build and server dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libgeos++-dev \ | |
| libproj-dev \ | |
| libgdal-dev \ | |
| libjson-c-dev \ | |
| postgresql-${PGVERSION} \ | |
| postgresql-${PGVERSION}-postgis-${POSTGISVERSION} \ | |
| postgresql-server-dev-${PGVERSION} | |
| # The default family set is what this tier reads: temporal geometry points | |
| # and the scalar temporal types. The optional families carry dependencies of | |
| # their own and no route here reaches one, so the build stays at the defaults | |
| # and the job measures the surface it uses. | |
| - name: Build and install MobilityDB | |
| run: | | |
| export PATH=/usr/lib/postgresql/${PGVERSION}/bin:$PATH | |
| mkdir mobilitydb/build | |
| cd mobilitydb/build | |
| cmake -DCMAKE_BUILD_TYPE=Release .. | |
| make -j "$(nproc)" | |
| sudo make install | |
| # The same source, configured as the standalone library the streaming | |
| # engine links. Building it from the checkout above is what makes the | |
| # engine tests measure the same MobilityDB the SQL routes measure; a | |
| # libmeos from anywhere else would be a second, unnamed commit. | |
| - name: Build and install libmeos from the same checkout | |
| run: | | |
| mkdir mobilitydb/build-meos | |
| cd mobilitydb/build-meos | |
| cmake -DMEOS=ON -DCMAKE_BUILD_TYPE=Release .. | |
| make -j "$(nproc)" | |
| sudo make install | |
| sudo ldconfig | |
| # LOADING THE EXTENSION IS THE ASSERTION THE PUBLISHED IMAGES FAILED. Doing | |
| # it in a step of its own, ahead of the fixture, is what makes a database that | |
| # cannot carry MobilityDB say so here rather than inside a fixture load. | |
| - name: Create the database and load the extension | |
| run: | | |
| sudo service postgresql start | |
| sudo -u postgres psql -v ON_ERROR_STOP=1 \ | |
| -c "ALTER USER postgres PASSWORD 'conformance'" | |
| sudo -u postgres createdb mfapi | |
| psql "$MFAPI_DSN" -v ON_ERROR_STOP=1 -q \ | |
| -c "CREATE EXTENSION mobilitydb CASCADE" | |
| psql "$MFAPI_DSN" -X -A -t -c "SELECT mobilitydbFullVersion()" | |
| - name: Load the conformance fixture | |
| run: psql "$MFAPI_DSN" -v ON_ERROR_STOP=1 -q -f tutorial/setup/load_conformance.sql | |
| # THE EXIT STATUS OF A PIPELINE IS ITS LAST STAGE'S. Without pipefail the | |
| # status read here is tee's, which is always 0, and a failing test reaches the | |
| # guard below as though it had passed. | |
| - name: The whole suite, with every precondition supplied | |
| run: | | |
| set -o pipefail | |
| go test -tags meos -v ./... 2>&1 | tee suite.log | |
| # A SKIP IS NOT A PASS, AND A DELETION IS NOT VISIBLE IN THE SKIP COUNT. | |
| # Two rules, because each is blind to what the other catches. The first | |
| # refuses a test that reported itself unrun; once that stands, deleting a | |
| # test is the remaining way to stop running it, and it leaves no skip | |
| # behind, so only the total moves. | |
| # | |
| # The rules live once, in MEOS-API beside the catalog, and every consumer | |
| # in the ecosystem reads that one definition rather than a copy of it: a | |
| # copy is what drifts, and a rule about coverage that drifts is worse than | |
| # none. The same script answers over a local log, so a by-hand answer and | |
| # the CI answer cannot differ. | |
| - name: Refuse a skip, and a suite that shrank | |
| uses: MobilityDB/MEOS-API/.github/actions/check-test-outcome@master | |
| with: | |
| log: suite.log | |
| min-tests: ${{ env.MFAPI_TEST_FLOOR }} | |
| # A SAMPLE THAT HAS DRIFTED IS WORSE THAN NO SAMPLE: it states what the | |
| # service used to answer, and a reader has no way to tell. Re-emitting here | |
| # and diffing against what is committed is what keeps samples/ the current | |
| # answer rather than a snapshot somebody forgot to refresh. | |
| # | |
| # The TemporalProperties document carries the time it was written, which the | |
| # standard defines it to carry, so that one line is normalised on both sides. | |
| - name: The committed samples are what the service writes | |
| run: | | |
| set -o pipefail | |
| go run . -emit samples.emitted | |
| norm() { sed -E 's/"timeStamp": "[^"]*"/"timeStamp": "<written>"/' "$1"; } | |
| rc=0 | |
| for f in samples/*; do | |
| b=$(basename "$f") | |
| if [ ! -f "samples.emitted/$b" ]; then | |
| echo "::error::samples/$b is no longer a resource the service serves"; rc=1; continue | |
| fi | |
| if ! diff -q <(norm "$f") <(norm "samples.emitted/$b") >/dev/null; then | |
| echo "::error::samples/$b is not what the service writes; re-run: go run . -emit samples" | |
| diff -u <(norm "$f") <(norm "samples.emitted/$b") | head -40 | |
| rc=1 | |
| fi | |
| done | |
| for f in samples.emitted/*; do | |
| b=$(basename "$f") | |
| if [ ! -f "samples/$b" ]; then | |
| echo "::error::the service serves a resource samples/ does not carry: $b"; rc=1 | |
| fi | |
| done | |
| if [ "$rc" -eq 0 ]; then | |
| echo "$(ls samples/*.json | wc -l) sample documents are what the service writes" | |
| fi | |
| exit $rc |