diff --git a/.github/workflows/check-backport-labels.yml b/.github/workflows/check-backport-labels.yml new file mode 100644 index 0000000000..b9d97724d6 --- /dev/null +++ b/.github/workflows/check-backport-labels.yml @@ -0,0 +1,126 @@ +name: Check + +on: + pull_request: + branches: + - main + types: [labeled, unlabeled, opened, reopened, synchronize] + +permissions: + pull-requests: "read" + +jobs: + check-backport-label: + name: backport label + runs-on: ubuntu-latest + + steps: + - name: "Check backport label" + env: + GH_TOKEN: ${{ github.token }} + run: | + json_pr_labels='${{ toJSON(github.event.pull_request.labels) }}' + readarray -t pr_labels < <(echo "${json_pr_labels}" | jq -r -c '.[].name') + + json_all_labels="$(gh label list --repo ${{ github.repository }} --json name --search "backport" --limit 1000)" + readarray -t all_labels < <(echo "${json_all_labels}" | jq -r -c '.[].name') + + declare -A all_backport_labels=() + declare -A all_floating_majors=() + + backport_regex="^backport ([0-9])+\.([0-9]+|x)$" + + echo "::group::Available Labels" + echo "skip-backport" + + for label in "${all_labels[@]}"; do + if [[ "${label}" =~ ${backport_regex} ]]; then + major="${BASH_REMATCH[1]}" + minor="${BASH_REMATCH[2]}" + all_backport_labels["${label}"]=1 + echo "${label}" + + if [ "${minor}" = "x" ]; then + all_floating_majors["${major}"]=1 + fi + fi + done + + echo "::endgroup::" + + has_exact_backport_label=false + declare -A pr_exact_majors=() + declare -A pr_floating_majors=() + + echo "::group::Detected Labels" + + for pr_label in "${pr_labels[@]}"; do + if [ "${pr_label}" = "skip-backport" ]; then + has_exact_backport_label=true + echo "${pr_label}" + continue + fi + + if [ -z "${all_backport_labels[${pr_label}]}" ]; then + continue + fi + + if [[ "${pr_label}" =~ ${backport_regex} ]]; then + major="${BASH_REMATCH[1]}" + minor="${BASH_REMATCH[2]}" + if [ "${minor}" != "x" ]; then + pr_exact_majors["${major}"]=1 + has_exact_backport_label=true + else + pr_floating_majors["${major}"]=1 + fi + fi + + echo "${pr_label}" + done + + echo "::endgroup::" + + if [ "${has_exact_backport_label}" != true ]; then + echo "::error::No exact backport label found. Please add at least one of the"\ + "'backport {major}.{minor}' labels or use 'skip-backport',"\ + "if this PR should not be backported." + exit 1 + fi + + # Validate that a floating backport label exists for each exact backport label major + # version. + + has_required_floating_labels=true + + for pr_major in "${!pr_exact_majors[@]}"; do + if [ -z "${all_floating_majors[${pr_major}]}" ]; then + # There is no floating version branch for the given major version. + continue + fi + + if [ -z "${pr_floating_majors[${pr_major}]}" ]; then + has_required_floating_labels=false + echo "::error::Missing floating backport label for '${pr_major}.x'" + fi + done + + if [ "${has_required_floating_labels}" != true ]; then + exit 1 + fi + + # Validate that an exact backport label exists for each floating backport label major + # version. + + has_required_exact_labels=true + + for pr_floating_major in "${!pr_floating_majors[@]}"; do + if [ -z "${pr_exact_majors[${pr_floating_major}]}" ]; then + has_required_exact_labels=false + echo "::error::Missing exact backport label for '${pr_floating_major}.x'" + fi + done + + if [ "${has_required_exact_labels}" != true ]; then + exit 1 + fi