name: Broker compatibility
on:
release:
types: [published]
workflow_dispatch:
inputs:
ref:
description: "Core tag or ref to probe against (defaults to the ref the run started from)"
required: false
type: string
permissions:
contents: read
concurrency:
group: broker-compat-${{ github.ref }}
cancel-in-progress: false
jobs:
probe:
name: ${{ matrix.broker }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
broker:
- ruststream-nats
- ruststream-fred
- ruststream-lapin
- ruststream-rdkafka
- ruststream-amqp
- ruststream-gcp-pubsub
- ruststream-sqs-sns
- ruststream-pulsar
- ruststream-rumqttc
- ruststream-zeromq
- ruststream-sea-file
- ruststream-kinesis
- ruststream-sqlx
steps:
- name: Check out the core at the probed ref
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref || github.ref }}
path: core
persist-credentials: false
- name: Check out the broker at its default branch
uses: actions/checkout@v7
with:
repository: powersemmi/${{ matrix.broker }}
path: broker
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get install -y --no-install-recommends protobuf-compiler
- uses: Swatinem/rust-cache@v2
with:
workspaces: broker
key: ${{ matrix.broker }}
- name: Point the broker at the released core
run: |
sed -i 's#^ruststream = { version = "[^"]*"#ruststream = { path = "../core"#' broker/Cargo.toml
grep '^ruststream = ' broker/Cargo.toml
- name: Build the broker against the core
id: check
continue-on-error: true
working-directory: broker
run: |
# Each check keeps its own pipeline, because a brace group feeding `tee` runs in a
# subshell: a failure recorded inside it never reaches the status this step exits with.
set -o pipefail
status=0
log="$RUNNER_TEMP/check.log"
: > "$log"
# The unpublished members (benchmarks, examples) are built with their own features: a
# benchmark refuses the `testing` feature on purpose, and every-feature builds are a
# property of the published crates, which is what the broker's own gates check too.
# A manifest cargo cannot read fails here; its error goes to the log the report quotes.
echo "\$ cargo metadata --no-deps" >> "$log"
if ! metadata="$(cargo metadata --no-deps --format-version 1 2>>"$log")"; then
cat "$log"
exit 1
fi
unpublished="$(jq -r '.packages[] | select(.publish == []) | .name' <<<"$metadata")"
exclude=""
for name in $unpublished; do exclude+=" --exclude $name"; done
echo "\$ cargo check --workspace$exclude --all-targets --all-features" | tee -a "$log"
cargo check --workspace $exclude --all-targets --all-features 2>&1 | tee -a "$log" \
|| status=1
for name in $unpublished; do
echo | tee -a "$log"
echo "\$ cargo check -p $name --all-targets" | tee -a "$log"
cargo check -p "$name" --all-targets 2>&1 | tee -a "$log" || status=1
done
echo | tee -a "$log"
echo "\$ cargo check --workspace --no-default-features" | tee -a "$log"
cargo check --workspace --no-default-features 2>&1 | tee -a "$log" || status=1
exit "$status"
- name: Report the verdict to the broker repository
env:
GH_TOKEN: ${{ secrets.BROKER_COMPAT_TOKEN }}
BROKER: ${{ matrix.broker }}
OUTCOME: ${{ steps.check.outcome }}
CORE_REF: ${{ inputs.ref || github.ref_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::warning::BROKER_COMPAT_TOKEN is not set; the verdict for $BROKER is only this run's status"
exit 0
fi
repo="powersemmi/$BROKER"
version="${CORE_REF#v}"
marker="against ruststream v$version"
# One open core-compat issue per repo is the whole protocol: filing opens it, the next
# broken release comments on it, the first green probe closes it.
existing="$(gh issue list --repo "$repo" --state open --search 'core-compat in:title' \
--json number --jq '.[0].number // empty')"
if [ "$OUTCOME" = "success" ]; then
if [ -n "$existing" ]; then
gh issue close "$existing" --repo "$repo" \
--comment "Builds $marker (checked by [this probe]($RUN_URL))."
fi
exit 0
fi
# The error section is what the maintainer needs; everything before the first error is
# build progress. GitHub caps an issue body at 65536 characters, the cap below leaves
# room for the prose around the log.
log="$(sed -n '/^error/,$p' "$RUNNER_TEMP/check.log" | head -c 50000)"
# A failure with no compiler error line (a network hiccup, a missing tool) still needs
# a report the maintainer can read.
[ -n "$log" ] || log="$(tail -c 20000 "$RUNNER_TEMP/check.log")"
body="$(printf '%s\n' \
"ruststream v$version was released and this crate does not build against it." \
"Checked by [this probe]($RUN_URL): the version requirement replaced by a path" \
"dependency on the released core, then \`cargo check --all-targets\` of the" \
"published crates with every feature and of the others with their own, and" \
"\`--no-default-features\`." \
"" \
"<details><summary>error log</summary>" \
"" \
'```text' \
"$log" \
'```' \
"" \
"</details>")"
if [ -n "$existing" ]; then
# A re-run of the same probe adds nothing; a new version failing on an open issue does.
if gh issue view "$existing" --repo "$repo" --json body,comments \
--jq '.body, .comments[].body' | grep -qF "$marker"; then
echo "already reported $marker in $repo#$existing"
exit 0
fi
comment="$(printf 'Still broken %s.\n\n%s' "$marker" "$body")"
gh issue comment "$existing" --repo "$repo" --body "$comment"
else
gh issue create --repo "$repo" \
--title "core-compat: broken against ruststream v$version" \
--body "$body"
fi
- name: Propagate the verdict
if: steps.check.outcome == 'failure'
run: exit 1