Files
docs/scripts/check-upstream-deps.sh

143 lines
4.8 KiB
Bash
Executable File

#!/bin/bash
#
# Fails when a pull request depends on a gitea change that is not merged yet.
#
# 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 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 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
LABEL="${UPSTREAM_LABEL:-depends-on-upstream}"
PULL_JSON="${1:-}"
if [ -z "$PULL_JSON" ] || [ ! -f "$PULL_JSON" ]; then
echo "usage: $0 <pull request json>" >&2
exit 2
fi
body="$(jq -r '.body // ""' "$PULL_JSON")"
# `label` is a keyword in jq 1.6, which is what the runner image has, so the
# argument cannot be named after it; `(.labels // [])` keeps it parsable there
labelled="$(jq -r --arg want "$LABEL" '[(.labels // [])[].name] | index($want) != null' "$PULL_JSON")"
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)"
if [ -z "$refs" ]; then
if [ "$labelled" = 'true' ]; then
echo "This pull request carries the \"$LABEL\" label but does not say what it"
echo "depends on. Add a line to the description, for example:"
echo
echo " Depends on: gitea#35851"
exit 1
fi
echo "no upstream dependency declared"
exit 0
fi
github_api() {
local url="$1"
if [ -n "${GITHUB_COM_TOKEN:-}" ]; then
curl --silent --show-error --write-out '\n%{http_code}' \
-H "Authorization: Bearer $GITHUB_COM_TOKEN" "$url"
else
curl --silent --show-error --write-out '\n%{http_code}' "$url"
fi
}
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)"
payload="$(printf '%s' "$response" | sed '$d')"
case "$code" in
200)
if [ "$(printf '%s' "$payload" | jq -r '.merged')" = 'true' ]; then
echo "gitea#$ref is merged"
else
echo "gitea#$ref is not merged yet ($(printf '%s' "$payload" | jq -r '.state'))"
unmerged=1
fi
;;
404)
echo "gitea#$ref is not a pull request of go-gitea/gitea, reference the pull request that adds the feature"
unmerged=1
;;
403 | 429)
echo "gitea#$ref: the github api rejected the request (rate limit), cannot tell whether it is merged"
unknown=1
;;
*)
echo "gitea#$ref: unexpected response $code from the github api, cannot tell whether it is merged"
unknown=1
;;
esac
done
if [ "$unknown" -eq 1 ] && [ -z "${GITHUB_COM_TOKEN:-}" ]; then
echo
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 declared dependency is merged, the \"$LABEL\" label can be removed"
fi