ci: only read the Depends on lines, and let the label be the gate

This commit is contained in:
Lunny Xiao
2026-09-08 12:03:59 -07:00
parent 83cbd0138f
commit fecad73221
3 changed files with 78 additions and 26 deletions
+17 -6
View File
@@ -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#<id>` 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#<id>` 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#<id>` 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
+58 -19
View File
@@ -5,15 +5,26 @@
# Usage: ./scripts/check-upstream-deps.sh <pull request json>
#
# 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#<id> 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
+3 -1
View File
@@ -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