├── tests ├── clean.html ├── with-warnings.html ├── exclude-test.html ├── with-new-warnings.html └── README.md ├── LICENSE ├── SECURITY.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── .github └── workflows │ └── ci.yml ├── README.md ├── examples.md └── action.yml /tests/clean.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Clean Code Output 5 | 6 | 7 |

Test Output

8 |
9 |
10 | Running code...
11 | Result: 42
12 | Success!
13 |         
14 |
15 | 16 | -------------------------------------------------------------------------------- /tests/with-warnings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code Output with Warnings 5 | 6 | 7 |

Test Output with Warnings

8 |
9 |
10 | Running code...
11 | /path/to/file.py:15: DeprecationWarning: This function is deprecated
12 |   result = old_function()
13 | /path/to/file.py:25: SyntaxWarning: invalid escape sequence '\d'
14 |   pattern = '\d+'
15 | Result: 42
16 |         
17 |
18 |
19 |
20 | Another execution...
21 | /path/to/another.py:10: FutureWarning: This will change in future versions
22 |   new_behavior = True
23 | Done!
24 |         
25 |
26 | 27 | -------------------------------------------------------------------------------- /tests/exclude-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test HTML for Exclude Warning Functionality 5 | 6 | 7 |

Test Page

8 | 9 | 10 |
11 |
/home/user/script.py:10: UserWarning: This is a test message
12 |
13 | 14 | 15 |
16 |
/home/user/script.py:20: DeprecationWarning: This function is deprecated
17 |
18 | 19 | 20 |
21 |
/home/user/script.py:30: RuntimeWarning: Invalid value encountered
22 |
23 | 24 | 25 |
26 |
/home/user/script.py:40: ResourceWarning: unclosed file
27 |
28 | 29 | 30 |

This paragraph mentions UserWarning but should not be detected.

31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 QuantEcon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/with-new-warnings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code Output with New Warning Types 5 | 6 | 7 |

Test Output with New Warning Types

8 |
9 |
10 | Running code with various warnings...
11 | /path/to/file.py:15: UserWarning: Creating legend with loc="best" can be slow with large amounts of data.
12 |   fig.canvas.print_figure(bytes_io, **kw)
13 | /path/to/file.py:25: RuntimeWarning: divide by zero encountered in log
14 |   result = np.log(0)
15 | /path/to/file.py:35: ResourceWarning: unclosed file <_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>
16 |   f = open('test.txt')
17 | Result: computed successfully
18 |         
19 |
20 |
21 |
22 | Another execution with more warnings...
23 | /path/to/another.py:10: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
24 |   import local_module
25 | /path/to/another.py:20: BytesWarning: str() on a bytes instance
26 |   text = str(b'hello')
27 | Done!
28 |         
29 |
30 | 31 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | We provide security updates for the following versions: 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | latest | :white_check_mark: | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | If you discover a security vulnerability in this GitHub Action, please report it responsibly: 14 | 15 | 1. **Do not** open a public issue 16 | 2. Send an email to contact@quantecon.org with: 17 | - Description of the vulnerability 18 | - Steps to reproduce 19 | - Potential impact 20 | - Suggested fix (if any) 21 | 22 | We will acknowledge your email within 48 hours and provide a detailed response within 1 week indicating the next steps in handling your report. 23 | 24 | ## Security Best Practices 25 | 26 | When using this action: 27 | 28 | - Always pin to a specific version or SHA rather than using `@main` 29 | - Regularly update to the latest version 30 | - Review the action's permissions and inputs 31 | - Use secrets appropriately and never log sensitive information 32 | 33 | ## Responsible Disclosure 34 | 35 | We follow responsible disclosure practices and will: 36 | 37 | - Acknowledge the vulnerability report 38 | - Work on a fix 39 | - Release the fix 40 | - Publicly acknowledge the reporter (if desired) 41 | 42 | Thank you for helping keep QuantEcon's GitHub Actions secure! -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the Check Python Warnings action will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | - Initial release of the Check Python Warnings action 12 | - Smart detection of warnings within HTML code cell outputs only 13 | - Support for all Python warning types by default 14 | - Configurable warning types and exclusions 15 | - GitHub issue creation with detailed reports 16 | - Workflow artifact generation 17 | - PR comment integration when failing on warnings 18 | - Comprehensive CI testing 19 | 20 | ### Features 21 | - Scans HTML files for Python warnings in `cell_output` elements 22 | - Avoids false positives by ignoring warnings in text content 23 | - Supports custom warning lists and exclusions 24 | - Optional workflow failure on warning detection 25 | - Detailed JSON output with warning locations and types 26 | - Integration with GitHub Issues API for automated reporting 27 | - Artifact creation for warning analysis 28 | - Assignable GitHub issue notifications 29 | 30 | ## [1.0.0] - 2025-10-01 31 | 32 | ### Added 33 | - Initial stable release migrated from QuantEcon/meta repository 34 | - Full compatibility with existing workflows 35 | - Enhanced documentation and examples 36 | - Comprehensive test suite 37 | - GitHub Marketplace listing -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Test Files for Check Warnings Action 2 | 3 | This directory contains test HTML files used to validate the check-warnings action functionality. 4 | 5 | ## Test Files 6 | 7 | ### with-warnings.html 8 | - Contains HTML with Python warnings in `cell_output` elements 9 | - Tests detection of DeprecationWarning and SyntaxWarning 10 | - Should be detected by the action 11 | 12 | ### with-new-warnings.html 13 | - Contains newer types of warnings for testing 14 | - Tests detection of various warning types 15 | 16 | ### clean.html 17 | - Contains HTML output without any warnings 18 | - Should pass all warning checks 19 | 20 | ### exclude-test.html 21 | - Contains warnings that can be used to test exclusion functionality 22 | - Tests the exclude-warning parameter 23 | 24 | ## Usage in CI 25 | 26 | These files are automatically used by the GitHub Actions CI workflow to test: 27 | - Warning detection accuracy 28 | - False positive avoidance (only checks `cell_output` elements) 29 | - Exclusion functionality 30 | - Clean file handling 31 | 32 | ## Running Tests Locally 33 | 34 | You can test the action locally using these files: 35 | 36 | ```bash 37 | # Test with warnings (should find issues) 38 | ./action.yml --html-path tests --fail-on-warning false 39 | 40 | # Test with clean files only (should pass) 41 | ./action.yml --html-path tests/clean.html --fail-on-warning true 42 | 43 | # Test exclusion functionality 44 | ./action.yml --html-path tests --warnings "DeprecationWarning" --exclude-warning "SyntaxWarning" 45 | ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to QuantEcon GitHub Actions 2 | 3 | Thank you for your interest in contributing! We welcome contributions from the community. 4 | 5 | ## Getting Started 6 | 7 | 1. Fork the repository 8 | 2. Create a feature branch: `git checkout -b feature/your-feature-name` 9 | 3. Make your changes 10 | 4. Add tests if applicable 11 | 5. Run the test suite 12 | 6. Commit your changes with a clear message 13 | 7. Push to your fork and submit a pull request 14 | 15 | ## Development Setup 16 | 17 | ```bash 18 | # Clone your fork 19 | git clone https://github.com/YOUR-USERNAME/REPO-NAME.git 20 | cd REPO-NAME 21 | 22 | # Install development dependencies (if applicable) 23 | pip install -r requirements-dev.txt 24 | 25 | # Run tests 26 | pytest tests/ 27 | ``` 28 | 29 | ## Code Style 30 | 31 | - Follow PEP 8 for Python code 32 | - Use meaningful variable and function names 33 | - Add docstrings for functions and classes 34 | - Keep functions small and focused 35 | 36 | ## Testing 37 | 38 | - Write tests for new functionality 39 | - Ensure existing tests pass 40 | - Test with different Python versions if applicable 41 | - Include integration tests for the GitHub Action 42 | 43 | ## Pull Request Process 44 | 45 | 1. Update documentation if needed 46 | 2. Add entries to CHANGELOG.md 47 | 3. Ensure CI passes 48 | 4. Request review from maintainers 49 | 50 | ## Reporting Issues 51 | 52 | When reporting issues, please include: 53 | 54 | - Clear description of the problem 55 | - Steps to reproduce 56 | - Expected vs actual behavior 57 | - Environment details (OS, Python version, etc.) 58 | - Relevant logs or error messages 59 | 60 | ## Questions? 61 | 62 | Feel free to open an issue for questions or reach out to the QuantEcon team. 63 | 64 | Thank you for contributing! -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test-action: 11 | runs-on: ubuntu-latest 12 | name: Test Check Warnings Action 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Test action with real test files (should find warnings) 19 | id: test-with-warnings 20 | continue-on-error: true 21 | uses: ./ 22 | with: 23 | html-path: 'tests' 24 | fail-on-warning: 'true' 25 | create-artifact: 'true' 26 | artifact-name: 'test-warnings-report' 27 | 28 | - name: Verify warnings were found 29 | run: | 30 | if [ "${{ steps.test-with-warnings.outputs.warnings-found }}" != "true" ]; then 31 | echo "ERROR: Expected to find warnings but none were found" 32 | exit 1 33 | fi 34 | echo "SUCCESS: Found expected warnings (${{ steps.test-with-warnings.outputs.warning-count }} warnings)" 35 | 36 | - name: Test action without failing (should pass) 37 | id: test-no-fail 38 | uses: ./ 39 | with: 40 | html-path: 'tests' 41 | fail-on-warning: 'false' 42 | 43 | - name: Test clean files only (should find no warnings) 44 | uses: ./ 45 | with: 46 | html-path: 'tests/clean.html' 47 | fail-on-warning: 'true' 48 | 49 | - name: Test exclude warning functionality 50 | uses: ./ 51 | with: 52 | html-path: 'tests' 53 | warnings: 'DeprecationWarning' 54 | exclude-warning: 'SyntaxWarning' 55 | fail-on-warning: 'false' 56 | 57 | test-different-warnings: 58 | runs-on: ubuntu-latest 59 | name: Test Different Warning Types 60 | 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@v4 64 | 65 | - name: Create test HTML with specific warnings 66 | run: | 67 | mkdir -p test-specific 68 | cat > test-specific/runtime-warning.html << 'EOF' 69 | 70 | 71 | 72 |
73 |
RuntimeWarning: Something happened
74 |
75 | 76 | 77 | EOF 78 | 79 | - name: Test specific warning type 80 | uses: ./ 81 | with: 82 | html-path: 'test-specific' 83 | warnings: 'RuntimeWarning' 84 | fail-on-warning: 'false' 85 | 86 | - name: Test excluded warning type 87 | uses: ./ 88 | with: 89 | html-path: 'test-specific' 90 | warnings: 'UserWarning,DeprecationWarning' 91 | fail-on-warning: 'true' # Should pass because no UserWarning or DeprecationWarning -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Check for Python Warnings Action 2 | 3 | [![CI](https://github.com/QuantEcon/action-check-warnings/actions/workflows/ci.yml/badge.svg)](https://github.com/QuantEcon/action-check-warnings/actions/workflows/ci.yml) 4 | 5 | A GitHub Action that scans HTML files for Python warnings within code cell outputs and optionally fails the workflow if any are found. Perfect for ensuring documentation builds are warning-free. 6 | 7 | ## 🎯 Features 8 | 9 | - **Smart Detection**: Only scans warnings within code cell outputs (`cell_output` class) to avoid false positives 10 | - **Comprehensive Coverage**: Supports all Python warning types by default 11 | - **Flexible Reporting**: Create GitHub issues, workflow artifacts, or PR comments 12 | - **Configurable**: Customize warning types, paths, and behavior 13 | - **CI/CD Ready**: Seamlessly integrates with documentation build workflows 14 | 15 | ## 🚀 Quick Start 16 | 17 | ```yaml 18 | - name: Check for warnings 19 | uses: QuantEcon/action-check-warnings@v1.0.0 20 | with: 21 | html-path: './_build/html' 22 | ``` 23 | 24 | ## Features 25 | 26 | - Scans HTML files for configurable Python warnings **within code cell outputs only** 27 | - Prevents false positives by only checking warnings in `cell_output` HTML elements 28 | - Supports multiple warning types (all Python warning types by default: UserWarning, DeprecationWarning, PendingDeprecationWarning, SyntaxWarning, RuntimeWarning, FutureWarning, ImportWarning, Uni[...] 29 | - Provides detailed output about warnings found 30 | - Optionally fails the workflow when warnings are detected 31 | - **Creates GitHub issues** with detailed warning reports 32 | - **Generates workflow artifacts** containing warning reports 33 | - **Posts PR comments** with warning reports when failing on warnings 34 | - Configurable search path and warning types 35 | 36 | ## Usage 37 | 38 | ### Basic Usage 39 | 40 | ```yaml 41 | - name: Check for Python warnings 42 | uses: QuantEcon/action-check-warnings@v1.0.0 43 | ``` 44 | 45 | ### Advanced Usage with PR Comments 46 | 47 | ```yaml 48 | - name: Check for Python warnings with PR feedback 49 | uses: QuantEcon/action-check-warnings@v1.0.0 50 | with: 51 | html-path: './_build/html' 52 | # Uses comprehensive default warnings (all Python warning types) 53 | fail-on-warning: 'true' # This will post a comment to the PR if warnings are found 54 | ``` 55 | 56 | ### Advanced Usage with Issue Creation 57 | 58 | ```yaml 59 | - name: Check for Python warnings with issue creation 60 | uses: QuantEcon/action-check-warnings@v1.0.0 61 | with: 62 | html-path: './_build/html' 63 | # Uses comprehensive default warnings (all Python warning types) 64 | fail-on-warning: 'false' 65 | create-issue: 'true' 66 | issue-title: 'Python Warnings Found in Documentation Build' 67 | ``` 68 | 69 | ### Advanced Usage with Issue Creation and User Assignment 70 | 71 | ```yaml 72 | - name: Check for Python warnings with assigned issue 73 | uses: QuantEcon/action-check-warnings@v1.0.0 74 | with: 75 | html-path: './_build/html' 76 | # Uses comprehensive default warnings (all Python warning types) 77 | fail-on-warning: 'false' 78 | create-issue: 'true' 79 | issue-title: 'Python Warnings Found in Documentation Build' 80 | notify: 'username1,username2' # Assign issue to multiple users 81 | ``` 82 | 83 | ### Advanced Usage with Artifact Creation 84 | 85 | ```yaml 86 | - name: Check for Python warnings with artifact 87 | uses: QuantEcon/action-check-warnings@v1.0.0 88 | with: 89 | html-path: './_build/html' 90 | # Uses comprehensive default warnings (all Python warning types) 91 | fail-on-warning: 'true' 92 | create-artifact: 'true' 93 | artifact-name: 'python-warning-report' 94 | ``` 95 | 96 | ### Complete Advanced Usage 97 | 98 | ```yaml 99 | - name: Check for Python warnings in build output 100 | uses: QuantEcon/action-check-warnings@v1.0.0 101 | with: 102 | html-path: './_build/html' 103 | # Uses comprehensive default warnings (all Python warning types) 104 | fail-on-warning: 'false' 105 | create-issue: 'true' 106 | issue-title: 'Python Warnings Detected in Build' 107 | notify: 'maintainer1,reviewer2' # Assign to specific team members 108 | create-artifact: 'true' 109 | artifact-name: 'detailed-warning-report' 110 | ``` 111 | 112 | ### Excluding Specific Warning Types 113 | 114 | Sometimes you may want to temporarily exclude certain warning types (e.g., when dealing with upstream warnings that take time to fix): 115 | 116 | ```yaml 117 | - name: Check for Python warnings excluding upstream warnings 118 | uses: QuantEcon/action-check-warnings@v1.0.0 119 | with: 120 | html-path: './_build/html' 121 | exclude-warning: 'UserWarning' # Exclude single warning type 122 | fail-on-warning: 'true' 123 | ``` 124 | 125 | ```yaml 126 | - name: Check for Python warnings excluding multiple warning types 127 | uses: QuantEcon/action-check-warnings@v1.0.0 128 | with: 129 | html-path: './_build/html' 130 | exclude-warning: 'UserWarning,RuntimeWarning,ResourceWarning' # Exclude multiple warnings 131 | fail-on-warning: 'true' 132 | ``` 133 | 134 | ### Custom Warning Types with Exclusions 135 | 136 | You can combine custom warning lists with exclusions: 137 | 138 | ```yaml 139 | - name: Check for specific warnings but exclude problematic ones 140 | uses: QuantEcon/action-check-warnings@v1.0.0 141 | with: 142 | html-path: './_build/html' 143 | warnings: 'UserWarning,DeprecationWarning,RuntimeWarning,ResourceWarning' 144 | exclude-warning: 'ResourceWarning' # Check all above except ResourceWarning 145 | fail-on-warning: 'true' 146 | ``` 147 | 148 | ### Using Outputs 149 | 150 | ```yaml 151 | - name: Check for Python warnings 152 | id: warning-check 153 | uses: QuantEcon/action-check-warnings@v1.0.0 154 | with: 155 | fail-on-warning: 'false' 156 | 157 | - name: Report warnings 158 | if: steps.warning-check.outputs.warnings-found == 'true' 159 | run: | 160 | echo "Found ${{ steps.warning-check.outputs.warning-count }} warnings:" 161 | echo "${{ steps.warning-check.outputs.warning-details }}" 162 | ``` 163 | 164 | ## New Features 165 | 166 | ### GitHub Issue Creation 167 | 168 | When `create-issue` is set to `true`, the action will automatically create a GitHub issue when warnings are detected. The issue includes: 169 | 170 | - Detailed warning information with file paths and line numbers 171 | - Repository and workflow context 172 | - Direct links to the failing workflow run 173 | - Suggested next steps for resolution 174 | - Automatic labeling (`bug`, `documentation`, `python-warnings`) 175 | 176 | #### Automatic User Assignment 177 | 178 | When the `notify` parameter is provided, the created issue will be automatically assigned to the specified GitHub users. This feature supports: 179 | 180 | - Single user assignment: `notify: 'username'` 181 | - Multiple user assignment: `notify: 'user1,user2,user3'` 182 | - Robust error handling: If assignment fails, the issue is still created successfully 183 | 184 | This ensures that the right team members are immediately notified about warnings and can take action to resolve them. 185 | 186 | Additionally, when issues are created in pull request contexts, a simple notification comment is posted to the PR thread containing: 187 | 188 | - List of files with warnings 189 | - Direct link to the created issue for detailed information 190 | 191 | This provides immediate awareness to PR authors without cluttering the conversation with full warning details. 192 | 193 | ### Workflow Artifacts 194 | 195 | When `create-artifact` is set to `true`, the action generates a detailed Markdown report as a workflow artifact. This report includes: 196 | 197 | - Complete warning details in a readable format 198 | - Repository and workflow metadata 199 | - Timestamp and commit information 200 | - Downloadable for offline review 201 | 202 | ### Pull Request Comments 203 | 204 | When `fail-on-warning` is set to `true` and warnings are found in a pull request, the action automatically posts a detailed comment to the PR containing: 205 | 206 | - Complete warning information formatted for easy reading 207 | - Direct links to the failing workflow run 208 | - Suggested next steps for fixing the warnings 209 | - Repository and commit context 210 | 211 | This feature helps developers quickly identify and fix warnings without digging through workflow logs. 212 | 213 | ### Using Both Features Together 214 | 215 | You can enable both issue creation and artifact generation simultaneously: 216 | 217 | ```yaml 218 | - name: Comprehensive warning check 219 | uses: QuantEcon/action-check-warnings@v1.0.0 220 | with: 221 | html-path: './_build/html' 222 | fail-on-warning: 'false' # Don't fail, just report 223 | create-issue: 'true' # Create issue for tracking 224 | create-artifact: 'true' # Create artifact for detailed review 225 | ``` 226 | 227 | ## How It Works 228 | 229 | This action specifically searches for Python warnings within HTML elements that have `cell_output` in their class attribute. This approach prevents false positives that would occur if warnings li[...] 230 | 231 | ### Example HTML Structure 232 | 233 | The action will detect warnings in this structure: 234 | ```html 235 |
236 |
237 |     /path/to/file.py:10: FutureWarning: This feature will be deprecated
238 |       result = old_function()
239 |     
240 |
241 | ``` 242 | 243 | But will **ignore** warnings mentioned in regular content: 244 | ```html 245 |
246 |

In this tutorial, we'll discuss FutureWarning messages.

247 |
248 | ``` 249 | 250 | This ensures that educational content about warnings doesn't trigger false positives in the check. 251 | 252 | ## Permissions 253 | 254 | For the action to work correctly with all features, ensure your workflow has the appropriate permissions: 255 | 256 | ```yaml 257 | permissions: 258 | contents: read # For checking out the repository 259 | issues: write # For creating GitHub issues (if create-issue is enabled) 260 | actions: read # For creating workflow artifacts (if create-artifact is enabled) 261 | pull-requests: write # For posting PR comments (when fail-on-warning is true OR create-issue is true in PRs) 262 | ``` 263 | 264 | If you're only using the basic warning check functionality, only `contents: read` is required. Add `pull-requests: write` when you want PR comments on warnings or when using issue creation in PR [...] 265 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | # Example: Using the Warning Check Action 2 | 3 | This directory contains examples of how to use the `check-warnings` action in different scenarios. 4 | 5 | ## Example 1: Basic Jupyter Book Build 6 | 7 | ```yaml 8 | name: Build and Check Jupyter Book 9 | 10 | on: 11 | push: 12 | branches: [ main ] 13 | pull_request: 14 | branches: [ main ] 15 | 16 | jobs: 17 | build-and-check: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | - name: Set up Python 25 | uses: actions/setup-python@v4 26 | with: 27 | python-version: '3.11' 28 | 29 | - name: Install dependencies 30 | run: | 31 | pip install jupyter-book 32 | pip install -r requirements.txt 33 | 34 | - name: Build Jupyter Book 35 | run: | 36 | jupyter-book build . 37 | 38 | - name: Check for Python warnings in built HTML 39 | uses: QuantEcon/meta/.github/actions/check-warnings@main 40 | with: 41 | html-path: './_build/html' 42 | # Uses comprehensive default warnings (all Python warning types) 43 | fail-on-warning: 'true' 44 | ``` 45 | 46 | ## Example 2: Non-failing Check with GitHub Issue Creation 47 | 48 | ```yaml 49 | name: Build with Issue Creation 50 | permissions: 51 | contents: read 52 | issues: write 53 | 54 | on: 55 | push: 56 | branches: [ main ] 57 | 58 | jobs: 59 | build-with-issue-creation: 60 | runs-on: ubuntu-latest 61 | 62 | steps: 63 | - name: Checkout repository 64 | uses: actions/checkout@v4 65 | 66 | - name: Build documentation 67 | run: | 68 | # Your build process here 69 | make html 70 | 71 | - name: Check for warnings with issue creation 72 | uses: QuantEcon/meta/.github/actions/check-warnings@main 73 | with: 74 | html-path: './docs/_build/html' 75 | fail-on-warning: 'false' 76 | create-issue: 'true' 77 | issue-title: 'Python Warnings Found in Documentation' 78 | notify: 'maintainer1,reviewer2' # Assign to team members 79 | ``` 80 | 81 | ## Example 2b: Issue Creation with Single User Assignment 82 | 83 | ```yaml 84 | - name: Check for warnings with single user assignment 85 | uses: QuantEcon/meta/.github/actions/check-warnings@main 86 | with: 87 | html-path: './_build/html' 88 | create-issue: 'true' 89 | issue-title: 'Critical Python Warnings Detected' 90 | notify: 'team-lead' # Assign to single responsible person 91 | ``` 92 | 93 | ## Example 3: Check with Artifact Generation 94 | 95 | ```yaml 96 | name: Build with Artifact Report 97 | 98 | on: 99 | pull_request: 100 | branches: [ main ] 101 | 102 | jobs: 103 | build-with-artifact: 104 | runs-on: ubuntu-latest 105 | 106 | steps: 107 | - name: Checkout repository 108 | uses: actions/checkout@v4 109 | 110 | - name: Build documentation 111 | run: | 112 | jupyter-book build . 113 | 114 | - name: Check for warnings with artifact 115 | uses: QuantEcon/meta/.github/actions/check-warnings@main 116 | with: 117 | html-path: './_build/html' 118 | fail-on-warning: 'true' 119 | create-artifact: 'true' 120 | artifact-name: 'pr-warning-report' 121 | ``` 122 | 123 | ## Example 4: Comprehensive Warning Management 124 | 125 | ```yaml 126 | name: Complete Warning Management 127 | 128 | on: 129 | push: 130 | branches: [ main ] 131 | pull_request: 132 | branches: [ main ] 133 | 134 | jobs: 135 | comprehensive-warning-check: 136 | runs-on: ubuntu-latest 137 | 138 | steps: 139 | - name: Checkout repository 140 | uses: actions/checkout@v4 141 | 142 | - name: Set up Python 143 | uses: actions/setup-python@v4 144 | with: 145 | python-version: '3.11' 146 | 147 | - name: Install dependencies 148 | run: | 149 | pip install jupyter-book 150 | pip install -r requirements.txt 151 | 152 | - name: Build Jupyter Book 153 | run: | 154 | jupyter-book build . 155 | 156 | - name: Comprehensive warning check 157 | id: warning-check 158 | uses: QuantEcon/meta/.github/actions/check-warnings@main 159 | with: 160 | html-path: './_build/html' 161 | # Uses comprehensive default warnings (all Python warning types) 162 | fail-on-warning: 'false' # Don't fail on warnings 163 | create-issue: ${{ github.event_name == 'push' }} # Create issues only on push to main 164 | issue-title: 'Python Warnings in Documentation Build - ${{ github.sha }}' 165 | notify: 'team-lead,maintainer' # Assign to responsible team members 166 | create-artifact: 'true' # Always create artifact for review 167 | artifact-name: 'warning-report-${{ github.run_id }}' 168 | 169 | - name: Comment on PR with warning info 170 | if: github.event_name == 'pull_request' && steps.warning-check.outputs.warnings-found == 'true' 171 | uses: actions/github-script@v7 172 | with: 173 | script: | 174 | const warningCount = '${{ steps.warning-check.outputs.warning-count }}'; 175 | const artifactName = 'warning-report-${{ github.run_id }}'; 176 | 177 | github.rest.issues.createComment({ 178 | issue_number: context.issue.number, 179 | owner: context.repo.owner, 180 | repo: context.repo.repo, 181 | body: `⚠️ **Warning:** Found ${warningCount} Python warning(s) in the documentation build. 182 | 183 | Please review the [warning report artifact](${context.payload.repository.html_url}/actions/runs/${{ github.run_id }}) for details. 184 | 185 | Consider fixing these warnings before merging to maintain code quality.` 186 | }) 187 | ``` 188 | 189 | ## Example 5: Non-failing Check with Reporting 190 | 191 | ```yaml 192 | name: Build with Warning Report 193 | 194 | on: 195 | push: 196 | branches: [ main ] 197 | 198 | jobs: 199 | build-with-warning-report: 200 | runs-on: ubuntu-latest 201 | 202 | steps: 203 | - name: Checkout repository 204 | uses: actions/checkout@v4 205 | 206 | - name: Build documentation 207 | run: | 208 | # Your build process here 209 | make html 210 | 211 | - name: Check for warnings (non-failing) 212 | id: warning-check 213 | uses: QuantEcon/meta/.github/actions/check-warnings@main 214 | with: 215 | html-path: './docs/_build/html' 216 | fail-on-warning: 'false' 217 | 218 | - name: Create warning report 219 | if: steps.warning-check.outputs.warnings-found == 'true' 220 | run: | 221 | echo "## Warning Report" >> $GITHUB_STEP_SUMMARY 222 | echo "Found ${{ steps.warning-check.outputs.warning-count }} warnings:" >> $GITHUB_STEP_SUMMARY 223 | echo '```' >> $GITHUB_STEP_SUMMARY 224 | echo "${{ steps.warning-check.outputs.warning-details }}" >> $GITHUB_STEP_SUMMARY 225 | echo '```' >> $GITHUB_STEP_SUMMARY 226 | 227 | - name: Post warning comment (for PRs) 228 | if: github.event_name == 'pull_request' && steps.warning-check.outputs.warnings-found == 'true' 229 | uses: actions/github-script@v7 230 | with: 231 | script: | 232 | github.rest.issues.createComment({ 233 | issue_number: context.issue.number, 234 | owner: context.repo.owner, 235 | repo: context.repo.repo, 236 | body: '⚠️ **Python Warnings Found**\n\nFound ${{ steps.warning-check.outputs.warning-count }} warnings in the built documentation:\n\n```\n${{ steps.warning-check.outputs.warning-details }}\n```' 237 | }) 238 | ``` 239 | 240 | ## Example 6: Custom Warning Types 241 | 242 | ```yaml 243 | name: Check for Custom Warnings 244 | 245 | on: 246 | workflow_dispatch: 247 | inputs: 248 | custom_warnings: 249 | description: 'Custom warnings to check for' 250 | required: false 251 | default: 'UserWarning,RuntimeWarning,ResourceWarning' 252 | 253 | jobs: 254 | custom-warning-check: 255 | runs-on: ubuntu-latest 256 | 257 | steps: 258 | - name: Checkout repository 259 | uses: actions/checkout@v4 260 | 261 | - name: Build project 262 | run: | 263 | # Your build process 264 | make build 265 | 266 | - name: Check for custom warnings 267 | uses: QuantEcon/meta/.github/actions/check-warnings@main 268 | with: 269 | html-path: './output' 270 | warnings: ${{ github.event.inputs.custom_warnings || 'UserWarning,RuntimeWarning,ResourceWarning' }} 271 | fail-on-warning: 'true' 272 | ``` 273 | 274 | ## Example 6b: Excluding Specific Warning Types 275 | 276 | ```yaml 277 | name: Check with Warning Exclusions 278 | 279 | on: 280 | push: 281 | branches: [ main ] 282 | 283 | jobs: 284 | warning-check-with-exclusions: 285 | runs-on: ubuntu-latest 286 | 287 | steps: 288 | - name: Checkout repository 289 | uses: actions/checkout@v4 290 | 291 | - name: Build documentation 292 | run: | 293 | jupyter-book build . 294 | 295 | - name: Check for warnings excluding upstream issues 296 | uses: QuantEcon/meta/.github/actions/check-warnings@main 297 | with: 298 | html-path: './_build/html' 299 | exclude-warning: 'UserWarning' # Exclude problematic upstream warnings 300 | fail-on-warning: 'true' 301 | ``` 302 | 303 | ## Example 6c: Multiple Warning Exclusions 304 | 305 | ```yaml 306 | name: Check with Multiple Warning Exclusions 307 | 308 | on: 309 | pull_request: 310 | branches: [ main ] 311 | 312 | jobs: 313 | warning-check-multiple-exclusions: 314 | runs-on: ubuntu-latest 315 | 316 | steps: 317 | - name: Checkout repository 318 | uses: actions/checkout@v4 319 | 320 | - name: Build documentation 321 | run: | 322 | jupyter-book build . 323 | 324 | - name: Check for warnings excluding multiple types 325 | uses: QuantEcon/meta/.github/actions/check-warnings@main 326 | with: 327 | html-path: './_build/html' 328 | exclude-warning: 'UserWarning,RuntimeWarning,ResourceWarning' # Exclude multiple warning types 329 | fail-on-warning: 'true' 330 | create-artifact: 'true' 331 | artifact-name: 'filtered-warning-report' 332 | ``` 333 | 334 | ## Example 6d: Custom Warnings with Exclusions 335 | 336 | ```yaml 337 | name: Custom Warnings with Exclusions 338 | 339 | on: 340 | schedule: 341 | - cron: '0 2 * * 1' # Weekly on Monday 342 | 343 | jobs: 344 | custom-warning-check-with-exclusions: 345 | runs-on: ubuntu-latest 346 | 347 | steps: 348 | - name: Checkout repository 349 | uses: actions/checkout@v4 350 | 351 | - name: Build project 352 | run: | 353 | make build 354 | 355 | - name: Check for specific warnings but exclude problematic ones 356 | uses: QuantEcon/meta/.github/actions/check-warnings@main 357 | with: 358 | html-path: './output' 359 | warnings: 'UserWarning,DeprecationWarning,RuntimeWarning,FutureWarning,ResourceWarning' 360 | exclude-warning: 'ResourceWarning,RuntimeWarning' # Exclude known upstream issues 361 | fail-on-warning: 'false' 362 | create-issue: 'true' 363 | issue-title: 'Critical Python Warnings Found (Filtered)' 364 | notify: 'team-lead' 365 | ``` 366 | 367 | ## Example 7: Matrix Strategy 368 | 369 | ```yaml 370 | name: Multi-version Warning Check 371 | 372 | on: 373 | schedule: 374 | - cron: '0 2 * * 1' # Weekly on Monday 375 | 376 | jobs: 377 | warning-check-matrix: 378 | runs-on: ubuntu-latest 379 | strategy: 380 | matrix: 381 | python-version: ['3.9', '3.10', '3.11', '3.12'] 382 | 383 | steps: 384 | - name: Checkout repository 385 | uses: actions/checkout@v4 386 | 387 | - name: Set up Python ${{ matrix.python-version }} 388 | uses: actions/setup-python@v4 389 | with: 390 | python-version: ${{ matrix.python-version }} 391 | 392 | - name: Install dependencies 393 | run: | 394 | pip install -r requirements.txt 395 | 396 | - name: Build documentation 397 | run: | 398 | jupyter-book build . 399 | 400 | - name: Check for warnings 401 | uses: QuantEcon/meta/.github/actions/check-warnings@main 402 | with: 403 | html-path: './_build/html' 404 | # Uses comprehensive default warnings (all Python warning types) 405 | fail-on-warning: 'false' 406 | 407 | - name: Upload HTML artifacts if warnings found 408 | if: steps.warning-check.outputs.warnings-found == 'true' 409 | uses: actions/upload-artifact@v4 410 | with: 411 | name: html-with-warnings-py${{ matrix.python-version }} 412 | path: ./_build/html 413 | ``` 414 | 415 | ## Tips for Usage 416 | 417 | 1. **Place the warning check after your build step**: The action needs the final HTML output to scan. 418 | 419 | 2. **Use `fail-on-warning: 'false'` for reporting**: If you want to report warnings without failing the workflow. 420 | 421 | 3. **Customize warning types**: Adjust the `warnings` input to match your project's needs. 422 | 423 | 4. **Path considerations**: Make sure the `html-path` points to where your build process outputs HTML files. 424 | 425 | 5. **Integration with existing workflows**: This action can be easily added to existing CI/CD pipelines. -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Check for Python Warnings' 2 | description: 'Scan HTML files for Python warnings within code cell outputs (avoiding false positives from text content)' 3 | author: 'QuantEcon' 4 | 5 | inputs: 6 | html-path: 7 | description: 'Path to directory containing HTML files to scan' 8 | required: false 9 | default: '.' 10 | warnings: 11 | description: 'Comma-separated list of warnings to check for' 12 | required: false 13 | default: 'UserWarning,DeprecationWarning,PendingDeprecationWarning,SyntaxWarning,RuntimeWarning,FutureWarning,ImportWarning,UnicodeWarning,BytesWarning,ResourceWarning,EncodingWarning' 14 | exclude-warning: 15 | description: 'Comma-separated list of warnings to exclude from checking (can be a single warning or multiple warnings)' 16 | required: false 17 | default: '' 18 | fail-on-warning: 19 | description: 'Whether to fail the workflow if warnings are found' 20 | required: false 21 | default: 'true' 22 | create-issue: 23 | description: 'Whether to create a GitHub issue when warnings are found' 24 | required: false 25 | default: 'false' 26 | issue-title: 27 | description: 'Title for the GitHub issue when warnings are found' 28 | required: false 29 | default: 'Python Warnings Found in Documentation Build' 30 | create-artifact: 31 | description: 'Whether to create a workflow artifact with the warning report' 32 | required: false 33 | default: 'false' 34 | artifact-name: 35 | description: 'Name for the workflow artifact containing the warning report' 36 | required: false 37 | default: 'warning-report' 38 | notify: 39 | description: 'GitHub username(s) to assign to the created issue (comma-separated for multiple users)' 40 | required: false 41 | default: '' 42 | 43 | outputs: 44 | warnings-found: 45 | description: 'Whether warnings were found (true/false)' 46 | value: ${{ steps.check.outputs.warnings-found }} 47 | warning-count: 48 | description: 'Number of warnings found' 49 | value: ${{ steps.check.outputs.warning-count }} 50 | warning-details: 51 | description: 'Details of warnings found' 52 | value: ${{ steps.check.outputs.warning-details }} 53 | issue-url: 54 | description: 'URL of the created GitHub issue (if create-issue is enabled)' 55 | value: ${{ steps.create-issue.outputs.issue-url }} 56 | artifact-path: 57 | description: 'Path to the created artifact file (if create-artifact is enabled)' 58 | value: ${{ steps.create-artifact.outputs.artifact-path }} 59 | 60 | runs: 61 | using: 'composite' 62 | steps: 63 | - name: Check for warnings 64 | id: check 65 | shell: bash 66 | run: | 67 | # Parse inputs 68 | HTML_PATH="${{ inputs.html-path }}" 69 | WARNINGS="${{ inputs.warnings }}" 70 | EXCLUDE_WARNINGS="${{ inputs.exclude-warning }}" 71 | FAIL_ON_WARNING="${{ inputs.fail-on-warning }}" 72 | 73 | echo "Scanning HTML files in: $HTML_PATH" 74 | echo "Looking for warnings: $WARNINGS" 75 | 76 | # Convert comma-separated warnings to array 77 | IFS=',' read -ra WARNING_ARRAY <<< "$WARNINGS" 78 | 79 | # Handle exclude-warning parameter 80 | if [ -n "$EXCLUDE_WARNINGS" ]; then 81 | echo "Excluding warnings: $EXCLUDE_WARNINGS" 82 | # Convert comma-separated exclude warnings to array 83 | IFS=',' read -ra EXCLUDE_ARRAY <<< "$EXCLUDE_WARNINGS" 84 | 85 | # Create a new array with warnings not in exclude list 86 | FILTERED_WARNING_ARRAY=() 87 | for warning in "${WARNING_ARRAY[@]}"; do 88 | # Remove leading/trailing whitespace from warning 89 | warning=$(echo "$warning" | xargs) 90 | exclude_warning=false 91 | 92 | # Check if this warning should be excluded 93 | for exclude in "${EXCLUDE_ARRAY[@]}"; do 94 | # Remove leading/trailing whitespace from exclude warning 95 | exclude=$(echo "$exclude" | xargs) 96 | if [ "$warning" = "$exclude" ]; then 97 | exclude_warning=true 98 | break 99 | fi 100 | done 101 | 102 | # Add to filtered array if not excluded 103 | if [ "$exclude_warning" = false ]; then 104 | FILTERED_WARNING_ARRAY+=("$warning") 105 | fi 106 | done 107 | 108 | # Replace WARNING_ARRAY with filtered array 109 | WARNING_ARRAY=("${FILTERED_WARNING_ARRAY[@]}") 110 | 111 | # Show final warning list 112 | if [ ${#WARNING_ARRAY[@]} -eq 0 ]; then 113 | echo "⚠️ All warnings have been excluded. No warnings will be checked." 114 | else 115 | echo "Final warning list after exclusions: ${WARNING_ARRAY[*]}" 116 | fi 117 | fi 118 | 119 | # Initialize counters 120 | TOTAL_WARNINGS=0 121 | WARNING_DETAILS="" 122 | WARNINGS_FOUND="false" 123 | DETAILED_REPORT="" 124 | 125 | # Find all HTML files 126 | if [ ! -e "$HTML_PATH" ]; then 127 | echo "Error: HTML path '$HTML_PATH' does not exist" 128 | exit 1 129 | fi 130 | 131 | # Determine if we're dealing with a file or directory 132 | if [ -f "$HTML_PATH" ]; then 133 | # Single file 134 | if [[ "$HTML_PATH" == *.html ]]; then 135 | echo "Checking single HTML file: $HTML_PATH" 136 | FILES=("$HTML_PATH") 137 | else 138 | echo "Error: '$HTML_PATH' is not an HTML file" 139 | exit 1 140 | fi 141 | else 142 | # Directory - find all HTML files 143 | mapfile -d '' FILES < <(find "$HTML_PATH" -name "*.html" -type f -print0) 144 | fi 145 | 146 | # Create temporary Python script for parsing HTML 147 | echo 'import re' > /tmp/check_warnings.py 148 | echo 'import sys' >> /tmp/check_warnings.py 149 | echo 'import os' >> /tmp/check_warnings.py 150 | echo '' >> /tmp/check_warnings.py 151 | echo 'def find_warnings_in_cell_outputs(file_path, warning_text):' >> /tmp/check_warnings.py 152 | echo ' try:' >> /tmp/check_warnings.py 153 | echo ' with open(file_path, "r", encoding="utf-8") as f:' >> /tmp/check_warnings.py 154 | echo ' content = f.read()' >> /tmp/check_warnings.py 155 | echo ' ' >> /tmp/check_warnings.py 156 | echo ' # Find all HTML elements with cell_output in the class attribute' >> /tmp/check_warnings.py 157 | echo ' pattern = r"<([^>]+)\s+class=\"[^\"]*cell_output[^\"]*\"[^>]*>(.*?)"' >> /tmp/check_warnings.py 158 | echo ' ' >> /tmp/check_warnings.py 159 | echo ' matches = []' >> /tmp/check_warnings.py 160 | echo ' ' >> /tmp/check_warnings.py 161 | echo ' # Search for cell_output blocks' >> /tmp/check_warnings.py 162 | echo ' for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE):' >> /tmp/check_warnings.py 163 | echo ' block_content = match.group(2)' >> /tmp/check_warnings.py 164 | echo ' block_start = match.start()' >> /tmp/check_warnings.py 165 | echo ' ' >> /tmp/check_warnings.py 166 | echo ' # Count line number where this block starts' >> /tmp/check_warnings.py 167 | echo ' block_line = content[:block_start].count("\\n") + 1' >> /tmp/check_warnings.py 168 | echo ' ' >> /tmp/check_warnings.py 169 | echo ' # Search for warning within this block' >> /tmp/check_warnings.py 170 | echo ' if warning_text in block_content:' >> /tmp/check_warnings.py 171 | echo ' # Find specific lines within the block that contain the warning' >> /tmp/check_warnings.py 172 | echo ' block_lines = block_content.split("\\n")' >> /tmp/check_warnings.py 173 | echo ' for i, line in enumerate(block_lines):' >> /tmp/check_warnings.py 174 | echo ' if warning_text in line:' >> /tmp/check_warnings.py 175 | echo ' actual_line_num = block_line + i' >> /tmp/check_warnings.py 176 | echo ' # Clean up the line for display (remove extra whitespace, HTML tags)' >> /tmp/check_warnings.py 177 | echo ' clean_line = re.sub(r"<[^>]+>", "", line).strip()' >> /tmp/check_warnings.py 178 | echo ' if clean_line: # Only add non-empty lines' >> /tmp/check_warnings.py 179 | echo ' matches.append(f"{actual_line_num}:{clean_line}")' >> /tmp/check_warnings.py 180 | echo ' ' >> /tmp/check_warnings.py 181 | echo ' # Output results' >> /tmp/check_warnings.py 182 | echo ' for match in matches:' >> /tmp/check_warnings.py 183 | echo ' print(match)' >> /tmp/check_warnings.py 184 | echo ' ' >> /tmp/check_warnings.py 185 | echo ' except Exception as e:' >> /tmp/check_warnings.py 186 | echo ' print(f"Error processing file: {e}", file=sys.stderr)' >> /tmp/check_warnings.py 187 | echo ' sys.exit(1)' >> /tmp/check_warnings.py 188 | echo '' >> /tmp/check_warnings.py 189 | echo 'if __name__ == "__main__":' >> /tmp/check_warnings.py 190 | echo ' file_path = sys.argv[1]' >> /tmp/check_warnings.py 191 | echo ' warning_text = sys.argv[2]' >> /tmp/check_warnings.py 192 | echo ' find_warnings_in_cell_outputs(file_path, warning_text)' >> /tmp/check_warnings.py 193 | 194 | # Search for warnings in HTML files within cell_output elements 195 | for file in "${FILES[@]}"; do 196 | echo "Checking file: $file" 197 | 198 | # Skip warning check if no warnings to check for 199 | if [ ${#WARNING_ARRAY[@]} -eq 0 ]; then 200 | echo "No warnings to check for in $file (all excluded)" 201 | continue 202 | fi 203 | 204 | for warning in "${WARNING_ARRAY[@]}"; do 205 | # Remove leading/trailing whitespace from warning 206 | warning=$(echo "$warning" | xargs) 207 | 208 | # Run the Python script and capture results 209 | matches=$(python3 /tmp/check_warnings.py "$file" "$warning" 2>/dev/null || true) 210 | 211 | if [ -n "$matches" ]; then 212 | WARNINGS_FOUND="true" 213 | count=$(echo "$matches" | wc -l) 214 | TOTAL_WARNINGS=$((TOTAL_WARNINGS + count)) 215 | 216 | echo "⚠️ Found $count instance(s) of '$warning' in $file:" 217 | echo "$matches" 218 | 219 | # Add to basic details 220 | if [ -n "$WARNING_DETAILS" ]; then 221 | WARNING_DETAILS="$WARNING_DETAILS\n" 222 | fi 223 | WARNING_DETAILS="$WARNING_DETAILS$file: $count instance(s) of '$warning'" 224 | 225 | # Add to detailed report 226 | DETAILED_REPORT="$DETAILED_REPORT## $warning in $file\n\n" 227 | DETAILED_REPORT="$DETAILED_REPORT**Found $count instance(s):**\n\n" 228 | DETAILED_REPORT="$DETAILED_REPORT\`\`\`\n" 229 | DETAILED_REPORT="$DETAILED_REPORT$matches\n" 230 | DETAILED_REPORT="$DETAILED_REPORT\`\`\`\n\n" 231 | fi 232 | done 233 | done 234 | 235 | # Set outputs 236 | echo "warnings-found=$WARNINGS_FOUND" >> $GITHUB_OUTPUT 237 | echo "warning-count=$TOTAL_WARNINGS" >> $GITHUB_OUTPUT 238 | echo "warning-details<> $GITHUB_OUTPUT 239 | echo -e "$WARNING_DETAILS" >> $GITHUB_OUTPUT 240 | echo "EOF" >> $GITHUB_OUTPUT 241 | echo "detailed-report<> $GITHUB_OUTPUT 242 | echo -e "$DETAILED_REPORT" >> $GITHUB_OUTPUT 243 | echo "EOF" >> $GITHUB_OUTPUT 244 | 245 | # Summary 246 | if [ "$WARNINGS_FOUND" = "true" ]; then 247 | echo "❌ Found $TOTAL_WARNINGS warning(s) in HTML files" 248 | echo "::error::Found $TOTAL_WARNINGS Python warning(s) in HTML output" 249 | else 250 | echo "✅ No warnings found in HTML files" 251 | fi 252 | 253 | - name: Post PR comment with warning report 254 | if: inputs.fail-on-warning == 'true' && steps.check.outputs.warnings-found == 'true' && github.event_name == 'pull_request' 255 | uses: actions/github-script@v7 256 | with: 257 | script: | 258 | const warningCount = '${{ steps.check.outputs.warning-count }}'; 259 | const detailedReport = ${{ toJSON(steps.check.outputs.detailed-report) }}; 260 | 261 | const body = [ 262 | '## ⚠️ Python Warnings Detected', 263 | '', 264 | '🚨 **' + warningCount + ' Python warning(s)** were found in the HTML output during the documentation build.', 265 | '', 266 | '**Build Details:**', 267 | '- **Workflow Run:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})', 268 | '- **Commit:** ${{ github.sha }}', 269 | '- **Date:** ' + new Date().toISOString(), 270 | '', 271 | '---', 272 | '', 273 | detailedReport, 274 | '', 275 | '---', 276 | '', 277 | '**Next Steps:**', 278 | '1. Review the warnings listed above', 279 | '2. Fix the underlying code that\'s generating these warnings', 280 | '3. Push the changes to update this PR', 281 | '', 282 | '📝 *This comment was automatically generated by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).*' 283 | ].join('\n'); 284 | 285 | try { 286 | await github.rest.issues.createComment({ 287 | owner: context.repo.owner, 288 | repo: context.repo.repo, 289 | issue_number: context.issue.number, 290 | body: body 291 | }); 292 | console.log('Posted PR comment with warning details'); 293 | } catch (error) { 294 | console.error('Failed to create PR comment:', error); 295 | core.setFailed('Failed to create PR comment: ' + error.message); 296 | } 297 | 298 | - name: Fail workflow on warnings 299 | if: inputs.fail-on-warning == 'true' && steps.check.outputs.warnings-found == 'true' 300 | shell: bash 301 | run: | 302 | echo "Failing workflow due to warnings found" 303 | exit 1 304 | 305 | - name: Create artifact with warning report 306 | id: create-artifact 307 | if: inputs.create-artifact == 'true' && steps.check.outputs.warnings-found == 'true' 308 | shell: bash 309 | run: | 310 | ARTIFACT_NAME="${{ inputs.artifact-name }}" 311 | ARTIFACT_FILE="$ARTIFACT_NAME.md" 312 | CURRENT_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC') 313 | 314 | # Create the report file 315 | { 316 | echo "# Python Warning Report" 317 | echo "" 318 | echo "**Date:** $CURRENT_DATE" 319 | echo "**Repository:** ${{ github.repository }}" 320 | echo "**Workflow:** ${{ github.workflow }}" 321 | echo "**Run ID:** ${{ github.run_id }}" 322 | echo "**Total Warnings Found:** ${{ steps.check.outputs.warning-count }}" 323 | echo "" 324 | echo "---" 325 | echo "" 326 | echo "${{ steps.check.outputs.detailed-report }}" 327 | echo "" 328 | echo "---" 329 | echo "" 330 | echo "Generated by [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings)" 331 | } > "$ARTIFACT_FILE" 332 | 333 | echo "artifact-path=$ARTIFACT_FILE" >> $GITHUB_OUTPUT 334 | echo "Created warning report artifact: $ARTIFACT_FILE" 335 | 336 | - name: Upload warning report artifact 337 | if: inputs.create-artifact == 'true' && steps.check.outputs.warnings-found == 'true' 338 | uses: actions/upload-artifact@v4 339 | with: 340 | name: ${{ inputs.artifact-name }} 341 | path: ${{ steps.create-artifact.outputs.artifact-path }} 342 | retention-days: 30 343 | 344 | - name: Create GitHub issue 345 | id: create-issue 346 | if: inputs.create-issue == 'true' && steps.check.outputs.warnings-found == 'true' 347 | uses: actions/github-script@v7 348 | with: 349 | script: | 350 | const warningCount = '${{ steps.check.outputs.warning-count }}'; 351 | const detailedReport = ${{ toJSON(steps.check.outputs.detailed-report) }}; 352 | const title = '${{ inputs.issue-title }}'; 353 | const notify = '${{ inputs.notify }}'; 354 | 355 | const body = [ 356 | '# Python Warnings Detected', 357 | '', 358 | '🚨 **' + warningCount + ' Python warning(s)** were found in the HTML output during the documentation build.', 359 | '', 360 | '**Details:**', 361 | '- **Repository:** ${{ github.repository }}', 362 | '- **Workflow:** ${{ github.workflow }}', 363 | '- **Run ID:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})', 364 | '- **Commit:** ${{ github.sha }}', 365 | '- **Branch:** ${{ github.ref_name }}', 366 | '- **Date:** ' + new Date().toISOString(), 367 | '', 368 | '---', 369 | '', 370 | detailedReport, 371 | '', 372 | '---', 373 | '', 374 | '**Next Steps:**', 375 | '1. Review the warnings listed above', 376 | '2. Fix the underlying code that\'s generating these warnings', 377 | '3. Re-run the build to verify the warnings are resolved', 378 | '', 379 | '**Note:** This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).', 380 | '', 381 | 'Please close this issue once all warnings have been addressed.' 382 | ].join('\n'); 383 | 384 | try { 385 | const response = await github.rest.issues.create({ 386 | owner: context.repo.owner, 387 | repo: context.repo.repo, 388 | title: title, 389 | body: body, 390 | labels: ['bug', 'execution', 'python-warnings'] 391 | }); 392 | 393 | const issueUrl = response.data.html_url; 394 | const issueNumber = response.data.number; 395 | console.log('Created issue: ' + issueUrl); 396 | core.setOutput('issue-url', issueUrl); 397 | 398 | // Assign users to the issue if notify parameter is provided 399 | if (notify && notify.trim()) { 400 | try { 401 | // Parse comma-separated usernames and clean them 402 | const assignees = notify.split(',') 403 | .map(username => username.trim()) 404 | .filter(username => username.length > 0); 405 | 406 | if (assignees.length > 0) { 407 | console.log('Assigning issue to users: ' + assignees.join(', ')); 408 | 409 | await github.rest.issues.addAssignees({ 410 | owner: context.repo.owner, 411 | repo: context.repo.repo, 412 | issue_number: issueNumber, 413 | assignees: assignees 414 | }); 415 | 416 | console.log('Successfully assigned issue to: ' + assignees.join(', ')); 417 | } 418 | } catch (assignError) { 419 | console.error('Failed to assign users to issue:', assignError); 420 | // Don't fail the entire workflow for assignment errors 421 | console.log('Issue was created successfully, but assignment failed. Users may need to be assigned manually.'); 422 | } 423 | } 424 | 425 | return issueUrl; 426 | } catch (error) { 427 | console.error('Failed to create issue:', error); 428 | core.setFailed('Failed to create issue: ' + error.message); 429 | } 430 | 431 | - name: Post simple PR comment linking to issue 432 | if: inputs.create-issue == 'true' && steps.check.outputs.warnings-found == 'true' && github.event_name == 'pull_request' 433 | uses: actions/github-script@v7 434 | with: 435 | script: | 436 | const warningDetails = ${{ toJSON(steps.check.outputs.warning-details) }}; 437 | const issueUrl = '${{ steps.create-issue.outputs.issue-url }}'; 438 | 439 | // Extract file names from warning details 440 | const files = []; 441 | const lines = warningDetails.split('\n'); 442 | for (const line of lines) { 443 | if (line.includes(': ') && line.includes('instance(s) of')) { 444 | const fileName = line.split(':')[0].trim(); 445 | if (fileName && !files.includes(fileName)) { 446 | files.push(fileName); 447 | } 448 | } 449 | } 450 | 451 | const fileList = files.map(file => `- ${file}`).join('\n'); 452 | 453 | const body = [ 454 | 'There were code execution warnings found in the following files:', 455 | '', 456 | fileList, 457 | '', 458 | `For further details please check ${issueUrl}`, 459 | '', 460 | 'Note: This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).' 461 | ].join('\n'); 462 | 463 | try { 464 | await github.rest.issues.createComment({ 465 | owner: context.repo.owner, 466 | repo: context.repo.repo, 467 | issue_number: context.issue.number, 468 | body: body 469 | }); 470 | console.log('Posted simple PR comment linking to issue'); 471 | } catch (error) { 472 | console.error('Failed to create PR comment:', error); 473 | core.setFailed('Failed to create PR comment: ' + error.message); 474 | } 475 | 476 | branding: 477 | icon: 'alert-triangle' 478 | color: 'orange' --------------------------------------------------------------------------------