| name: validate-pr | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| on: | |
| pull_request: | |
| types: [opened, edited, labeled, unlabeled, synchronize] | |
| jobs: | |
| check-labels: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 # guardrails timeout for the whole job | |
| steps: | |
| - name: Missing `area/` label | |
| if: always() && contains(join(github.event.pull_request.labels.*.name, ','), 'impact/') && !contains(join(github.event.pull_request.labels.*.name, ','), 'area/') | |
| run: | | |
| echo "::error::Every PR with an 'impact/*' label should also have an 'area/*' label" | |
| exit 1 | |
| - name: Missing `kind/` label | |
| if: always() && contains(join(github.event.pull_request.labels.*.name, ','), 'impact/') && !contains(join(github.event.pull_request.labels.*.name, ','), 'kind/') | |
| run: | | |
| echo "::error::Every PR with an 'impact/*' label should also have a 'kind/*' label" | |
| exit 1 | |
| - name: OK | |
| run: exit 0 | |
| check-changelog: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 # guardrails timeout for the whole job | |
| env: | |
| HAS_IMPACT_LABEL: ${{ contains(join(github.event.pull_request.labels.*.name, ','), 'impact/') }} | |
| PR_BODY: | | |
| ${{ github.event.pull_request.body }} | |
| steps: | |
| - name: Check changelog description | |
| run: | | |
| # Extract the `markdown changelog` note code block | |
| block=$(echo -n "$PR_BODY" | tr -d '\r' | awk '/^```markdown changelog$/{flag=1;next}/^```$/{flag=0}flag') | |
| # Strip empty lines | |
| desc=$(echo "$block" | awk NF) | |
| if [ "$HAS_IMPACT_LABEL" = "true" ]; then | |
| if [ -z "$desc" ]; then | |
| echo "::error::Changelog section is empty. Please provide a description for the changelog." | |
| exit 1 | |
| fi | |
| len=$(echo -n "$desc" | wc -c) | |
| if [[ $len -le 6 ]]; then | |
| echo "::error::Description looks too short: $desc" | |
| exit 1 | |
| fi | |
| else | |
| if [ -n "$desc" ]; then | |
| echo "::error::PR has a changelog description, but no changelog label" | |
| echo "::error::Please add the relevant 'impact/' label to the PR or remove the changelog description" | |
| exit 1 | |
| fi | |
| fi | |
| echo "This PR will be included in the release notes with the following note:" | |
| echo "$desc" | |
| check-pr-branch: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 # guardrails timeout for the whole job | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| steps: | |
| # Backports or PR that target a release branch directly should mention the target branch in the title, for example: | |
| # [X.Y backport] Some change that needs backporting to X.Y | |
| # [X.Y] Change directly targeting the X.Y branch | |
| - name: Check release branch | |
| id: title_branch | |
| run: | | |
| # If PR targets a different branch than master, the PR title should mention the target branch in square brackets, for example: | |
| # [27.1 backport] Some change that needs backporting to 27.1 | |
| # [27.1] Change directly targeting the 27.1 branch | |
| # [docker-29.x] Change directly targeting the docker-29.x branch | |
| # [docker-29.x backport] Some change that needs backporting to docker-29.x | |
| # get the intended major version prefix ("[27.1 backport]" -> "27.") from the PR title. | |
| target_branch=$(echo "$PR_TITLE" | sed -nE 's/^\[([^]]+)\].*/\1/p' | sed 's/ backport$//') | |
| echo "target_branch: $target_branch" | |
| echo "GITHUB_BASE_REF: $GITHUB_BASE_REF" | |
| # If the PR is opened against the master branch and the target branch is not specified, exit early. | |
| if [[ "$GITHUB_BASE_REF" == "master" && "$target_branch" == "" ]]; then | |
| exit 0 | |
| fi | |
| if [[ "$target_branch" != "$GITHUB_BASE_REF" ]]; then | |
| echo "::error::PR is opened against the $GITHUB_BASE_REF branch, but its title suggests otherwise." | |
| exit 1 | |
| fi |