Skip to content

Commit cf69ee6

Browse files
Merge branch 'amazon-mq-rabbitmq-server-15438'
2 parents 8851d94 + a80dc6f commit cf69ee6

5 files changed

Lines changed: 550 additions & 1 deletion

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env bash
2+
3+
# Validates docs/compatibility.json for well-formedness, correct structure,
4+
# newest-first ordering, and 1:1 correspondence with release tags.
5+
6+
set -o errexit
7+
set -o pipefail
8+
set -o nounset
9+
10+
if [[ -d ${GITHUB_WORKSPACE:-} ]]
11+
then
12+
repo_root="$GITHUB_WORKSPACE"
13+
else
14+
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
15+
fi
16+
readonly repo_root
17+
18+
declare -r compat_file="$repo_root/docs/compatibility.json"
19+
declare -i errors=0
20+
21+
if [[ ! -f $compat_file ]]
22+
then
23+
echo "Error: $compat_file not found" >&2
24+
exit 1
25+
fi
26+
27+
if ! jq . "$compat_file" > /dev/null 2>&1
28+
then
29+
echo "Error: $compat_file is not valid JSON" >&2
30+
exit 1
31+
fi
32+
33+
echo "Validating structure..."
34+
35+
declare -r interval_pattern='^\[(([0-9]+\.)+[0-9]+),(([0-9]+\.)+[0-9]+)\)$'
36+
37+
while IFS= read -r version
38+
do
39+
erlang=$(jq -r --arg v "$version" '.[$v].erlang // empty' "$compat_file")
40+
elixir=$(jq -r --arg v "$version" '.[$v].elixir // empty' "$compat_file")
41+
42+
if [[ -z $erlang ]]
43+
then
44+
echo "Error: $version is missing 'erlang' key" >&2
45+
(( ++errors ))
46+
elif [[ ! $erlang =~ $interval_pattern ]]
47+
then
48+
echo "Error: $version has invalid erlang range: $erlang" >&2
49+
(( ++errors ))
50+
fi
51+
52+
if [[ -z $elixir ]]
53+
then
54+
echo "Error: $version is missing 'elixir' key" >&2
55+
(( ++errors ))
56+
elif [[ ! $elixir =~ $interval_pattern ]]
57+
then
58+
echo "Error: $version has invalid elixir range: $elixir" >&2
59+
(( ++errors ))
60+
fi
61+
done < <(jq -r 'keys_unsorted[]' "$compat_file")
62+
63+
echo "Validating newest-first ordering..."
64+
65+
declare -a json_versions
66+
while IFS= read -r version
67+
do
68+
json_versions+=("$version")
69+
done < <(jq -r 'keys_unsorted[]' "$compat_file")
70+
71+
declare -a sorted_versions
72+
while IFS= read -r version
73+
do
74+
sorted_versions+=("$version")
75+
done < <(printf '%s\n' "${json_versions[@]}" | sort -V -r)
76+
77+
for (( i=0; i<${#json_versions[@]}; i++ ))
78+
do
79+
if [[ ${json_versions[$i]} != "${sorted_versions[$i]}" ]]
80+
then
81+
echo "Error: ordering mismatch at position $i: found ${json_versions[$i]}, expected ${sorted_versions[$i]}" >&2
82+
(( ++errors ))
83+
break
84+
fi
85+
done
86+
87+
echo "Validating against release tags..."
88+
89+
declare -a expected_versions
90+
while IFS= read -r tag
91+
do
92+
expected_versions+=("${tag#v}")
93+
done < <(git -C "$repo_root" tag -l 'v3.11.*' 'v3.12.*' 'v3.13.*' 'v4.*' \
94+
| grep -v -E '(beta|rc|alpha)' \
95+
| sort -V -r)
96+
97+
for version in "${expected_versions[@]}"
98+
do
99+
if ! jq -e --arg v "$version" 'has($v)' "$compat_file" > /dev/null 2>&1
100+
then
101+
echo "Error: release tag v$version has no entry in compatibility.json" >&2
102+
(( ++errors ))
103+
fi
104+
done
105+
106+
for version in "${json_versions[@]}"
107+
do
108+
if ! git -C "$repo_root" rev-parse "v$version" > /dev/null 2>&1
109+
then
110+
echo "Error: compatibility.json has entry $version with no matching release tag" >&2
111+
(( ++errors ))
112+
fi
113+
done
114+
115+
if (( errors > 0 ))
116+
then
117+
echo "Validation failed with $errors error(s)" >&2
118+
exit 1
119+
fi
120+
121+
echo "Validation passed (${#json_versions[@]} entries)"

.github/workflows/update-discussion-versions.yml renamed to .github/workflows/update-versions.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Update Discussion Template Versions
1+
name: Update Versions and Validate Compatibility
22

33
on:
44
schedule:
@@ -14,13 +14,16 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
17+
with:
18+
fetch-tags: true
1719
- uses: carvel-dev/setup-action@47ccf1e203f9789b83ad664384be98880639c3cf # v2.0.1
1820
with:
1921
token: ${{ secrets.GITHUB_TOKEN }}
2022
only: ytt
2123
- env:
2224
GH_TOKEN: ${{ github.token }}
2325
run: ${{ github.workspace }}/.github/scripts/update-versions.sh
26+
- run: ${{ github.workspace }}/.github/scripts/validate-compatibility.sh
2427
- uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
2528
with:
2629
token: ${{ secrets.GITHUB_TOKEN }}

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ from individual `deps/` component directories (see below).
8585
* `deps/rabbitmq_web_mqtt_examples`: MQTT-over-WebSockets examples (with a Web UI part)
8686
* `deps/rabbitmq_web_stomp`: STOMP-over-WebSockets
8787
* `deps/rabbitmq_web_stomp_examples`: STOMP-over-WebSockets examples (with a Web UI part)
88+
* `docs/compatibility.json`: machine-readable Erlang/Elixir compatibility matrix for all releases from 3.11.0 onwards. See `docs/COMPATIBILITY.md` for maintenance instructions
8889
* `scripts` contains shell scripts that drive the server and CLI tools
8990
* `packaging` contains *some* packaging-related code; release artifacts source can be found in [`rabbitmq/rabbitmq-packaging`](https://github.com/rabbitmq/rabbitmq-packaging)
9091
* `selenium` contains Selenium tests for the management UI and the OAuth 2 plugin
@@ -122,6 +123,9 @@ These dependencies are cloned by `gmake` during the build process:
122123

123124
RabbitMQ [targets Erlang `27.x`](https://www.rabbitmq.com/docs/which-erlang) and a reasonably [recent Elixir](https://github.com/elixir-lang/elixir/releases) (e.g. `1.18.x`, `1.19.x`).
124125

126+
Per-release Erlang and Elixir compatibility ranges in machine-readable format
127+
can be found in `docs/compatibility.json`.
128+
125129

126130
## GitHub Actions
127131

docs/COMPATIBILITY.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Maintaining `compatibility.json`
2+
3+
This document describes how `docs/compatibility.json` was built and how it
4+
should be maintained going forward.
5+
6+
## Purpose
7+
8+
`compatibility.json` is a machine-readable compatibility matrix that maps each
9+
RabbitMQ release version to its supported Erlang/OTP and Elixir version ranges.
10+
It uses mathematical interval notation familiar to Maven and NuGet users.
11+
12+
See [GitHub Discussion #15438](https://github.com/rabbitmq/rabbitmq-server/discussions/15438)
13+
for the original proposal.
14+
15+
## File Format
16+
17+
The file is a JSON object keyed by RabbitMQ version string, ordered
18+
newest-first. Each value contains `erlang` and `elixir` keys with version
19+
ranges in interval notation:
20+
21+
```json
22+
{
23+
"4.2.4": {
24+
"erlang": "[26.2,28.0)",
25+
"elixir": "[1.13.4,1.20.0)"
26+
}
27+
}
28+
```
29+
30+
`[` means inclusive, `)` means exclusive. For example, `[26.2,28.0)` means
31+
`26.2 <= version < 28.0`.
32+
33+
## Scope
34+
35+
The file covers RabbitMQ 3.11.0 onwards. Earlier releases are not included.
36+
37+
Only final releases are included. Betas, release candidates, and alphas are
38+
excluded.
39+
40+
## Data Sources
41+
42+
### Erlang Version Ranges
43+
44+
The Erlang minimum and maximum version for each RabbitMQ release come from the compatibility
45+
table in the RabbitMQ website repository at `docs/which-erlang.md`:
46+
47+
https://github.com/rabbitmq/rabbitmq-website/blob/main/docs/which-erlang.md
48+
49+
That file contains two HTML tables (supported and unsupported series) with
50+
columns for RabbitMQ versions, minimum required Erlang/OTP, and maximum
51+
supported Erlang/OTP.
52+
53+
To convert the HTML table values to interval notation:
54+
55+
- The minimum becomes the inclusive lower bound
56+
- The maximum `X.Y.x` becomes the exclusive upper bound `X.(Y+1)`
57+
- Example: min `26.2`, max `27.x` becomes `[26.2,28.0)`
58+
- Example: min `25.0`, max `26.2.x` becomes `[25.0,26.3)`
59+
- Example: min `25.0`, max `25.3.x` becomes `[25.0,25.4)`
60+
61+
Preserve the precision from the HTML table. If the table says `24.3.4.8`, use
62+
that exact value in the interval.
63+
64+
When a release exists in the git tags but is not explicitly listed in the HTML
65+
table, assign it the same Erlang range as its siblings in the same release
66+
series and compatibility group.
67+
68+
### Elixir Version Ranges
69+
70+
The Elixir range for each release comes from the `elixir` field in
71+
`deps/rabbitmq_cli/mix.exs` at the corresponding git tag.
72+
73+
To extract the Elixir constraint for a release:
74+
75+
```bash
76+
git show v4.2.4:deps/rabbitmq_cli/mix.exs | grep -oP 'elixir:\s*"\K[^"]+'
77+
```
78+
79+
This returns a string like `>= 1.13.4 and < 1.20.0`, which converts to
80+
interval notation as `[1.13.4,1.20.0)`.
81+
82+
To extract all Elixir constraints at once:
83+
84+
```bash
85+
for tag in $(git tag -l 'v3.11.*' 'v3.12.*' 'v3.13.*' 'v4.*' \
86+
| grep -v -E '(beta|rc|alpha)' | sort -V); do
87+
elixir=$(git show "$tag:deps/rabbitmq_cli/mix.exs" 2>/dev/null \
88+
| grep -oP 'elixir:\s*"\K[^"]+')
89+
echo "$tag: $elixir"
90+
done
91+
```
92+
93+
## Adding a New Release
94+
95+
When a new RabbitMQ version is released:
96+
97+
1. Determine the Erlang range from `docs/which-erlang.md` in the website
98+
repository, or from the release notes if the website has not been updated
99+
yet. If neither source has been updated, use the same Erlang range as the
100+
previous release in the same series, and update it later when the
101+
authoritative data is available
102+
2. Extract the Elixir constraint from `deps/rabbitmq_cli/mix.exs` at the new
103+
release tag
104+
3. Add the new entry at the top of the JSON object (newest-first ordering)
105+
4. Validate the file is well-formed: `jq . docs/compatibility.json > /dev/null`
106+
5. Verify the entry count matches the number of release tags:
107+
`jq 'keys | length' docs/compatibility.json`
108+
109+
## Relationship to Other Files
110+
111+
`docs/compatibility.json` is separate from `.github/versions.json`. The
112+
`versions.json` file lists RabbitMQ and Erlang versions for populating GitHub
113+
Discussions template dropdowns and is auto-updated by
114+
`.github/scripts/update-versions.sh`. It serves a different purpose and should
115+
not be combined with the compatibility matrix.

0 commit comments

Comments
 (0)