From 392723ec6e643f694a66e47941e5c8ae670e219f Mon Sep 17 00:00:00 2001
From: Pietro Albini <pietro@pietroalbini.org>
Date: Thu, 6 May 2021 18:47:37 +0200
Subject: [PATCH] ci: error out if someone sends a PR to the wrong branch

---
 .github/workflows/ci.yml         |  9 +++++++++
 src/ci/github-actions/ci.yml     |  4 ++++
 src/ci/scripts/verify-channel.sh | 28 ++++++++++++++++++++++++++++
 src/ci/shared.sh                 | 12 ++++++++++++
 4 files changed, 53 insertions(+)
 create mode 100755 src/ci/scripts/verify-channel.sh

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ffaa2b03df9..aa9d97ba477 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -72,6 +72,9 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: ensure the channel matches the target branch
+        run: src/ci/scripts/verify-channel.sh
+        if: success() && !env.SKIP_JOB
       - name: configure GitHub Actions to kill the build when outdated
         uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
         with:
@@ -434,6 +437,9 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: ensure the channel matches the target branch
+        run: src/ci/scripts/verify-channel.sh
+        if: success() && !env.SKIP_JOB
       - name: configure GitHub Actions to kill the build when outdated
         uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
         with:
@@ -541,6 +547,9 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: ensure the channel matches the target branch
+        run: src/ci/scripts/verify-channel.sh
+        if: success() && !env.SKIP_JOB
       - name: configure GitHub Actions to kill the build when outdated
         uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
         with:
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index a59a90b86bc..343091cb779 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -126,6 +126,10 @@ x--expand-yaml-anchors--remove:
         run: src/ci/scripts/should-skip-this.sh
         <<: *step
 
+      - name: ensure the channel matches the target branch
+        run: src/ci/scripts/verify-channel.sh
+        <<: *step
+
       - name: configure GitHub Actions to kill the build when outdated
         uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
         with:
diff --git a/src/ci/scripts/verify-channel.sh b/src/ci/scripts/verify-channel.sh
new file mode 100755
index 00000000000..7945512738e
--- /dev/null
+++ b/src/ci/scripts/verify-channel.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# We want to make sure all PRs are targeting the right branch when they're
+# opened, otherwise we risk (for example) to land a beta-specific change to the
+# master branch. This script ensures the branch of the PR matches the channel.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
+
+declare -A CHANNEL_BRANCH
+CHANNEL_BRANCH["nightly"]="master"
+CHANNEL_BRANCH["beta"]="beta"
+CHANNEL_BRANCH["stable"]="stable"
+
+if isCiBranch auto || isCiBranch try; then
+    echo "channel verification is only executed on PR builds"
+    exit
+fi
+
+channel=$(cat "$(ciCheckoutPath)/src/ci/channel")
+branch="$(ciBaseBranch)"
+if [[ "${branch}" != "${CHANNEL_BRANCH[$channel]}" ]]; then
+    echo "error: PRs changing the \`${channel}\` channel should be sent to the \
+\`${CHANNEL_BRANCH[$channel]}\` branch!"
+
+    exit 1
+fi
diff --git a/src/ci/shared.sh b/src/ci/shared.sh
index 3c196c9478c..332a949a4dc 100644
--- a/src/ci/shared.sh
+++ b/src/ci/shared.sh
@@ -73,6 +73,18 @@ function isCiBranch {
     fi
 }
 
+function ciBaseBranch {
+    if isAzurePipelines; then
+        echo "unsupported on Azure Pipelines"
+        exit 1
+    elif isGitHubActions; then
+        echo "${GITHUB_BASE_REF#refs/heads/}"
+    else
+        echo "ciBaseBranch only works inside CI!"
+        exit 1
+    fi
+}
+
 function ciCommit {
     if isAzurePipelines; then
         echo "${BUILD_SOURCEVERSION}"