├── 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 | [](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[^\"]*\"[^>]*>(.*?)\1>"' >> /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'
--------------------------------------------------------------------------------