/ .github / workflows / release_notes.yml
release_notes.yml
 1  name: Check Release Notes
 2  
 3  on:
 4    pull_request:
 5      types:
 6        - opened
 7        - reopened
 8        - synchronize
 9        - ready_for_review
10        - labeled
11        - unlabeled
12      paths:
13        - "**.py"
14        - "pyproject.toml"
15        - "!.github/**/*.py"
16        - "releasenotes/notes/*.yaml"
17  
18  jobs:
19    reno:
20      runs-on: ubuntu-slim
21      env:
22        PYTHON_VERSION: "3.10"
23      steps:
24        - name: Checkout
25          uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26          with:
27            # With the default value of 1, there are corner cases where tj-actions/changed-files
28            # fails with a `no merge base` error
29            fetch-depth: 0
30  
31        - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
32          with:
33            python-version: "${{ env.PYTHON_VERSION }}"
34        - name: Get release note files
35          id: changed-files
36          uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
37          with:
38            files: releasenotes/notes/*.yaml
39  
40        - name: Check release notes
41          if: steps.changed-files.outputs.any_changed == 'false' && !contains( github.event.pull_request.labels.*.name, 'ignore-for-release-notes')
42          run: |
43            # Check if any of the commit messages contain tags ci/docs/test
44            if git log --pretty=%s origin/main..HEAD | grep -E '^(ci:|docs:|test:)' > /dev/null; then
45              echo "Skipping release note check for commits with 'ci:', 'docs:', or 'test:' tags."
46            else
47              echo "::error::The release notes file is missing, please add one or attach the label 'ignore-for-release-notes' to this PR."
48              exit 1
49            fi
50  
51        - name: Verify release notes formatting
52          if: steps.changed-files.outputs.any_changed == 'true' && !contains( github.event.pull_request.labels.*.name, 'ignore-for-release-notes')
53          run: |
54            pip install "reno<5"
55            reno lint .  # it is not possible to pass a list of files to reno lint
56  
57        - name: Check reStructuredText code formatting
58          if: steps.changed-files.outputs.any_changed == 'true' && !contains( github.event.pull_request.labels.*.name, 'ignore-for-release-notes')
59          shell: python
60          run: |
61            files = "${{ steps.changed-files.outputs.all_changed_files }}".split()
62            errors = []
63  
64            for filepath in files:
65              with open(filepath) as f:
66                for line_no, line in enumerate(f, start=1):
67                  # Check for triple backticks (Markdown code blocks)
68                  if "```" in line:
69                    err = (f"Format error in {filepath}:{line_no}: "
70                           "Found triple backticks. Use reStructuredText code block directive instead: .. code:: python")
71                    errors.append(err)
72  
73                  # Check for single backticks (Markdown inline code)
74                  if "`" in line.replace("```", "").replace("``", ""):
75                    err = (f"Format error in {filepath}:{line_no}: "
76                           "Found single backticks. Use double backticks (``code``) for inline code.")
77                    errors.append(err)
78  
79            if errors:
80                raise Exception("\n".join(errors))