diff --git a/.github/justfile b/.github/justfile index fbcffcf242..1bccfc5338 100644 --- a/.github/justfile +++ b/.github/justfile @@ -34,10 +34,6 @@ formula *args: install-formula *args: python3 sh/gh/install-formula.py "$@" -# Open the pull request that merges `release` back into `main`; Check's `land` job merges it. -back-merge: - sh/gh/back-merge.sh - # Trigger the apt-repo and rpm-repo workflows for a release tag. [positional-arguments] trigger-repo-publish *args: diff --git a/.github/workflows/alert.yml b/.github/workflows/alert.yml index b71353258b..2776df94b7 100644 --- a/.github/workflows/alert.yml +++ b/.github/workflows/alert.yml @@ -28,7 +28,6 @@ on: # request. workflows: - apt-repo - - Back-merge - Cache - Cachix - Check diff --git a/.github/workflows/back-merge.yml b/.github/workflows/back-merge.yml deleted file mode 100644 index 745559be66..0000000000 --- a/.github/workflows/back-merge.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Back-merge - -# Every push to `release` (a cut, a backport, or release-plz's version bump) -# merges `release` back into `main`, so trunk carries the published versions -# and CHANGELOGs and the next cut starts from an advanced merge base. -# -# It lands through a pull request as a merge commit, never a squash: a squash -# leaves the merge base at the last cut, so a second publish before the next -# cut would conflict. Check's `land` job merges it once Check and Test pass. - -on: - push: - branches: [release] - # Re-running by hand, e.g. after resolving a conflict that failed a run. - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: back-merge - cancel-in-progress: false - -jobs: - back-merge: - name: Back-merge - runs-on: ubuntu-latest - if: github.repository_owner == 'moq-dev' - - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: ./.github/actions/just - - # The app token, not GITHUB_TOKEN: a pull request GITHUB_TOKEN opens - # triggers no workflows, so Check and Test would never report. - - name: Generate moq-bot token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 - id: token - with: - client-id: ${{ secrets.APP_CLIENT_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - permission-contents: write - permission-pull-requests: write - - - name: Merge release into main - env: - GH_TOKEN: ${{ steps.token.outputs.token }} - run: just gh back-merge diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9bce210c10..cd86881e1b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -129,38 +129,3 @@ jobs: CI_BASE: ${{ github.event.merge_group.base_sha }} MOQ_STRICT: 1 NEXTEST_PROFILE: ci - - # The release back-merge (sh/gh/back-merge.sh) must land as a merge commit, but - # the merge queue only squashes. Once Check and Test pass on its head, moq-bot - # merges it with --admin; the main ruleset lets moq-bot bypass only when - # merging a pull request. Gated on this repository's own branch, since a fork - # branch of the same name gets no secrets and must not land. --match-head-commit - # pins the merge to the commit the jobs above checked. - land: - name: Land back-merge - needs: [check, test] - if: >- - github.event_name == 'pull_request' && - github.event.action != 'closed' && - github.event.pull_request.head.ref == 'merge/release-into-main' && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.base.ref == 'main' - runs-on: ubuntu-latest - permissions: {} - - steps: - - name: Generate moq-bot token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 - id: token - with: - client-id: ${{ secrets.APP_CLIENT_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - permission-contents: write - permission-pull-requests: write - - - name: Merge - env: - GH_TOKEN: ${{ steps.token.outputs.token }} - PR_URL: ${{ github.event.pull_request.html_url }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - run: gh pr merge --merge --admin --match-head-commit "$HEAD_SHA" "$PR_URL" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00834b7250..beb2c78f83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Commits PRs into `main` are squash-merged through the merge queue, so the PR title becomes the commit subject and the PR description becomes the body in `git log`. -The one exception is the release back-merge, which lands as a merge commit (see [Merge queue](#merge-queue)). +The one exception is the maintainer's pre-release merge of `release` into `main`, which lands as a merge commit (see [Releases](#releases)). PRs into `release` use a merge commit, so their history survives until they land. - Use conventional-commit subjects (`feat(watch): ...`, `fix: ...`, `chore: ...`, `docs: ...`) @@ -34,10 +34,7 @@ Enqueue a reviewed PR with `gh pr merge `. A dequeued PR means the combination failed checks, timed out, or no longer meets the ruleset. Read the removal reason in the PR timeline and the merge group run, fix the cause, and enqueue again. -Never bypass the queue with `--admin`, with one exception: the queue only squashes, so Check's `land` job has moq-bot merge the release back-merge with `--admin` as a merge commit once Check and Test pass on its head. -moq-bot can bypass the queue only when merging a pull request, never on a direct push. -The `land` job fires only for this repository's `merge/release-into-main` branch, so anyone with write access who pushes to that branch gets a passing head merged past the queue, unreviewed. -Never enqueue the back-merge, since the queue would squash it; if `land` fails, re-run it from the PR's Check run. +Never bypass the queue with `--admin`, with one exception: the maintainer's pre-release merge of `release` into `main`, since the queue would squash it. # AI @@ -86,7 +83,7 @@ For an advisory against noq or Quinn, compare the pinned release's `PARENT` with - A release is cut by hand: a PR merging `main` into `release`, with a merge commit. - Consider a backport for any critical bug fix (crash, security, data loss, broken interop): land it on `main` first, then cherry-pick it onto `release` as a separate PR. -- After every push to `release`, the Back-merge workflow opens a PR merging `release` into `main`, so trunk carries the published versions and CHANGELOGs. It lands as a merge commit, outside the merge queue; never squash it, or the next back-merge conflicts. +- 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. # Versions diff --git a/quest/m0/broadcast-epoch/README.md b/quest/m0/broadcast-epoch/README.md index 5e401842cf..c87b53e6f0 100644 --- a/quest/m0/broadcast-epoch/README.md +++ b/quest/m0/broadcast-epoch/README.md @@ -91,9 +91,9 @@ republish against a live relay. When the release cut carries stats epochs, drop [#4810](https://github.com/moq-dev/moq/pull/4810)'s wall-clock group seed and -its `doc/concept/stats.md` sentence from `release`; release-to-main -back-merges keep `main`'s `rs/moq-stats` and `doc/concept/stats.md` until -then. MoQ Pro's VOD `storage.json` moves from the same seed to its own epoch. +its `doc/concept/stats.md` sentence from `release`; until then, the +maintainer's pre-release merge of `release` into `main` keeps `main`'s +`rs/moq-stats` and `doc/concept/stats.md`. MoQ Pro's VOD `storage.json` moves from the same seed to its own epoch. ## Required diff --git a/quest/m1/README.md b/quest/m1/README.md index 3049a6b040..e9afc4be13 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -48,8 +48,7 @@ ladders. - [Cluster routing](/quest/m1/cluster-routing/README.md) - any node routes toward a broadcast's origin over CDN and P2P links alike, with per-origin routes and path-less announces - [Track tail interop](/quest/m1/track-tail-interop.md) - a Rust publisher ending a track with a group in flight is read to its end by the JS subscriber, and the reverse, in `just test interop` - [Flat questlines](/quest/m1/quest-flat-lines.md) - moq pins the current quest CLI, lands its questline branches on main, and retires them -- [moq-bot may push workflow changes](/quest/m1/bot-workflows-permission.md) - condition: the maintainer grants moq-bot's GitHub App the `workflows` permission, so back-merges carrying workflow changes go through -- [main merges through a squash queue](/quest/m1/merge-queue-settings.md) - condition: once `release` carries the new back-merge script, the maintainer enables the squash merge queue and moq-bot's pull_request bypass together +- [main merges through a squash queue](/quest/m1/merge-queue-settings.md) - condition: the maintainer enables the squash merge queue on `main` - [Binding audio delay](/quest/m1/binding-surface.md) - moq-ffi and every wrapper configure and observe audio playout delay - [Per-frame arrivals in @moq/watch](/quest/m1/watch-arrivals.md) - the audio and video decoders expose a window of recent frame arrivals, late and skipped marks included, as a signal - [Bump web-transport-iroh for the capsule close](/quest/m1/iroh-capsule-bump.md) - condition: moq-dev/web-transport#419 ships in a release, then moq's iroh HTTP/3 client reports a peer's close capsule diff --git a/quest/m1/bot-workflows-permission.md b/quest/m1/bot-workflows-permission.md deleted file mode 100644 index c06268de9a..0000000000 --- a/quest/m1/bot-workflows-permission.md +++ /dev/null @@ -1,22 +0,0 @@ -# [XS] moq-bot may push workflow changes - -## Goal - -Condition: the maintainer grants moq-bot's GitHub App the `workflows` -permission, so the Back-merge workflow (`.github/workflows/back-merge.yml`, -which pushes with a moq-bot app token) can push a `release` into `main` -merge that carries changes under `.github/workflows/`. Without it, GitHub -refuses that push and the back-merge stalls until someone merges by hand. - -Check: `gh api orgs/moq-dev/installations --jq '.installations[] | -select(.app_slug == "moq-bot") | .permissions.workflows'` prints `write` -(it prints `null` as of 2026-10-08). Advance it by asking the maintainer to -grant it (Settings > GitHub Apps > moq-bot > permissions). - -Once it holds, the token step in `back-merge.yml` must also ask for it -(`permission-workflows: write` beside `permission-contents` and -`permission-pull-requests`), since an app token carries only the -permissions it requests. Make that one-line change in the PR that deletes -this quest. - -Planned as a follow-up of the 2026-10-05 audit. diff --git a/quest/m1/merge-queue-settings.md b/quest/m1/merge-queue-settings.md index b80f2452ad..3b938e1f10 100644 --- a/quest/m1/merge-queue-settings.md +++ b/quest/m1/merge-queue-settings.md @@ -2,50 +2,24 @@ ## Goal -Condition: once `release` carries #4619's `sh/gh/back-merge.sh`, the -maintainer switches `main` to the merge queue in ruleset 2420853, making both -changes at once: - -- Enable "Require merge queue" with method Squash, keeping Check and Test as - required checks. -- Add moq-bot as a bypass actor with mode `pull_request`, so the `land` job in - `.github/workflows/check.yml` can merge the release back-merge as a merge - commit outside the queue. - -Order matters. Until `release` carries the new script, a push to `release` -runs the old copy, which enables auto-merge on the back-merge PR; under a -squash queue that squashes it. With the queue on but no bypass, `land`'s merge -is refused. - -Check: `git show origin/release:sh/gh/back-merge.sh` no longer runs -`gh pr merge --auto`, and -`gh api repos/moq-dev/moq/rulesets/2420853` lists a `merge_queue` rule with -merge method `SQUASH` and a moq-bot bypass actor with `bypass_mode` -`pull_request`. Delete this quest once both hold and the Dependabot check -below is done. - -Advance it by backporting #4619's `sh/gh/back-merge.sh` to `release` (a -backport PR, per `CONTRIBUTING.md`); as of 2026-10-08 `release` still runs -`gh pr merge --auto`, and the ruleset has no `merge_queue` rule. Then ask the +Condition: the maintainer enables "Require merge queue" with method Squash on +`main` in ruleset 2420853, keeping Check and Test as required checks. No bot +bypass is needed: nothing merges `release` into `main` automatically, and the +maintainer lands the pre-release `release` into `main` merge with their own +admin bypass. + +Check: `gh api repos/moq-dev/moq/rulesets/2420853` lists a `merge_queue` rule +with merge method `SQUASH` (it has none as of 2026-10-09). Delete this quest +once it holds and the Dependabot check below is done. Advance it by asking the maintainer to flip the ruleset. ## Plan -Decided in #4619 (2026-10-05): a squash queue with a pull_request-only bot -bypass for the back-merge, landed by the `land` job; the back-merge may land -against a `main` that moved during its CI run, accepted. - -`CONTRIBUTING.md`'s Merge queue section and the `land` job's comment in -`check.yml` already describe the queue as live. They become true when this -clears; if the plan changes instead, fix them in the same PR. - -Check once both settings are on: `.github/workflows/dependabot.yml` merges -with the same moq-bot app token through `gh pr merge --auto --squash`, without -`--admin`, so its merges may still enqueue rather than bypass. On the next -Dependabot PR, see whether it went through the queue or skipped it as a -bypass actor, and record the result in the PR that deletes this quest. If it -skipped the queue, ask the maintainer whether that is acceptable. - -## Related +`CONTRIBUTING.md`'s Commits and Merge queue sections already describe the +queue as live. They become true when this clears; if the plan changes +instead, fix them in the same PR. -- [moq-bot may push workflow changes](/quest/m1/bot-workflows-permission.md) - the other moq-bot permission the back-merge needs +Check once the queue is on: `.github/workflows/dependabot.yml` merges with the +moq-bot app token through `gh pr merge --auto --squash`. On the next +Dependabot PR, confirm it went through the queue, and record the result in the +PR that deletes this quest. diff --git a/sh/gh/back-merge.sh b/sh/gh/back-merge.sh deleted file mode 100755 index 2a7142b42f..0000000000 --- a/sh/gh/back-merge.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# -# Open (or refresh) the pull request that merges `release` back into `main`. -# Check's `land` job merges it as a merge commit once Check and Test pass on its -# head, bypassing the squash-only merge queue. -# -# The pull request's head is its own branch rather than `release` itself: the -# repository deletes head branches on merge, and a conflict is resolved by -# merging `main` into that branch, which must never touch `release`. -# -# Required env: -# GH_TOKEN A token whose pull requests trigger CI (not GITHUB_TOKEN). -# GITHUB_REPOSITORY owner/repo, set by GitHub Actions. - -set -euo pipefail - -repo="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY must be set}" -branch=merge/release-into-main - -ahead=$(gh api "repos/$repo/compare/main...release" --jq .ahead_by) -if [[ "$ahead" == 0 ]]; then - echo "main already contains release" - exit 0 -fi - -sha=$(gh api "repos/$repo/git/ref/heads/release" --jq .object.sha) - -# --exit-code: 2 means the branch is absent; anything else nonzero is a failure. -status=0 -git ls-remote --exit-code --heads "https://github.com/$repo" "$branch" >/dev/null || status=$? -case "$status" in - 0) - # An open pull request may carry a hand-resolved conflict, so merge into the - # branch instead of resetting it. A conflict fails here, loudly. - gh api -X POST "repos/$repo/merges" -f base="$branch" -f head="$sha" \ - -f commit_message="Merge release into $branch" >/dev/null - ;; - 2) - gh api -X POST "repos/$repo/git/refs" -f ref="refs/heads/$branch" -f sha="$sha" >/dev/null - ;; - *) - echo "error: cannot query $branch on $repo" >&2 - exit 1 - ;; -esac - -# Only a pull request from this repository's own branch: `gh pr list --head` -# matches the branch name alone, so a fork's branch of the same name would pass. -pr=$(gh api "repos/$repo/pulls?state=open&base=main&head=${repo%%/*}:$branch" \ - --jq "[.[] | select(.head.repo.full_name == \"$repo\")][0].number // empty") -if [[ -z "$pr" ]]; then - gh pr create --repo "$repo" --head "$branch" --base main \ - --title "chore: merge release into main" \ - --body "Carries what \`release\` published (versions, CHANGELOGs, backports) back to trunk. Opened by the Back-merge workflow. - -Check's \`land\` job merges it as a merge commit once Check and Test pass. **Never squash.** A squash leaves the merge base at the last cut, so the next back-merge conflicts. On a conflict, merge \`main\` into \`$branch\` and resolve it there." -fi