Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/release-native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ name: Release native (arcadedb)
# starts release-jvm.yml, which publishes `arcadedb-jvm`; the two workflows do not wait for each other.
#
# The build and the tests are test-native.yml, called with `release: true` (the full matrix: linux-x86_64, linux-aarch64,
# macos-arm64 and windows-x64, each on Python 3.11 to 3.14). A native image build takes about 5 to 30 minutes per platform; this workflow adds
# macos-arm64 and windows-x64, each on Python 3.11 to 3.14, and the `pyver` job: the linux-x86_64 wheel on Python 3.15 and on the
# free-threaded 3.14t). A native image build takes about 5 to 30 minutes per platform; this workflow adds
# no build of its own. The way the JVM release calls test-jvm.yml is the model.
#
# PyPI keys a trusted publisher on this FILENAME and on the job's environment, so do not rename either without changing the publisher
Expand All @@ -15,7 +16,8 @@ name: Release native (arcadedb)
# The GitHub environment `pypi` only accepts deployments from tags that match [0-9]*. There is one environment and one publish job for
# dev, final and post releases.
#
# What is published (the `verify` job decides, the `publish` job uploads):
# What is published (the `verify` job decides, the `publish` job uploads). Every kind of release also needs the two `pyver` legs to have
# passed (3.15 and 3.14t on the linux-x86_64 wheel; the other platforms are not tested on them):
# A.B.C.devN the parity run of linux-x86_64, linux-aarch64 and macos-arm64 passed on every Python. windows-x64 is published too when it
# passed on every Python, and is informational otherwise.
# A.B.C, A.B.C.postN ONLY when the readiness bar is met: the repository variable ARCADEDB_NATIVE_READINESS_BAR_MET is `true` (a maintainer
Expand Down Expand Up @@ -135,7 +137,7 @@ jobs:
path: wheels
merge-multiple: true

- name: Every required platform passed on every Python, and the wheels are the ones that were tested
- name: Every required platform passed on every Python, the pyver legs passed, and the wheels are the ones that were tested
id: verify
env:
VERSION: ${{ needs.validate-version.outputs.version }}
Expand All @@ -146,6 +148,8 @@ jobs:
mkdir -p passed dist
ls -l passed wheels
pythons="3.11 3.12 3.13 3.14"
# the `pyver` legs of a release (scripts/select_native_matrix.py; tests/test_select_native_matrix.py checks that this list is the same)
pyver_pythons="3.15 3.14t"
if [ "$FINAL" = true ]; then
required="linux-x86_64 linux-aarch64 macos-arm64 windows-x64"
if [ "$BAR" != true ]; then
Expand All @@ -160,6 +164,12 @@ jobs:
[ -f "passed/$key-py$py.ok" ] || { echo "::error::required leg $key on Python $py did not pass its parity run: nothing is published" >&2; exit 1; }
done
done
# every release needs the `pyver` legs too, and they must have installed the wheel of linux-x86_64 that is published
tested="$(cat "passed/linux-x86_64-py3.12.ok")"
for py in $pyver_pythons; do
[ -f "passed/pyver-py$py.ok" ] || { echo "::error::the pyver leg on Python $py did not pass: nothing is published" >&2; exit 1; }
[ "$(cat "passed/pyver-py$py.ok")" = "$tested" ] || { echo "::error::the pyver leg on Python $py tested another wheel than $tested: nothing is published" >&2; exit 1; }
done
# every platform that passed on every Python is published (Windows is informational for a pre-release)
for key in linux-x86_64 linux-aarch64 macos-arm64 windows-x64; do
all=true
Expand Down
28 changes: 23 additions & 5 deletions .github/workflows/test-native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
#
# Beside the full parity runs, the `pyver` job installs the already built linux-x86_64 wheel (py3-none: no rebuild) into the Pythons the full
# runs of the event leave out (a pull request: 3.11, 3.13, 3.14; main, nightly, dispatch: 3.11, 3.13; a release: none), into 3.15 (always: the full suite
# needs jpype1 and ladybug, which have no cp315 wheel yet, so 3.15 runs without the test files that need jpype1) and into the free-threaded 3.14t, and runs a small subset of the tests there (about 20 s of tests per Python). It is informational: release-native.yml does not need it.
# needs jpype1 and ladybug, which have no cp315 wheel yet, so 3.15 runs without the test files that need jpype1) and into the free-threaded 3.14t, and runs a small subset of the tests there (about 20 s of tests per Python). A release needs it: a leg that passes uploads a marker (native-passed-pyver-py<version>), and release-native.yml
# publishes nothing unless the legs of a release (3.15 and the free-threaded 3.14t) passed. Only linux-x86_64 is checked on them.
# scripts/select_native_matrix.py decides its legs.
#
# The `examples` job runs every script of examples/ against the installed wheel of each platform (Python 3.12 and 3.14; a pull request: linux-x86_64, 3.12), through
Expand All @@ -31,8 +32,9 @@
# test suite of this repository run against the INSTALLED wheel (native/wheel/parity_wheel.py): no failure, error, crash, timeout, or skip
# besides the counted NOTYET and NA-JVM kinds (tests/conftest.py).
#
# A leg that passes uploads a marker (native-passed-<platform>-py<version>) and the wheel (native-wheel-<platform>); release-native.yml
# reads them to decide what may be published. Do not rename these artifacts without changing release-native.yml.
# A leg that passes uploads a marker (native-passed-<platform>-py<version>) and the wheel (native-wheel-<platform>); a `pyver` leg that
# passes uploads a marker too (native-passed-pyver-py<version>). release-native.yml reads them to decide what may be published. Do not
# rename these artifacts without changing release-native.yml.
#
# Crashes (ArcadeData/arcadedb-python#295): a test file whose process dies without a result is a CRASH row with its exit code, and fails the leg.
# On the Windows legs only, the harness runs such a file once more in a fresh process (--retry-crash): a retry that produces a result is a
Expand Down Expand Up @@ -968,8 +970,9 @@ jobs:
# The cheap Python-version coverage of the wheel of linux-x86_64 (py3-none: the same file for every Python, nothing is rebuilt): a subset of
# the test files, not the whole suite. The legs are the supported Pythons the full parity runs of this event leave out, plus the
# free-threaded 3.14t (scripts/select_native_matrix.py).
# INFORMATIONAL for now: release-native.yml (verify and the publish jobs) reads only the native-passed-* markers and the native-wheel-* wheels,
# never an artifact of this job, so a red leg never blocks a release. Promote it once it has been green for a while.
# Part of the release gate (ArcadeData/arcadedb-python#349): a leg that passes uploads the marker native-passed-pyver-py<version> (the file name of
# the wheel it installed), and `verify` in release-native.yml requires the markers of the legs a release has (3.15 and 3.14t, see
# scripts/select_native_matrix.py), so a red leg stops the release. Only the linux-x86_64 wheel is checked on these Pythons.
# 3.14t runs without polars, and so without test_dataframe_ingest.py (it imports polars at module level): polars ships abi3 wheels only
# (polars and polars-runtime-32 have no cp314t wheel, and an abi3 wheel cannot be installed on a free-threaded build). The other
# dependencies of the subset (numpy, pandas, pyarrow, jpype1, requests) all have cp314t wheels. polars is an optional extra of the wheel.
Expand Down Expand Up @@ -1090,6 +1093,21 @@ jobs:
if (skipped + 0 != 0) { print "skipped tests that are neither NOTYET nor NA-JVM: " skipped > "/dev/stderr"; exit 1 }
}' "$tsv" | tee -a "$GITHUB_STEP_SUMMARY"

# Reached only when the gate passed: release-native.yml reads this marker (one per Python, holding the wheel file name).
- name: Mark this leg as passed
run: |
set -euo pipefail
mkdir -p "$T/passed"
basename "$(ls "$T"/wheel/*.whl)" > "$T/passed/pyver-py$PY.ok"
cat "$T/passed/pyver-py$PY.ok"

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: native-passed-pyver-py${{ matrix.python }}
path: ${{ runner.temp }}/passed/pyver-py${{ matrix.python }}.ok
if-no-files-found: error
retention-days: 30

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ !cancelled() }}
with:
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/development/ci.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The workflow `test-native.yml` builds the native image of the `arcadedb` package

* A pull request runs it only when native paths change: `+native/**+`, `+src/arcadedb/**+`, `+tests/**+`, `+examples/**+` (not `examples/README.md`), `pyproject.toml`, `setup.py`, `scripts/select_native_matrix.py`, `scripts/smoke_wheel.py`, `scripts/run_examples.sh`, `scripts/native_examples_skip.txt` or `test-native.yml` itself. Changes under `tests/` and `src/arcadedb/` are included because both backends share those files, so a shared test or source can break native parity without touching a native file; docs outside those paths do not run it. It then runs on `linux-x86_64` with Python 3.12 only.
* A push to `main` (only when a native path changed, same filter as a pull request), the nightly run (skipped when nothing changed in the last 25 hours, except on Mondays) and a manual run (dispatch) run all four platforms: `linux-x86_64`, `linux-aarch64`, `macos-arm64` and `windows-x64` (Python 3.12 and 3.14). A release (`release-native.yml` calls the workflow with `release: true`) runs the same four platforms on all four Python versions, 16 legs.
* The `pyver` job installs the already built `linux-x86_64` wheel (no rebuild) into the Pythons that the full parity runs of the event leave out (3.11, 3.13 and 3.14 on a pull request; 3.11 and 3.13 on a push to `main`, the nightly run and a dispatch; none on a release) into 3.15 and into the free-threaded 3.14t, and runs 13 test files there (14 where polars installs; 3.15 has no jpype1 wheel, so it leaves out the two files that need jpype1), about 20 seconds of tests per Python. It is informational: `release-native.yml` reads only the `native-passed-*` markers and the `native-wheel-*` wheels, so a red `pyver` leg never blocks a release. The 3.14t leg has no polars (no free-threaded wheel exists), so it skips `test_dataframe_ingest.py`. `scripts/select_native_matrix.py` decides its legs.
* The `pyver` job installs the already built `linux-x86_64` wheel (no rebuild) into the Pythons that the full parity runs of the event leave out (3.11, 3.13 and 3.14 on a pull request; 3.11 and 3.13 on a push to `main`, the nightly run and a dispatch; none on a release) into 3.15 and into the free-threaded 3.14t, and runs 13 test files there (14 where polars installs; 3.15 has no jpype1 wheel, so it leaves out the two files that need jpype1), about 20 seconds of tests per Python. A release needs it: a `pyver` leg that passes uploads the marker `native-passed-pyver-py<version>` (it holds the file name of the wheel the leg installed), and `Verify what may be published` in `release-native.yml` requires the markers of 3.15 and 3.14t, the two legs of a release, and that both tested the `linux-x86_64` wheel that is published. A red leg stops a release before anything reaches PyPI. Only `linux-x86_64` is checked on these two Pythons: the other platforms are not tested on 3.15 and 3.14t, and the JVM package has no such job. The 3.14t leg has no polars (no free-threaded wheel exists), so it skips `test_dataframe_ingest.py`. `scripts/select_native_matrix.py` decides its legs, and `tests/test_select_native_matrix.py` and `tests/test_release_native_verify.py` pin the gate.
* Every test file runs in its own process (`native/wheel/parity_wheel.py`). A process that dies without writing its result is a `CRASH` row in `parity-wheel.tsv` with its exit code, for example `CRASH(rc=0xC0000005,3221225477)`, and it fails the leg. On the Windows legs only (the crashes of issue 295 happen there), the harness runs such a file once more in a fresh process (`--retry-crash`). A retry that produces a result is a `FLAKY-CRASH` row: the gate lets the leg pass, the job summary lists the file with the first exit code, and on a release or a dispatch the `crash-report` job adds one comment to issue 295 (run URL, Python, wheel version, exit codes). A second crash, a failing test, a timeout and every crash on Linux or macOS stay failures. A process that wrote its result and then died in shutdown is a `DIRTY-EXIT` record (exit code, file, attempt) in the metadata, the job summary and the issue comment: it is not retried and does not fail the leg. Each Windows test process also writes a faulthandler trace, `parity-wheel-xml/<file>.attemptN.fault.txt`, into the parity artifact of the leg. Only a retry hides a crash, so treat a `FLAKY-CRASH` as an open bug: read the trace in the artifact before a release is announced.
* The `examples` job runs every script of `examples/` against the installed wheel of each platform (Python 3.12 and 3.14; a pull request: `linux-x86_64`, Python 3.12) through `scripts/run_examples.sh`, the runner that `test-python-examples.yml` uses too. `scripts/native_examples_skip.txt` lists, with a reason, the examples that need the JVM package. `Examples summary` is the one check for all its legs. These legs do not decide what a release publishes.
* A native image build takes about 5 to 30 minutes for each platform (macOS is the slowest). The organisation shares one runner limit with upstream ArcadeDB, so do not add runs lightly. Change the paths or the matrix of `test-native.yml` only for a reason that you can write down.
Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/development/release.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,14 @@ The version rule above holds for both packages. One tag starts both release work
`release-native.yml` has four jobs:

. `Validate tag` checks that the tag is a bare version and the next one (`scripts/check_version.py`).
. `test` calls `test-native.yml` with the full matrix: four platforms, each on Python 3.11 to 3.14. It builds the native image and the wheel on each platform and runs the test suite of this repository against the installed wheel.
. `test` calls `test-native.yml` with the full matrix: four platforms, each on Python 3.11 to 3.14. It builds the native image and the wheel on each platform and runs the test suite of this repository against the installed wheel. It also runs the `pyver` job: a subset of the tests against the `linux-x86_64` wheel on Python 3.15 and on the free-threaded 3.14t.
. `Verify what may be published` reads which legs passed, applies the readiness gate below and collects the wheels.
. `Publish to PyPI` uploads the wheels through the environment `pypi`. It refuses any wheel that is not the distribution `arcadedb`.

`arcadedb` has one trusted publisher on PyPI (`release-native.yml` with the environment `pypi`) for dev, final and post releases, and one publish job in the workflow. `arcadedb-jvm` has one trusted publisher (`release-jvm.yml` with the environment `pypi-jvm`). PyPI matches the workflow file name, so renaming either file means changing the publisher on PyPI first. There is no separate pre-release environment; the readiness gate below is what keeps a final release back.

* A native pre-release (`A.B.C.devN`) publishes when the builds on `linux-x86_64`, `linux-aarch64` and `macos-arm64` pass.
* Every native release, pre-release or final, also needs the two `pyver` legs (Python 3.15 and 3.14t on the `linux-x86_64` wheel) to pass. The other platforms are not tested on those two Pythons.
* A FINAL native release (`A.B.C` or `A.B.C.postN`) needs two more things:
** The repository variable `ARCADEDB_NATIVE_READINESS_BAR_MET` is `true`. A maintainer sets it after checking the readiness bar: the correctness fixes are merged; the full suite is green on every platform, Windows included, and on Python 3.11 to 3.14; the fault and concurrency probes pass; and the documented API raises no `NotOnNativeYet`.
** The parity run of every platform and every Python version passes with zero `NotOnNativeYet` skips.
Expand Down
2 changes: 1 addition & 1 deletion native/wheel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Intel macOS, Windows arm64 and musl-based Linux (Alpine) are not built and not s

- **JVM-only APIs do not exist**: `get_java_database()` and the other `get_java_*` calls, `start_jvm()`, `jvm_kwargs` and JVM options. There is no JVM. Use `arcadedb-jvm` if you need them.
- **A few API calls raise `NotOnNativeYet`** (a `NotImplementedError`) instead of misbehaving: `AsyncExecutor.transaction`, `find_nearest_approximate` over product quantization, `get_metadata` of a foreign index, and `get_stats`.
- Free-threaded Python (3.14t) runs a subset of the tests in CI; it is informational and does not gate a release.
- Python 3.15 and free-threaded Python (3.14t) run a subset of the tests in CI, on the `linux-x86_64` wheel only; a release needs both to pass. The other platforms are not tested on them.

The bindings' own tests run against the installed wheel (the parity run). What does not pass is the list above, or JVM-only by nature.

Expand Down
6 changes: 3 additions & 3 deletions scripts/select_native_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

Prints `build=<json>`, `test=<json>`, `pyver=<json>` and `examples=<json>` lines (GITHUB_OUTPUT format). Publishing is not decided here:
release-native.yml does it. `pyver` is the cheap Python-version job (see PYVER_NOTE): a subset of the tests against the built
linux-x86_64 wheel on the Pythons the full parity run of the event does not cover, and always on the free-threaded 3.14t. It never
gates a release. The GraalVM CE archives are pinned by sha256 per platform (GraalVM Community Edition only, never Oracle GraalVM).
linux-x86_64 wheel on the Pythons the full parity run of the event does not cover, and always on the free-threaded 3.14t. A release
needs its legs to pass (release-native.yml, `verify`). The GraalVM CE archives are pinned by sha256 per platform (GraalVM Community Edition only, never Oracle GraalVM).
"""

from __future__ import annotations
Expand Down Expand Up @@ -100,7 +100,7 @@
# full parity run of the event does not cover, and runs a subset of the test files there. 3.14t is the free-threaded build of 3.14:
# it is not a classifier of the wheel and has no polars (polars ships abi3 wheels only, which a free-threaded Python cannot install).
FREE_THREADED_PYTHONS = ["3.14t"]
PYVER_NOTE = "informational: release-native.yml does not read it"
PYVER_NOTE = "gates a release: release-native.yml requires a native-passed-pyver-py<version> marker for each leg of a release"

PULL_REQUEST_PLATFORMS = ["linux-x86_64"]
PULL_REQUEST_PYTHONS = ["3.12"]
Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"test_dev_engine_pin.py": "runs the bash code of a workflow step",
# runs the awk program of the parity gate step of test-native.yml, which is code for the bash of a runner
"test_parity_gate_awk.py": "runs the workflows' awk step code",
# runs the bash code of the verify step of release-native.yml on small passed/ and wheels/ directories (#349)
"test_release_native_verify.py": "runs the bash code of a workflow step",
}
if sys.platform == "win32":
collect_ignore.extend(WINDOWS_NOT_APPLICABLE)
Expand Down
Loading
Loading