Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
21f1e30
chore(quest): claim quest/m1/api-diff.md
kixelated Oct 23, 2026
01af431
feat(ci): diff every published API against its last release on releas…
kixelated Oct 23, 2026
02aec67
fix(ci): document each side's crates in one build for the Rust API diff
kixelated Oct 23, 2026
6cabefa
Merge remote-tracking branch 'origin/main' into quest/m1/api-diff
kixelated Oct 23, 2026
1d68322
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
157522e
fix(js): reject a declaration name that stripInternal left undeclared
kixelated Oct 23, 2026
e6bfda6
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
39bc624
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
b47b09c
feat(js): commit each package's API report and diff them on release PRs
kixelated Oct 23, 2026
418efa4
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
5c64dc1
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
66979a8
fix(ci): report-only API diff only for this repository's main
kixelated Oct 23, 2026
a0e4c8f
Merge remote-tracking branch 'origin/main' into iterate/api-diff
kixelated Oct 23, 2026
5f758b3
fix(ci): strip FFI header comments with GCC, failing loud without it
kixelated Oct 23, 2026
7bf577b
Merge remote-tracking branch 'origin/main' into merge-6477
kixelated Oct 25, 2026
c9bcd0e
chore(js): regenerate API reports after merging main
kixelated Oct 25, 2026
b10b274
Merge remote-tracking branch 'origin/main' into merge-6477
kixelated Oct 25, 2026
eb9992c
chore(js): regenerate the qmux API report after merging main
kixelated Oct 25, 2026
3ba3575
fix(ci): treat a line added inside an existing API declaration as a b…
kixelated Oct 25, 2026
9f6dc09
fix(ci): stop the Rust API diff when a metadata query fails
kixelated Oct 25, 2026
29544f8
Merge remote-tracking branch 'origin/main' into merge-6477
kixelated Oct 25, 2026
00b615f
chore(js): regenerate the hang API report after merging main
kixelated Oct 25, 2026
2147e4e
fix(js): include unexported referenced types in the API reports
kixelated Oct 25, 2026
e5f168d
Merge remote-tracking branch 'origin/main' into quest/m1/api-diff
kixelated Oct 25, 2026
8c79596
chore(js): regenerate API reports after merging main
kixelated Oct 25, 2026
6770f61
fix(ci): select api-diff on its own workflow and fence report diffs w…
kixelated Oct 25, 2026
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
54 changes: 54 additions & 0 deletions .github/workflows/api-diff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: API diff

# Diffs every published API against its last release on PRs into `release`.
# A backport fails on any break, since it must keep the published API. The cut
# (a PR from `main`) only reports, for the migration audit: release-plz bumps
# the versions after it lands, so its breaks are expected.
permissions:
contents: read

on:
workflow_call:

concurrency:
group: api-diff-${{ github.ref }}
cancel-in-progress: true

jobs:
diff:
name: Public API
runs-on: ubuntu-24.04-arm
# cargo-semver-checks documents every published crate twice, baseline and current.
timeout-minutes: 120
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/nix
- uses: ./.github/actions/rust-cache
with:
access-key-id: ${{ secrets.MBX_R2_ACCESS_KEY_ID }}
secret-access-key: ${{ secrets.MBX_R2_SECRET_ACCESS_KEY }}
# The JS baseline is the API reports committed on `release`.
- name: Fetch release
run: git fetch --no-tags --depth=1 origin +refs/heads/release:refs/remotes/origin/release
- name: Diff against the last release
id: diff
run: |
mode=(--deny)
# Only this repository's own `main` is the cut; a fork's `main` is a backport.
[[ "$HEAD_REF" != main || "$HEAD_REPO" != "$GITHUB_REPOSITORY" ]] || mode=()
nix develop --command just api-diff "${mode[@]}"
env:
HEAD_REF: ${{ github.head_ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
# Lists and downloads the moq-c and moq-ffi release assets.
GH_TOKEN: ${{ github.token }}
- name: Keep the report
if: ${{ !cancelled() && steps.diff.outcome != 'skipped' }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: api-diff
path: target/api-diff/
retention-days: 30
if-no-files-found: error
23 changes: 23 additions & 0 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
timeout-minutes: 10
outputs:
android: ${{ steps.filter.outputs.android }}
api-diff: ${{ steps.filter.outputs.api-diff }}
audio-quality: ${{ steps.filter.outputs.audio-quality }}
cpp: ${{ steps.filter.outputs.cpp }}
interop: ${{ steps.filter.outputs.interop }}
Expand Down Expand Up @@ -82,6 +83,20 @@ jobs:
- "Cargo.lock"
- "rust-toolchain.toml"
- ".github/workflows/android.yml"
# Anything a published API is built from, including the lockfiles,
# since a bumped dependency can change a re-exported type.
api-diff:
- "rs/**"
- "js/**"
- "Cargo.toml"
- "Cargo.lock"
- "package.json"
- "bun.lock"
- "cpp/moq/uniffi.toml"
- "sh/api-diff/**"
- "justfile"
- ".github/workflows/api-diff.yml"
- ".github/workflows/release-gate.yml"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
audio-quality:
- "js/watch/**"
# The container consumer decides what a replay delivers, waits for, and skips, on the
Expand Down Expand Up @@ -318,6 +333,13 @@ jobs:
if: needs.scope.outputs.android == 'true'
uses: ./.github/workflows/android.yml

api-diff:
name: API diff
needs: scope
if: needs.scope.outputs.api-diff == 'true'
uses: ./.github/workflows/api-diff.yml
secrets: inherit

audio-quality:
name: Audio quality
needs: scope
Expand Down Expand Up @@ -469,6 +491,7 @@ jobs:
- platform
- features
- android
- api-diff
- audio-quality
- cpp
- interop
Expand Down
3 changes: 3 additions & 0 deletions .remarkignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pkg/
result
**/CHANGELOG.md

# API Extractor reports, generated and checked by each package's build.
js/*/api/*.api.md

# IETF drafts are kramdown-rfc markdown, not CommonMark; linted by `just
# drafts check` (kramdown-rfc) instead. doc/draft/ is generated from them by
# doc/.vitepress/drafts.ts.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ For an advisory against Quinn, follow the advisory triage in [`rs/moq-quic/READM

- A release is cut by hand: a PR merging `main` into `release`, with a merge commit.
- A breaking cut first writes, on `main`, each changed package's version-transition section in its `MIGRATE.md` (`## 0.3 -> 0.4`) from the migration audit: the `!` subjects since the `release` merge base, each of those PRs' Impact section, and the API diff.
- Suggest a `release` backport for any serious bug fix (crash, security, data loss, broken interop), whether found on `main` or reported against a release. Land it on `main` first, then port it onto `release` as a separate PR that keeps the published API, so it ships as a patch.
- Suggest a `release` backport for any serious bug fix (crash, security, data loss, broken interop), whether found on `main` or reported against a release. Land it on `main` first, then port it onto `release` as a separate PR that keeps the published API, so it ships as a patch. The Release gate's API diff (`just api-diff --deny`) fails a backport that changes one; on the cut PR, whose head is `main`, it only reports.
- After any release publishes, patch or breaking, run `just test wire-compat --remeasure` on `main` and land the rewritten `test/interop/compat/planned-breaks.json` with refreshed reasons. Otherwise the nightly wire-compat check fails on the stale pins.
- Nothing merges `release` into `main` automatically. Before cutting a minor or breaking release, the maintainer merges `release` into `main` once, so trunk carries the published versions, CHANGELOGs, and any `release`-only commits. It lands as a merge commit with `--admin`, never squashed, so the cut starts from an advanced merge base.

Expand Down
Loading
Loading