From fecad73221843e77d93aa5862d7a78fa79e6cb4d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 8 Sep 2026 12:03:59 -0700 Subject: [PATCH] ci: only read the Depends on lines, and let the label be the gate --- README.md | 23 +++++++--- scripts/check-upstream-deps.sh | 77 ++++++++++++++++++++++++-------- scripts/unlabel-upstream-deps.sh | 4 +- 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f4913248..0b31a82f 100644 --- a/README.md +++ b/README.md @@ -52,23 +52,34 @@ group from the `_category_.json` of its directory. A page that describes a gitea change which is not merged yet must wait for it, otherwise the site documents something no release has. Say so in the description -of the pull request: +of the pull request, on a line of its own, and add the `depends-on-upstream` +label: ``` Depends on: gitea#35851 ``` -The `depends-upstream` check reads those references (`gitea#` or the full -pull request url, several are allowed) and fails while any of them is unmerged, -which blocks the merge since `main` requires the `checks` contexts. Add the -`depends-on-upstream` label as well, so the pull request is easy to find; the -label alone fails the check, it has to be accompanied by a reference. +The label is what blocks the merge: the `depends-upstream` check fails while it +is set, unless every dependency is verified as merged, and `main` requires the +`checks` contexts. The check reads the `Depends on:` lines (`gitea#` or the +full pull request url, several lines are allowed) to find out what to wait for, +so the label needs at least one of them. A `gitea#` mentioned anywhere else +in the description is ignored. + +Without the label, a dependency that the check finds unmerged still fails it, so +forgetting the label does not let an unreleased feature through. The `unlabel merged upstream dependencies` job runs every 6 hours, removes the label once every referenced pull request is merged and comments on the pull request, which reruns the check. `scripts/check-upstream-deps.sh` is the check itself and can be run locally against the json of a pull request. +The check asks the github api whether a pull request is merged, and the runners +share the 60 requests per hour it allows without authentication. The optional +`GITHUB_COM_TOKEN` secret (a token without scopes is enough) raises that limit; +without it an unverifiable dependency only fails the check when the label is +set. + ## Cutting a version ```shell diff --git a/scripts/check-upstream-deps.sh b/scripts/check-upstream-deps.sh index a308e028..ac168ff8 100755 --- a/scripts/check-upstream-deps.sh +++ b/scripts/check-upstream-deps.sh @@ -5,15 +5,26 @@ # Usage: ./scripts/check-upstream-deps.sh # # The json is what the gitea api returns for a pull request; the script reads -# the description and the labels from it. A dependency is declared in the -# description as +# the description and the labels from it. A dependency is declared on a line of +# its own in the description: # # Depends on: gitea#35851 # Depends on: https://github.com/go-gitea/gitea/pull/35851 # -# and may appear several times. `GITHUB_COM_TOKEN` is optional and only lifts -# the rate limit of the unauthenticated github api, `UPSTREAM_LABEL` overrides -# the label the check looks for. +# and may be declared several times. Only such lines count, so a pull request +# that merely mentions gitea#35851 in prose is not held back. +# +# The `depends-on-upstream` label is what blocks the merge: it fails the check +# unless every declared dependency is verified as merged. Without the label, a +# dependency that is known to be unmerged still fails, so a forgotten label does +# not let an unreleased feature through. +# +# Exit codes: 0 nothing blocks, 1 blocked, 2 usage. +# +# Environment: +# GITHUB_COM_TOKEN optional, lifts the rate limit of the github api +# UPSTREAM_LABEL label to look for, `depends-on-upstream` by default +# REQUIRE_VERIFIED set to 1 to also fail when the state cannot be determined set -euo pipefail @@ -30,8 +41,16 @@ body="$(jq -r '.body // ""' "$PULL_JSON")" # argument cannot be named after it; `(.labels // [])` keeps it parsable there labelled="$(jq -r --arg want "$LABEL" '[(.labels // [])[].name] | index($want) != null' "$PULL_JSON")" -# every gitea# and every pull request url, deduplicated +require_verified="${REQUIRE_VERIFIED:-0}" +if [ "$labelled" = 'true' ]; then + # the label says the pull request is not ready, so an unverifiable dependency + # keeps it blocked instead of letting it through + require_verified=1 +fi + +# the ids of the `Depends on:` lines, deduplicated refs="$(printf '%s\n' "$body" \ + | grep -iE '^[[:space:]]*depends[ _-]?on[[:space:]]*:' \ | grep -oiE 'gitea#[0-9]+|github\.com/go-gitea/gitea/pull/[0-9]+' \ | grep -oE '[0-9]+' \ | sort -un || true)" @@ -58,7 +77,8 @@ github_api() { fi } -status=0 +unmerged=0 +unknown=0 for ref in $refs; do response="$(github_api "https://api.github.com/repos/go-gitea/gitea/pulls/$ref")" code="$(printf '%s' "$response" | tail -n 1)" @@ -70,34 +90,53 @@ for ref in $refs; do echo "gitea#$ref is merged" else echo "gitea#$ref is not merged yet ($(printf '%s' "$payload" | jq -r '.state'))" - status=1 + unmerged=1 fi ;; 404) echo "gitea#$ref is not a pull request of go-gitea/gitea, reference the pull request that adds the feature" - status=1 + unmerged=1 ;; 403 | 429) - echo "the github api refused the request for gitea#$ref (rate limit?), cannot tell whether it is merged" - status=1 + echo "gitea#$ref: the github api rejected the request (rate limit), cannot tell whether it is merged" + unknown=1 ;; *) - echo "unexpected response $code from the github api for gitea#$ref" - status=1 + echo "gitea#$ref: unexpected response $code from the github api, cannot tell whether it is merged" + unknown=1 ;; esac done -if [ "$status" -ne 0 ]; then +if [ "$unknown" -eq 1 ] && [ -z "${GITHUB_COM_TOKEN:-}" ]; then echo - echo "The documentation of an unreleased feature can only be merged once the" - echo "feature itself is in gitea main. Keep the \"$LABEL\" label on this pull" - echo "request; the scheduled job removes it and reruns this check as soon as" - echo "every referenced pull request is merged." + echo "The github api allows 60 unauthenticated requests per hour and address," + echo "which the runners share. Set the GITHUB_COM_TOKEN secret to a token with" + echo "no scopes to raise that limit." +fi + +if [ "$unmerged" -eq 1 ] || { [ "$unknown" -eq 1 ] && [ "$require_verified" = '1' ]; }; then + echo + if [ "$labelled" = 'true' ]; then + echo "This pull request is labelled \"$LABEL\", so it stays blocked until every" + echo "dependency above is merged into gitea main. The scheduled job removes the" + echo "label and reruns this check as soon as that is the case." + else + echo "The documentation of an unreleased feature can only be merged once the" + echo "feature itself is in gitea main. Add the \"$LABEL\" label so the pull" + echo "request is picked up by the job that watches for it." + fi exit 1 fi +if [ "$unknown" -eq 1 ]; then + echo + echo "Could not verify every dependency, letting the check pass: the pull request" + echo "does not carry the \"$LABEL\" label." + exit 0 +fi + if [ "$labelled" = 'true' ]; then echo - echo "every referenced pull request is merged, the \"$LABEL\" label can be removed" + echo "every declared dependency is merged, the \"$LABEL\" label can be removed" fi diff --git a/scripts/unlabel-upstream-deps.sh b/scripts/unlabel-upstream-deps.sh index eeddad64..35f26697 100755 --- a/scripts/unlabel-upstream-deps.sh +++ b/scripts/unlabel-upstream-deps.sh @@ -54,7 +54,9 @@ for index in $indexes; do # the check script exits 0 when every reference is merged; it needs the label # to be absent from its input to stay quiet about it, so drop it here jq 'del(.labels)' "$work/pull.json" > "$work/pull-unlabelled.json" - if ! output="$("$here/check-upstream-deps.sh" "$work/pull-unlabelled.json" 2>&1)"; then + # REQUIRE_VERIFIED keeps a dependency whose state could not be determined from + # counting as merged, which would drop the label too early + if ! output="$(REQUIRE_VERIFIED=1 "$here/check-upstream-deps.sh" "$work/pull-unlabelled.json" 2>&1)"; then echo "#$index still waits for upstream:" printf '%s\n' "$output" | sed 's/^/ /' continue