#!/bin/bash # # Fails when a pull request depends on a gitea change that is not merged yet. # # 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 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 " >&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