From d6edc64190838db624b391e886247486ad2f2366 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 9 Jun 2026 12:00:15 -0600 Subject: [PATCH 1/2] GHA check for validating rust hash is a released version update rust hash --- .github/workflows/ci.yml | 13 +++++ .github/workflows/rust-release-check.yml | 61 ++++++++++++++++++++++++ AGENTS.md | 6 +++ client-sdk-rust | 2 +- 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/rust-release-check.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1deadcac..06293c3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: tests: ${{ steps.filter.outputs.tests }} docs: ${{ steps.filter.outputs.docs }} cpp_checks: ${{ steps.filter.outputs.cpp_checks }} + rust_submodule: ${{ steps.filter.outputs.rust_submodule }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 @@ -83,6 +84,8 @@ jobs: - .clang-tidy - .github/workflows/ci.yml - .github/workflows/cpp-checks.yml + rust_submodule: + - client-sdk-rust builds: name: Builds @@ -116,6 +119,15 @@ jobs: if: ${{ needs.changes.outputs.docs == 'true' || github.event_name == 'workflow_dispatch' }} uses: ./.github/workflows/generate-docs.yml + # Only runs on a client-sdk-rust submodule bump. Runs in parallel and is not a + # dependency of builds/tests, so developer iteration against an unreleased + # Rust commit still gets full build feedback in CI. + rust-release-check: + name: Rust Release Check + needs: changes + if: ${{ needs.changes.outputs.rust_submodule == 'true' || github.event_name == 'workflow_dispatch' }} + uses: ./.github/workflows/rust-release-check.yml + # Context: GitHub repository rulesets "required checks" actually need a job name to list. # This job is an aggregate job ci: @@ -129,6 +141,7 @@ jobs: - license-check - cpp-checks - generate-docs + - rust-release-check if: always() steps: - name: Verify required CI jobs diff --git a/.github/workflows/rust-release-check.yml b/.github/workflows/rust-release-check.yml new file mode 100644 index 00000000..9e586ce3 --- /dev/null +++ b/.github/workflows/rust-release-check.yml @@ -0,0 +1,61 @@ +name: Rust Release Check + +# Called by top-level ci.yml. Verifies that the pinned client-sdk-rust +# submodule commit corresponds to a published release in the Rust SDK repo, +# so the C++ SDK is never released on a stray (unreleased) Rust commit. +on: + workflow_call: {} + workflow_dispatch: {} + +permissions: + contents: read + +jobs: + rust-release-check: + name: Rust Release Check + runs-on: ubuntu-latest + steps: + - name: Checkout (with submodules) + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + submodules: recursive + fetch-depth: 1 + + - name: Resolve Rust submodule SHA + id: rust_sha + shell: bash + run: echo "sha=$(git -C client-sdk-rust rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Verify Rust commit has an associated release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RUST_REPO: livekit/client-sdk-rust + RUST_SHA: ${{ steps.rust_sha.outputs.sha }} + run: | + set -euo pipefail + echo "Pinned client-sdk-rust commit: ${RUST_SHA}" + + # Published (non-draft) releases -> their tag names. + gh api --paginate "repos/${RUST_REPO}/releases" \ + --jq '.[] | select(.draft == false) | .tag_name' \ + | sort -u > release_tags.txt + + # All tags -> "tagcommit_sha"; .commit.sha is the dereferenced + # target commit, so annotated and lightweight tags are handled alike. + gh api --paginate "repos/${RUST_REPO}/tags" \ + --jq '.[] | [.name, .commit.sha] | @tsv' > all_tags.tsv + + # A match is a tag that points at the pinned commit AND is released. + match="$(awk -v sha="${RUST_SHA}" ' + NR == FNR { released[$0] = 1; next } + ($2 == sha) && ($1 in released) { print $1 } + ' release_tags.txt all_tags.tsv | head -n 1)" + + if [[ -z "${match}" ]]; then + echo "::error::client-sdk-rust commit ${RUST_SHA} is not associated with any published release in ${RUST_REPO}." + echo "Bump the submodule to a released commit before merging; the C++ SDK must not release on a stray Rust commit." + exit 1 + fi + + echo "client-sdk-rust commit ${RUST_SHA} is covered by release tag '${match}'." diff --git a/AGENTS.md b/AGENTS.md index 6d9a0d44..64107fd0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -390,6 +390,12 @@ all filtered stages; normal pull requests and pushes use the path filters. - `.github/workflows/cpp-checks.yml` — Reusable `clang-format` and `clang-tidy` checks. - `.github/workflows/generate-docs.yml` — Reusable Doxygen docs validation. +- `.github/workflows/rust-release-check.yml` — Reusable check that the pinned + `client-sdk-rust` submodule commit maps to a published release. Gated by the + `rust_submodule` path filter so it only runs on a submodule bump, runs in + parallel, and is intentionally not a dependency of `builds`/`tests` so + developer iteration against an unreleased Rust commit still gets build + feedback. - `.github/workflows/license_check.yml` — Cheap license check, run on every CI invocation. - `.github/workflows/docker-images.yml` — Docker image build/publish workflow, diff --git a/client-sdk-rust b/client-sdk-rust index a0c91f5a..175cf276 160000 --- a/client-sdk-rust +++ b/client-sdk-rust @@ -1 +1 @@ -Subproject commit a0c91f5ae2309f3911c4a5d5843f385e6f20846a +Subproject commit 175cf276a8aa6770dbc795404fa91dc55dd27f10 From 6cbaba3da8c18c8fe396eabbf548998f43606ba9 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 9 Jun 2026 19:33:52 -0600 Subject: [PATCH 2/2] rust_submodule -> rust_release_check --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06293c3d..6831a9f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: tests: ${{ steps.filter.outputs.tests }} docs: ${{ steps.filter.outputs.docs }} cpp_checks: ${{ steps.filter.outputs.cpp_checks }} - rust_submodule: ${{ steps.filter.outputs.rust_submodule }} + rust_release_check: ${{ steps.filter.outputs.rust_release_check }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 @@ -84,7 +84,7 @@ jobs: - .clang-tidy - .github/workflows/ci.yml - .github/workflows/cpp-checks.yml - rust_submodule: + rust_release_check: - client-sdk-rust builds: @@ -125,7 +125,7 @@ jobs: rust-release-check: name: Rust Release Check needs: changes - if: ${{ needs.changes.outputs.rust_submodule == 'true' || github.event_name == 'workflow_dispatch' }} + if: ${{ needs.changes.outputs.rust_release_check == 'true' || github.event_name == 'workflow_dispatch' }} uses: ./.github/workflows/rust-release-check.yml # Context: GitHub repository rulesets "required checks" actually need a job name to list.