From 362a71183ca770fd2e806a81ed849e7aca1fc0f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:36:43 +0000 Subject: [PATCH 01/13] Initial plan From 88a80875054bffefe2cb90e6768e7387f6adac08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:42:43 +0000 Subject: [PATCH 02/13] Implement GitHub action to check for Python warnings in HTML output Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 125 ++++++++++++++ .github/actions/check-warnings/action.yml | 124 ++++++++++++++ .github/actions/check-warnings/examples.md | 189 +++++++++++++++++++++ .github/workflows/test-warning-check.yml | 85 +++++++++ README.md | 24 +++ test/html-samples/clean.html | 16 ++ test/html-samples/with-warnings.html | 27 +++ 7 files changed, 590 insertions(+) create mode 100644 .github/actions/check-warnings/README.md create mode 100644 .github/actions/check-warnings/action.yml create mode 100644 .github/actions/check-warnings/examples.md create mode 100644 .github/workflows/test-warning-check.yml create mode 100644 test/html-samples/clean.html create mode 100644 test/html-samples/with-warnings.html diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md new file mode 100644 index 0000000..457873a --- /dev/null +++ b/.github/actions/check-warnings/README.md @@ -0,0 +1,125 @@ +# Check for Python Warnings Action + +This GitHub Action scans HTML files for Python warnings and optionally fails the workflow if any are found. It's designed to be used after building documentation or running code that generates HTML output, to ensure that no warnings are present in the final output. + +## Features + +- Scans HTML files for configurable Python warnings +- Supports multiple warning types (SyntaxWarning, DeprecationWarning, FutureWarning) +- Provides detailed output about warnings found +- Optionally fails the workflow when warnings are detected +- Configurable search path and warning types + +## Usage + +### Basic Usage + +```yaml +- name: Check for Python warnings + uses: QuantEcon/meta/.github/actions/check-warnings@main +``` + +### Advanced Usage + +```yaml +- name: Check for Python warnings in build output + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning,UserWarning' + fail-on-warning: 'true' +``` + +### Using Outputs + +```yaml +- name: Check for Python warnings + id: warning-check + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + fail-on-warning: 'false' + +- name: Report warnings + if: steps.warning-check.outputs.warnings-found == 'true' + run: | + echo "Found ${{ steps.warning-check.outputs.warning-count }} warnings:" + echo "${{ steps.warning-check.outputs.warning-details }}" +``` + +## Inputs + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `html-path` | Path to directory containing HTML files to scan | No | `.` | +| `warnings` | Comma-separated list of warnings to check for | No | `SyntaxWarning,DeprecationWarning,FutureWarning` | +| `fail-on-warning` | Whether to fail the workflow if warnings are found | No | `true` | + +## Outputs + +| Output | Description | +|--------|-------------| +| `warnings-found` | Whether warnings were found (`true`/`false`) | +| `warning-count` | Number of warnings found | +| `warning-details` | Details of warnings found | + +## Example Workflow + +Here's a complete example of how to use this action in a workflow: + +```yaml +name: Build and Check Documentation + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + pip install -r requirements.txt + + - name: Build documentation + run: | + jupyter-book build . + + - name: Check for Python warnings + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'true' +``` + +## Use Case + +This action is particularly useful for: + +1. **Documentation builds**: After building Jupyter Books or Sphinx documentation, check that no Python warnings appear in the generated HTML +2. **Code execution**: When running notebooks or Python scripts that generate HTML output, ensure no warnings are present +3. **Continuous Integration**: Maintain code quality by preventing warnings from being introduced + +## How It Works + +1. The action searches for all `.html` files in the specified directory +2. For each HTML file, it searches for the specified warning strings +3. If warnings are found, it reports the details and optionally fails the workflow +4. The action provides outputs that can be used by subsequent steps + +## Error Handling + +- If the specified HTML path doesn't exist, the action will fail with an error +- The action will report the exact location (file and line number) where warnings are found +- When `fail-on-warning` is `true`, the workflow will fail if any warnings are detected \ No newline at end of file diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml new file mode 100644 index 0000000..9fab62c --- /dev/null +++ b/.github/actions/check-warnings/action.yml @@ -0,0 +1,124 @@ +name: 'Check for Python Warnings' +description: 'Scan HTML files for Python warnings and fail if found' +author: 'QuantEcon' + +inputs: + html-path: + description: 'Path to directory containing HTML files to scan' + required: false + default: '.' + warnings: + description: 'Comma-separated list of warnings to check for' + required: false + default: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: + description: 'Whether to fail the workflow if warnings are found' + required: false + default: 'true' + +outputs: + warnings-found: + description: 'Whether warnings were found (true/false)' + value: ${{ steps.check.outputs.warnings-found }} + warning-count: + description: 'Number of warnings found' + value: ${{ steps.check.outputs.warning-count }} + warning-details: + description: 'Details of warnings found' + value: ${{ steps.check.outputs.warning-details }} + +runs: + using: 'composite' + steps: + - name: Check for warnings + id: check + shell: bash + run: | + # Parse inputs + HTML_PATH="${{ inputs.html-path }}" + WARNINGS="${{ inputs.warnings }}" + FAIL_ON_WARNING="${{ inputs.fail-on-warning }}" + + echo "Scanning HTML files in: $HTML_PATH" + echo "Looking for warnings: $WARNINGS" + + # Convert comma-separated warnings to array + IFS=',' read -ra WARNING_ARRAY <<< "$WARNINGS" + + # Initialize counters + TOTAL_WARNINGS=0 + WARNING_DETAILS="" + WARNINGS_FOUND="false" + + # Find all HTML files + if [ ! -e "$HTML_PATH" ]; then + echo "Error: HTML path '$HTML_PATH' does not exist" + exit 1 + fi + + # Determine if we're dealing with a file or directory + if [ -f "$HTML_PATH" ]; then + # Single file + if [[ "$HTML_PATH" == *.html ]]; then + echo "Checking single HTML file: $HTML_PATH" + FILES=("$HTML_PATH") + else + echo "Error: '$HTML_PATH' is not an HTML file" + exit 1 + fi + else + # Directory - find all HTML files + mapfile -d '' FILES < <(find "$HTML_PATH" -name "*.html" -type f -print0) + fi + + # Search for warnings in HTML files + for file in "${FILES[@]}"; do + echo "Checking file: $file" + + for warning in "${WARNING_ARRAY[@]}"; do + # Remove leading/trailing whitespace from warning + warning=$(echo "$warning" | xargs) + + # Search for the warning in the file + matches=$(grep -n "$warning" "$file" 2>/dev/null || true) + + if [ -n "$matches" ]; then + WARNINGS_FOUND="true" + count=$(echo "$matches" | wc -l) + TOTAL_WARNINGS=$((TOTAL_WARNINGS + count)) + + echo "⚠️ Found $count instance(s) of '$warning' in $file:" + echo "$matches" + + # Add to details + if [ -n "$WARNING_DETAILS" ]; then + WARNING_DETAILS="$WARNING_DETAILS\n" + fi + WARNING_DETAILS="$WARNING_DETAILS$file: $count instance(s) of '$warning'" + fi + done + done + + # Set outputs + echo "warnings-found=$WARNINGS_FOUND" >> $GITHUB_OUTPUT + echo "warning-count=$TOTAL_WARNINGS" >> $GITHUB_OUTPUT + echo "warning-details<> $GITHUB_OUTPUT + echo -e "$WARNING_DETAILS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Summary + if [ "$WARNINGS_FOUND" = "true" ]; then + echo "❌ Found $TOTAL_WARNINGS warning(s) in HTML files" + echo "::error::Found $TOTAL_WARNINGS Python warning(s) in HTML output" + + if [ "$FAIL_ON_WARNING" = "true" ]; then + echo "Failing workflow due to warnings found" + exit 1 + fi + else + echo "✅ No warnings found in HTML files" + fi + +branding: + icon: 'alert-triangle' + color: 'orange' \ No newline at end of file diff --git a/.github/actions/check-warnings/examples.md b/.github/actions/check-warnings/examples.md new file mode 100644 index 0000000..54b5766 --- /dev/null +++ b/.github/actions/check-warnings/examples.md @@ -0,0 +1,189 @@ +# Example: Using the Warning Check Action + +This directory contains examples of how to use the `check-warnings` action in different scenarios. + +## Example 1: Basic Jupyter Book Build + +```yaml +name: Build and Check Jupyter Book + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + pip install jupyter-book + pip install -r requirements.txt + + - name: Build Jupyter Book + run: | + jupyter-book build . + + - name: Check for Python warnings in built HTML + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'true' +``` + +## Example 2: Non-failing Check with Reporting + +```yaml +name: Build with Warning Report + +on: + push: + branches: [ main ] + +jobs: + build-with-warning-report: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build documentation + run: | + # Your build process here + make html + + - name: Check for warnings (non-failing) + id: warning-check + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './docs/_build/html' + fail-on-warning: 'false' + + - name: Create warning report + if: steps.warning-check.outputs.warnings-found == 'true' + run: | + echo "## Warning Report" >> $GITHUB_STEP_SUMMARY + echo "Found ${{ steps.warning-check.outputs.warning-count }} warnings:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "${{ steps.warning-check.outputs.warning-details }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + - name: Post warning comment (for PRs) + if: github.event_name == 'pull_request' && steps.warning-check.outputs.warnings-found == 'true' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + 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```' + }) +``` + +## Example 3: Custom Warning Types + +```yaml +name: Check for Custom Warnings + +on: + workflow_dispatch: + inputs: + custom_warnings: + description: 'Custom warnings to check for' + required: false + default: 'UserWarning,RuntimeWarning,ResourceWarning' + +jobs: + custom-warning-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build project + run: | + # Your build process + make build + + - name: Check for custom warnings + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './output' + warnings: ${{ github.event.inputs.custom_warnings || 'SyntaxWarning,DeprecationWarning,FutureWarning' }} + fail-on-warning: 'true' +``` + +## Example 4: Matrix Strategy + +```yaml +name: Multi-version Warning Check + +on: + schedule: + - cron: '0 2 * * 1' # Weekly on Monday + +jobs: + warning-check-matrix: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12'] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + pip install -r requirements.txt + + - name: Build documentation + run: | + jupyter-book build . + + - name: Check for warnings + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'false' + + - name: Upload HTML artifacts if warnings found + if: steps.warning-check.outputs.warnings-found == 'true' + uses: actions/upload-artifact@v4 + with: + name: html-with-warnings-py${{ matrix.python-version }} + path: ./_build/html +``` + +## Tips for Usage + +1. **Place the warning check after your build step**: The action needs the final HTML output to scan. + +2. **Use `fail-on-warning: 'false'` for reporting**: If you want to report warnings without failing the workflow. + +3. **Customize warning types**: Adjust the `warnings` input to match your project's needs. + +4. **Path considerations**: Make sure the `html-path` points to where your build process outputs HTML files. + +5. **Integration with existing workflows**: This action can be easily added to existing CI/CD pipelines. \ No newline at end of file diff --git a/.github/workflows/test-warning-check.yml b/.github/workflows/test-warning-check.yml new file mode 100644 index 0000000..21f7e68 --- /dev/null +++ b/.github/workflows/test-warning-check.yml @@ -0,0 +1,85 @@ +name: Test Warning Check Action + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + test-clean-files: + runs-on: ubuntu-latest + name: Test with clean HTML files + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Test action with clean files + id: clean-test + uses: .//.github/actions/check-warnings + with: + html-path: './test/html-samples/clean.html' + fail-on-warning: 'false' + + - name: Verify clean results + run: | + echo "Warnings found: ${{ steps.clean-test.outputs.warnings-found }}" + echo "Warning count: ${{ steps.clean-test.outputs.warning-count }}" + if [ "${{ steps.clean-test.outputs.warnings-found }}" != "false" ]; then + echo "❌ Expected no warnings but found some" + exit 1 + fi + echo "✅ Clean test passed" + + test-files-with-warnings: + runs-on: ubuntu-latest + name: Test with HTML files containing warnings + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Test action with files containing warnings + id: warning-test + uses: .//.github/actions/check-warnings + with: + html-path: './test/html-samples' + fail-on-warning: 'false' + + - name: Verify warning results + run: | + echo "Warnings found: ${{ steps.warning-test.outputs.warnings-found }}" + echo "Warning count: ${{ steps.warning-test.outputs.warning-count }}" + echo "Warning details: ${{ steps.warning-test.outputs.warning-details }}" + if [ "${{ steps.warning-test.outputs.warnings-found }}" != "true" ]; then + echo "❌ Expected warnings but found none" + exit 1 + fi + if [ "${{ steps.warning-test.outputs.warning-count }}" -lt "3" ]; then + echo "❌ Expected at least 3 warnings but found ${{ steps.warning-test.outputs.warning-count }}" + exit 1 + fi + echo "✅ Warning test passed" + + test-fail-on-warning: + runs-on: ubuntu-latest + name: Test fail-on-warning functionality + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Test that action fails when warnings found and fail-on-warning is true + id: fail-test + continue-on-error: true + uses: .//.github/actions/check-warnings + with: + html-path: './test/html-samples/with-warnings.html' + fail-on-warning: 'true' + + - name: Verify action failed + run: | + if [ "${{ steps.fail-test.outcome }}" != "failure" ]; then + echo "❌ Expected action to fail but it succeeded" + exit 1 + fi + echo "✅ Fail-on-warning test passed" \ No newline at end of file diff --git a/README.md b/README.md index bf878f7..217b91d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # meta For issues and discussion covering more than one repository + +## GitHub Actions + +This repository contains reusable GitHub Actions for QuantEcon projects: + +### Check Warnings Action + +A GitHub Action that scans HTML files for Python warnings and optionally fails the workflow if any are found. + +**Location**: `.github/actions/check-warnings` + +**Usage**: +```yaml +- name: Check for Python warnings + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'true' +``` + +**Use case**: Ideal for checking Jupyter Book builds or any HTML output from Python code execution to ensure no warnings are present in the final documentation. + +See the [action documentation](./.github/actions/check-warnings/README.md) for detailed usage instructions and examples. diff --git a/test/html-samples/clean.html b/test/html-samples/clean.html new file mode 100644 index 0000000..52d0c9e --- /dev/null +++ b/test/html-samples/clean.html @@ -0,0 +1,16 @@ + + + + Clean Code Output + + +

Test Output

+
+
+Running code...
+Result: 42
+Success!
+        
+
+ + \ No newline at end of file diff --git a/test/html-samples/with-warnings.html b/test/html-samples/with-warnings.html new file mode 100644 index 0000000..dabd823 --- /dev/null +++ b/test/html-samples/with-warnings.html @@ -0,0 +1,27 @@ + + + + Code Output with Warnings + + +

Test Output with Warnings

+
+
+Running code...
+/path/to/file.py:15: DeprecationWarning: This function is deprecated
+  result = old_function()
+/path/to/file.py:25: SyntaxWarning: invalid escape sequence '\d'
+  pattern = '\d+'
+Result: 42
+        
+
+
+
+Another execution...
+/path/to/another.py:10: FutureWarning: This will change in future versions
+  new_behavior = True
+Done!
+        
+
+ + \ No newline at end of file From 6ef012486bc4265872d318215fd57ac224500576 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 06:20:54 +0000 Subject: [PATCH 03/13] Add GitHub issue creation and artifact generation features to check-warnings action Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 129 +++++++++++++++++++- .github/actions/check-warnings/action.yml | 130 +++++++++++++++++++- .github/actions/check-warnings/examples.md | 132 ++++++++++++++++++++- 3 files changed, 383 insertions(+), 8 deletions(-) diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md index 457873a..25836ae 100644 --- a/.github/actions/check-warnings/README.md +++ b/.github/actions/check-warnings/README.md @@ -8,6 +8,8 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the - Supports multiple warning types (SyntaxWarning, DeprecationWarning, FutureWarning) - Provides detailed output about warnings found - Optionally fails the workflow when warnings are detected +- **Creates GitHub issues** with detailed warning reports +- **Generates workflow artifacts** containing warning reports - Configurable search path and warning types ## Usage @@ -19,15 +21,45 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the uses: QuantEcon/meta/.github/actions/check-warnings@main ``` -### Advanced Usage +### Advanced Usage with Issue Creation ```yaml -- name: Check for Python warnings in build output +- name: Check for Python warnings with issue creation uses: QuantEcon/meta/.github/actions/check-warnings@main with: html-path: './_build/html' warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning,UserWarning' + fail-on-warning: 'false' + create-issue: 'true' + issue-title: 'Python Warnings Found in Documentation Build' +``` + +### Advanced Usage with Artifact Creation + +```yaml +- name: Check for Python warnings with artifact + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' fail-on-warning: 'true' + create-artifact: 'true' + artifact-name: 'python-warning-report' +``` + +### Complete Advanced Usage + +```yaml +- name: Check for Python warnings in build output + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning,UserWarning' + fail-on-warning: 'false' + create-issue: 'true' + issue-title: 'Python Warnings Detected in Build' + create-artifact: 'true' + artifact-name: 'detailed-warning-report' ``` ### Using Outputs @@ -46,6 +78,54 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the echo "${{ steps.warning-check.outputs.warning-details }}" ``` +## New Features + +### GitHub Issue Creation + +When `create-issue` is set to `true`, the action will automatically create a GitHub issue when warnings are detected. The issue includes: + +- Detailed warning information with file paths and line numbers +- Repository and workflow context +- Direct links to the failing workflow run +- Suggested next steps for resolution +- Automatic labeling (`bug`, `documentation`, `python-warnings`) + +### Workflow Artifacts + +When `create-artifact` is set to `true`, the action generates a detailed Markdown report as a workflow artifact. This report includes: + +- Complete warning details in a readable format +- Repository and workflow metadata +- Timestamp and commit information +- Downloadable for offline review + +### Using Both Features Together + +You can enable both issue creation and artifact generation simultaneously: + +```yaml +- name: Comprehensive warning check + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + fail-on-warning: 'false' # Don't fail, just report + create-issue: 'true' # Create issue for tracking + create-artifact: 'true' # Create artifact for detailed review +``` + +## Permissions + +For the action to work correctly with all features, ensure your workflow has the appropriate permissions: + +```yaml +permissions: + contents: read # For checking out the repository + issues: write # For creating GitHub issues (if create-issue is enabled) + actions: read # For creating workflow artifacts (if create-artifact is enabled) +``` + +If you're only using the basic warning check functionality, only `contents: read` is required. + ## Inputs | Input | Description | Required | Default | @@ -53,6 +133,10 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the | `html-path` | Path to directory containing HTML files to scan | No | `.` | | `warnings` | Comma-separated list of warnings to check for | No | `SyntaxWarning,DeprecationWarning,FutureWarning` | | `fail-on-warning` | Whether to fail the workflow if warnings are found | No | `true` | +| `create-issue` | Whether to create a GitHub issue when warnings are found | No | `false` | +| `issue-title` | Title for the GitHub issue when warnings are found | No | `Python Warnings Found in Documentation Build` | +| `create-artifact` | Whether to create a workflow artifact with the warning report | No | `false` | +| `artifact-name` | Name for the workflow artifact containing the warning report | No | `warning-report` | ## Outputs @@ -61,6 +145,8 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the | `warnings-found` | Whether warnings were found (`true`/`false`) | | `warning-count` | Number of warnings found | | `warning-details` | Details of warnings found | +| `issue-url` | URL of the created GitHub issue (if `create-issue` is enabled) | +| `artifact-path` | Path to the created artifact file (if `create-artifact` is enabled) | ## Example Workflow @@ -75,6 +161,11 @@ on: pull_request: branches: [ main ] +permissions: + contents: read + issues: write + actions: read + jobs: build-and-check: runs-on: ubuntu-latest @@ -100,7 +191,10 @@ jobs: with: html-path: './_build/html' warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' - fail-on-warning: 'true' + fail-on-warning: ${{ github.event_name == 'push' }} # Fail on push, warn on PR + create-issue: ${{ github.event_name == 'push' }} # Create issues for main branch + create-artifact: 'true' # Always create artifacts + artifact-name: 'warning-report' ``` ## Use Case @@ -122,4 +216,31 @@ This action is particularly useful for: - If the specified HTML path doesn't exist, the action will fail with an error - The action will report the exact location (file and line number) where warnings are found -- When `fail-on-warning` is `true`, the workflow will fail if any warnings are detected \ No newline at end of file +- When `fail-on-warning` is `true`, the workflow will fail if any warnings are detected + +## Tips for Usage + +1. **Place the warning check after your build step**: The action needs the final HTML output to scan. + +2. **Use `fail-on-warning: 'false'` for reporting**: If you want to report warnings without failing the workflow. + +3. **Customize warning types**: Adjust the `warnings` input to match your project's needs. + +4. **Path considerations**: Make sure the `html-path` points to where your build process outputs HTML files. + +5. **Integration with existing workflows**: This action can be easily added to existing CI/CD pipelines. + +6. **Issue management**: When using `create-issue: 'true'`, consider: + - Setting up issue templates for consistency + - Using branch-specific conditions to avoid duplicate issues + - Implementing automatic issue closing when warnings are resolved + +7. **Artifact usage**: Artifacts are perfect for: + - Detailed offline review of warnings + - Sharing warning reports with team members + - Historical tracking of warning trends + +8. **Performance considerations**: For large HTML output directories, consider: + - Using specific paths rather than scanning entire directories + - Limiting warning types to only those relevant to your project + - Setting appropriate artifact retention periods \ No newline at end of file diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 9fab62c..8d1789e 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -15,6 +15,22 @@ inputs: description: 'Whether to fail the workflow if warnings are found' required: false default: 'true' + create-issue: + description: 'Whether to create a GitHub issue when warnings are found' + required: false + default: 'false' + issue-title: + description: 'Title for the GitHub issue when warnings are found' + required: false + default: 'Python Warnings Found in Documentation Build' + create-artifact: + description: 'Whether to create a workflow artifact with the warning report' + required: false + default: 'false' + artifact-name: + description: 'Name for the workflow artifact containing the warning report' + required: false + default: 'warning-report' outputs: warnings-found: @@ -26,6 +42,12 @@ outputs: warning-details: description: 'Details of warnings found' value: ${{ steps.check.outputs.warning-details }} + issue-url: + description: 'URL of the created GitHub issue (if create-issue is enabled)' + value: ${{ steps.create-issue.outputs.issue-url }} + artifact-path: + description: 'Path to the created artifact file (if create-artifact is enabled)' + value: ${{ steps.create-artifact.outputs.artifact-path }} runs: using: 'composite' @@ -49,6 +71,7 @@ runs: TOTAL_WARNINGS=0 WARNING_DETAILS="" WARNINGS_FOUND="false" + DETAILED_REPORT="" # Find all HTML files if [ ! -e "$HTML_PATH" ]; then @@ -90,11 +113,18 @@ runs: echo "⚠️ Found $count instance(s) of '$warning' in $file:" echo "$matches" - # Add to details + # Add to basic details if [ -n "$WARNING_DETAILS" ]; then WARNING_DETAILS="$WARNING_DETAILS\n" fi WARNING_DETAILS="$WARNING_DETAILS$file: $count instance(s) of '$warning'" + + # Add to detailed report + DETAILED_REPORT="$DETAILED_REPORT## $warning in $file\n\n" + DETAILED_REPORT="$DETAILED_REPORT**Found $count instance(s):**\n\n" + DETAILED_REPORT="$DETAILED_REPORT\`\`\`\n" + DETAILED_REPORT="$DETAILED_REPORT$matches\n" + DETAILED_REPORT="$DETAILED_REPORT\`\`\`\n\n" fi done done @@ -105,6 +135,9 @@ runs: echo "warning-details<> $GITHUB_OUTPUT echo -e "$WARNING_DETAILS" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT + echo "detailed-report<> $GITHUB_OUTPUT + echo -e "$DETAILED_REPORT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT # Summary if [ "$WARNINGS_FOUND" = "true" ]; then @@ -119,6 +152,101 @@ runs: echo "✅ No warnings found in HTML files" fi + - name: Create artifact with warning report + id: create-artifact + if: inputs.create-artifact == 'true' && steps.check.outputs.warnings-found == 'true' + shell: bash + run: | + ARTIFACT_NAME="${{ inputs.artifact-name }}" + ARTIFACT_FILE="$ARTIFACT_NAME.md" + CURRENT_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC') + + # Create the report file + cat > "$ARTIFACT_FILE" << EOF + # Python Warning Report + + **Date:** $CURRENT_DATE + **Repository:** ${{ github.repository }} + **Workflow:** ${{ github.workflow }} + **Run ID:** ${{ github.run_id }} + **Total Warnings Found:** ${{ steps.check.outputs.warning-count }} + + --- + + ${{ steps.check.outputs.detailed-report }} + + --- + + Generated by [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings) + EOF + + echo "artifact-path=$ARTIFACT_FILE" >> $GITHUB_OUTPUT + echo "Created warning report artifact: $ARTIFACT_FILE" + + - name: Upload warning report artifact + if: inputs.create-artifact == 'true' && steps.check.outputs.warnings-found == 'true' + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: ${{ steps.create-artifact.outputs.artifact-path }} + retention-days: 30 + + - name: Create GitHub issue + id: create-issue + if: inputs.create-issue == 'true' && steps.check.outputs.warnings-found == 'true' + uses: actions/github-script@v7 + with: + script: | + const warningCount = '${{ steps.check.outputs.warning-count }}'; + const detailedReport = `${{ steps.check.outputs.detailed-report }}`; + const title = '${{ inputs.issue-title }}'; + + const body = `# Python Warnings Detected + + 🚨 **${{ steps.check.outputs.warning-count }} Python warning(s)** were found in the HTML output during the documentation build. + + **Details:** + - **Repository:** ${{ github.repository }} + - **Workflow:** ${{ github.workflow }} + - **Run ID:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - **Commit:** ${{ github.sha }} + - **Branch:** ${{ github.ref_name }} + - **Date:** ${new Date().toISOString()} + + --- + + ${detailedReport} + + --- + + **Next Steps:** + 1. Review the warnings listed above + 2. Fix the underlying code that's generating these warnings + 3. Re-run the build to verify the warnings are resolved + + **Note:** This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings). + + Please close this issue once all warnings have been addressed.`; + + try { + const response = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: body, + labels: ['bug', 'documentation', 'python-warnings'] + }); + + const issueUrl = response.data.html_url; + console.log(`Created issue: ${issueUrl}`); + core.setOutput('issue-url', issueUrl); + + return issueUrl; + } catch (error) { + console.error('Failed to create issue:', error); + core.setFailed(`Failed to create issue: ${error.message}`); + } + branding: icon: 'alert-triangle' color: 'orange' \ No newline at end of file diff --git a/.github/actions/check-warnings/examples.md b/.github/actions/check-warnings/examples.md index 54b5766..e97d5ab 100644 --- a/.github/actions/check-warnings/examples.md +++ b/.github/actions/check-warnings/examples.md @@ -43,7 +43,133 @@ jobs: fail-on-warning: 'true' ``` -## Example 2: Non-failing Check with Reporting +## Example 2: Non-failing Check with GitHub Issue Creation + +```yaml +name: Build with Issue Creation + +on: + push: + branches: [ main ] + +jobs: + build-with-issue-creation: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build documentation + run: | + # Your build process here + make html + + - name: Check for warnings with issue creation + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './docs/_build/html' + fail-on-warning: 'false' + create-issue: 'true' + issue-title: 'Python Warnings Found in Documentation' +``` + +## Example 3: Check with Artifact Generation + +```yaml +name: Build with Artifact Report + +on: + pull_request: + branches: [ main ] + +jobs: + build-with-artifact: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build documentation + run: | + jupyter-book build . + + - name: Check for warnings with artifact + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + fail-on-warning: 'true' + create-artifact: 'true' + artifact-name: 'pr-warning-report' +``` + +## Example 4: Comprehensive Warning Management + +```yaml +name: Complete Warning Management + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + comprehensive-warning-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + pip install jupyter-book + pip install -r requirements.txt + + - name: Build Jupyter Book + run: | + jupyter-book build . + + - name: Comprehensive warning check + id: warning-check + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning,UserWarning' + fail-on-warning: 'false' # Don't fail on warnings + create-issue: ${{ github.event_name == 'push' }} # Create issues only on push to main + issue-title: 'Python Warnings in Documentation Build - ${{ github.sha }}' + create-artifact: 'true' # Always create artifact for review + artifact-name: 'warning-report-${{ github.run_id }}' + + - name: Comment on PR with warning info + if: github.event_name == 'pull_request' && steps.warning-check.outputs.warnings-found == 'true' + uses: actions/github-script@v7 + with: + script: | + const warningCount = '${{ steps.warning-check.outputs.warning-count }}'; + const artifactName = 'warning-report-${{ github.run_id }}'; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `⚠️ **Warning:** Found ${warningCount} Python warning(s) in the documentation build. + + Please review the [warning report artifact](${context.payload.repository.html_url}/actions/runs/${{ github.run_id }}) for details. + + Consider fixing these warnings before merging to maintain code quality.` + }) +``` + +## Example 5: Non-failing Check with Reporting ```yaml name: Build with Warning Report @@ -94,7 +220,7 @@ jobs: }) ``` -## Example 3: Custom Warning Types +## Example 6: Custom Warning Types ```yaml name: Check for Custom Warnings @@ -128,7 +254,7 @@ jobs: fail-on-warning: 'true' ``` -## Example 4: Matrix Strategy +## Example 7: Matrix Strategy ```yaml name: Multi-version Warning Check From cf902658a925a57b8ddde9c92a805ae1c12adc5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 06:29:23 +0000 Subject: [PATCH 04/13] Improve warning detection to only check cell_output elements, preventing false positives from text content Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 30 ++++++- .github/actions/check-warnings/action.yml | 105 +++++++++++++++++++++- 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md index 25836ae..a869407 100644 --- a/.github/actions/check-warnings/README.md +++ b/.github/actions/check-warnings/README.md @@ -2,9 +2,12 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the workflow if any are found. It's designed to be used after building documentation or running code that generates HTML output, to ensure that no warnings are present in the final output. +**Important:** This action specifically targets warnings found within code cell outputs (elements with `cell_output` class) to avoid false positives from warnings mentioned in text content. + ## Features -- Scans HTML files for configurable Python warnings +- Scans HTML files for configurable Python warnings **within code cell outputs only** +- Prevents false positives by only checking warnings in `cell_output` HTML elements - Supports multiple warning types (SyntaxWarning, DeprecationWarning, FutureWarning) - Provides detailed output about warnings found - Optionally fails the workflow when warnings are detected @@ -113,6 +116,31 @@ You can enable both issue creation and artifact generation simultaneously: create-artifact: 'true' # Create artifact for detailed review ``` +## How It Works + +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 like "FutureWarning" or "DeprecationWarning" are mentioned in the text content of documentation pages. + +### Example HTML Structure + +The action will detect warnings in this structure: +```html +
+
+    /path/to/file.py:10: FutureWarning: This feature will be deprecated
+      result = old_function()
+    
+
+``` + +But will **ignore** warnings mentioned in regular content: +```html +
+

In this tutorial, we'll discuss FutureWarning messages.

+
+``` + +This ensures that educational content about warnings doesn't trigger false positives in the check. + ## Permissions For the action to work correctly with all features, ensure your workflow has the appropriate permissions: diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 8d1789e..11acd47 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -1,5 +1,5 @@ name: 'Check for Python Warnings' -description: 'Scan HTML files for Python warnings and fail if found' +description: 'Scan HTML files for Python warnings within code cell outputs (avoiding false positives from text content)' author: 'QuantEcon' inputs: @@ -94,7 +94,7 @@ runs: mapfile -d '' FILES < <(find "$HTML_PATH" -name "*.html" -type f -print0) fi - # Search for warnings in HTML files + # Search for warnings in HTML files within cell_output elements for file in "${FILES[@]}"; do echo "Checking file: $file" @@ -102,8 +102,105 @@ runs: # Remove leading/trailing whitespace from warning warning=$(echo "$warning" | xargs) - # Search for the warning in the file - matches=$(grep -n "$warning" "$file" 2>/dev/null || true) + # Create a temporary Python script to parse HTML and search within cell_output elements + python3 << EOF > /tmp/search_results.txt +import re +import sys + +def find_warnings_in_cell_outputs(file_path, warning_text): + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Find all HTML elements with cell_output in the class attribute + # This regex looks for opening tags with class containing cell_output + # and captures content until the matching closing tag + pattern = r'<([^>]+)\s+class="[^"]*cell_output[^"]*"[^>]*>(.*?)' + + matches = [] + line_number = 1 + + # Split content into lines for line number tracking + lines = content.split('\n') + + # Search for cell_output blocks + for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): + block_content = match.group(2) + block_start = match.start() + + # Count line number where this block starts + block_line = content[:block_start].count('\n') + 1 + + # Search for warning within this block + if warning_text in block_content: + # Find specific lines within the block that contain the warning + block_lines = block_content.split('\n') + for i, line in enumerate(block_lines): + if warning_text in line: + actual_line_num = block_line + i + # Clean up the line for display (remove extra whitespace, HTML tags) + clean_line = re.sub(r'<[^>]+>', '', line).strip() + if clean_line: # Only add non-empty lines + matches.append(f"{actual_line_num}:{clean_line}") + + # Output results + for match in matches: + print(match) + + except Exception as e: + print(f"Error processing file: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + find_warnings_in_cell_outputs("$file", "$warning") +EOF + + # Run the Python script and capture results + matches=$(python3 -c " +import re +import sys + +def find_warnings_in_cell_outputs(file_path, warning_text): + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Find all HTML elements with cell_output in the class attribute + # This regex looks for opening tags with class containing cell_output + pattern = r'<([^>]+)\s+class=\"[^\"]*cell_output[^\"]*\"[^>]*>(.*?)' + + matches = [] + + # Search for cell_output blocks + for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): + block_content = match.group(2) + block_start = match.start() + + # Count line number where this block starts + block_line = content[:block_start].count('\n') + 1 + + # Search for warning within this block + if warning_text in block_content: + # Find specific lines within the block that contain the warning + block_lines = block_content.split('\n') + for i, line in enumerate(block_lines): + if warning_text in line: + actual_line_num = block_line + i + # Clean up the line for display (remove extra whitespace, HTML tags) + clean_line = re.sub(r'<[^>]+>', '', line).strip() + if clean_line: # Only add non-empty lines + matches.append(f'{actual_line_num}:{clean_line}') + + # Output results + for match in matches: + print(match) + + except Exception as e: + print(f'Error processing file: {e}', file=sys.stderr) + sys.exit(1) + +find_warnings_in_cell_outputs('$file', '$warning') +" 2>/dev/null || true) if [ -n "$matches" ]; then WARNINGS_FOUND="true" From c3345ba96530a8277a228250ebd1a8024578c41c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 06:41:42 +0000 Subject: [PATCH 05/13] Fix YAML syntax error in check-warnings action by using proper heredoc escaping Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 143 ++++++++-------------- 1 file changed, 48 insertions(+), 95 deletions(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 11acd47..6e77ef1 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -102,105 +102,58 @@ runs: # Remove leading/trailing whitespace from warning warning=$(echo "$warning" | xargs) - # Create a temporary Python script to parse HTML and search within cell_output elements - python3 << EOF > /tmp/search_results.txt -import re -import sys - -def find_warnings_in_cell_outputs(file_path, warning_text): - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Find all HTML elements with cell_output in the class attribute - # This regex looks for opening tags with class containing cell_output - # and captures content until the matching closing tag - pattern = r'<([^>]+)\s+class="[^"]*cell_output[^"]*"[^>]*>(.*?)' - - matches = [] - line_number = 1 - - # Split content into lines for line number tracking - lines = content.split('\n') - - # Search for cell_output blocks - for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): - block_content = match.group(2) - block_start = match.start() + # Create temporary Python script for parsing HTML + cat > /tmp/check_warnings.py << 'EOF' + import re + import sys + import os - # Count line number where this block starts - block_line = content[:block_start].count('\n') + 1 + def find_warnings_in_cell_outputs(file_path, warning_text): + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Find all HTML elements with cell_output in the class attribute + pattern = r'<([^>]+)\s+class="[^"]*cell_output[^"]*"[^>]*>(.*?)' + + matches = [] + + # Search for cell_output blocks + for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): + block_content = match.group(2) + block_start = match.start() + + # Count line number where this block starts + block_line = content[:block_start].count('\n') + 1 + + # Search for warning within this block + if warning_text in block_content: + # Find specific lines within the block that contain the warning + block_lines = block_content.split('\n') + for i, line in enumerate(block_lines): + if warning_text in line: + actual_line_num = block_line + i + # Clean up the line for display (remove extra whitespace, HTML tags) + clean_line = re.sub(r'<[^>]+>', '', line).strip() + if clean_line: # Only add non-empty lines + matches.append(f'{actual_line_num}:{clean_line}') + + # Output results + for match in matches: + print(match) + + except Exception as e: + print(f'Error processing file: {e}', file=sys.stderr) + sys.exit(1) - # Search for warning within this block - if warning_text in block_content: - # Find specific lines within the block that contain the warning - block_lines = block_content.split('\n') - for i, line in enumerate(block_lines): - if warning_text in line: - actual_line_num = block_line + i - # Clean up the line for display (remove extra whitespace, HTML tags) - clean_line = re.sub(r'<[^>]+>', '', line).strip() - if clean_line: # Only add non-empty lines - matches.append(f"{actual_line_num}:{clean_line}") - - # Output results - for match in matches: - print(match) - - except Exception as e: - print(f"Error processing file: {e}", file=sys.stderr) - sys.exit(1) - -if __name__ == "__main__": - find_warnings_in_cell_outputs("$file", "$warning") -EOF + if __name__ == "__main__": + file_path = sys.argv[1] + warning_text = sys.argv[2] + find_warnings_in_cell_outputs(file_path, warning_text) + EOF # Run the Python script and capture results - matches=$(python3 -c " -import re -import sys - -def find_warnings_in_cell_outputs(file_path, warning_text): - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Find all HTML elements with cell_output in the class attribute - # This regex looks for opening tags with class containing cell_output - pattern = r'<([^>]+)\s+class=\"[^\"]*cell_output[^\"]*\"[^>]*>(.*?)' - - matches = [] - - # Search for cell_output blocks - for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): - block_content = match.group(2) - block_start = match.start() - - # Count line number where this block starts - block_line = content[:block_start].count('\n') + 1 - - # Search for warning within this block - if warning_text in block_content: - # Find specific lines within the block that contain the warning - block_lines = block_content.split('\n') - for i, line in enumerate(block_lines): - if warning_text in line: - actual_line_num = block_line + i - # Clean up the line for display (remove extra whitespace, HTML tags) - clean_line = re.sub(r'<[^>]+>', '', line).strip() - if clean_line: # Only add non-empty lines - matches.append(f'{actual_line_num}:{clean_line}') - - # Output results - for match in matches: - print(match) - - except Exception as e: - print(f'Error processing file: {e}', file=sys.stderr) - sys.exit(1) - -find_warnings_in_cell_outputs('$file', '$warning') -" 2>/dev/null || true) + matches=$(python3 /tmp/check_warnings.py "$file" "$warning" 2>/dev/null || true) if [ -n "$matches" ]; then WARNINGS_FOUND="true" From b701c2876ea0cd8bd9700584b6b6e10a7b9f9d15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 07:35:10 +0000 Subject: [PATCH 06/13] Fix shell script heredoc syntax error in check-warnings action Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 132 +++++++++++----------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 6e77ef1..fea1653 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -94,6 +94,54 @@ runs: mapfile -d '' FILES < <(find "$HTML_PATH" -name "*.html" -type f -print0) fi + # Create temporary Python script for parsing HTML + echo 'import re' > /tmp/check_warnings.py + echo 'import sys' >> /tmp/check_warnings.py + echo 'import os' >> /tmp/check_warnings.py + echo '' >> /tmp/check_warnings.py + echo 'def find_warnings_in_cell_outputs(file_path, warning_text):' >> /tmp/check_warnings.py + echo ' try:' >> /tmp/check_warnings.py + echo ' with open(file_path, "r", encoding="utf-8") as f:' >> /tmp/check_warnings.py + echo ' content = f.read()' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' # Find all HTML elements with cell_output in the class attribute' >> /tmp/check_warnings.py + echo ' pattern = r"<([^>]+)\s+class=\"[^\"]*cell_output[^\"]*\"[^>]*>(.*?)"' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' matches = []' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' # Search for cell_output blocks' >> /tmp/check_warnings.py + echo ' for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE):' >> /tmp/check_warnings.py + echo ' block_content = match.group(2)' >> /tmp/check_warnings.py + echo ' block_start = match.start()' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' # Count line number where this block starts' >> /tmp/check_warnings.py + echo ' block_line = content[:block_start].count("\\n") + 1' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' # Search for warning within this block' >> /tmp/check_warnings.py + echo ' if warning_text in block_content:' >> /tmp/check_warnings.py + echo ' # Find specific lines within the block that contain the warning' >> /tmp/check_warnings.py + echo ' block_lines = block_content.split("\\n")' >> /tmp/check_warnings.py + echo ' for i, line in enumerate(block_lines):' >> /tmp/check_warnings.py + echo ' if warning_text in line:' >> /tmp/check_warnings.py + echo ' actual_line_num = block_line + i' >> /tmp/check_warnings.py + echo ' # Clean up the line for display (remove extra whitespace, HTML tags)' >> /tmp/check_warnings.py + echo ' clean_line = re.sub(r"<[^>]+>", "", line).strip()' >> /tmp/check_warnings.py + echo ' if clean_line: # Only add non-empty lines' >> /tmp/check_warnings.py + echo ' matches.append(f"{actual_line_num}:{clean_line}")' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' # Output results' >> /tmp/check_warnings.py + echo ' for match in matches:' >> /tmp/check_warnings.py + echo ' print(match)' >> /tmp/check_warnings.py + echo ' ' >> /tmp/check_warnings.py + echo ' except Exception as e:' >> /tmp/check_warnings.py + echo ' print(f"Error processing file: {e}", file=sys.stderr)' >> /tmp/check_warnings.py + echo ' sys.exit(1)' >> /tmp/check_warnings.py + echo '' >> /tmp/check_warnings.py + echo 'if __name__ == "__main__":' >> /tmp/check_warnings.py + echo ' file_path = sys.argv[1]' >> /tmp/check_warnings.py + echo ' warning_text = sys.argv[2]' >> /tmp/check_warnings.py + echo ' find_warnings_in_cell_outputs(file_path, warning_text)' >> /tmp/check_warnings.py + # Search for warnings in HTML files within cell_output elements for file in "${FILES[@]}"; do echo "Checking file: $file" @@ -102,56 +150,6 @@ runs: # Remove leading/trailing whitespace from warning warning=$(echo "$warning" | xargs) - # Create temporary Python script for parsing HTML - cat > /tmp/check_warnings.py << 'EOF' - import re - import sys - import os - - def find_warnings_in_cell_outputs(file_path, warning_text): - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Find all HTML elements with cell_output in the class attribute - pattern = r'<([^>]+)\s+class="[^"]*cell_output[^"]*"[^>]*>(.*?)' - - matches = [] - - # Search for cell_output blocks - for match in re.finditer(pattern, content, re.DOTALL | re.IGNORECASE): - block_content = match.group(2) - block_start = match.start() - - # Count line number where this block starts - block_line = content[:block_start].count('\n') + 1 - - # Search for warning within this block - if warning_text in block_content: - # Find specific lines within the block that contain the warning - block_lines = block_content.split('\n') - for i, line in enumerate(block_lines): - if warning_text in line: - actual_line_num = block_line + i - # Clean up the line for display (remove extra whitespace, HTML tags) - clean_line = re.sub(r'<[^>]+>', '', line).strip() - if clean_line: # Only add non-empty lines - matches.append(f'{actual_line_num}:{clean_line}') - - # Output results - for match in matches: - print(match) - - except Exception as e: - print(f'Error processing file: {e}', file=sys.stderr) - sys.exit(1) - - if __name__ == "__main__": - file_path = sys.argv[1] - warning_text = sys.argv[2] - find_warnings_in_cell_outputs(file_path, warning_text) - EOF - # Run the Python script and capture results matches=$(python3 /tmp/check_warnings.py "$file" "$warning" 2>/dev/null || true) @@ -212,23 +210,23 @@ runs: CURRENT_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC') # Create the report file - cat > "$ARTIFACT_FILE" << EOF - # Python Warning Report - - **Date:** $CURRENT_DATE - **Repository:** ${{ github.repository }} - **Workflow:** ${{ github.workflow }} - **Run ID:** ${{ github.run_id }} - **Total Warnings Found:** ${{ steps.check.outputs.warning-count }} - - --- - - ${{ steps.check.outputs.detailed-report }} - - --- - - Generated by [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings) - EOF + { + echo "# Python Warning Report" + echo "" + echo "**Date:** $CURRENT_DATE" + echo "**Repository:** ${{ github.repository }}" + echo "**Workflow:** ${{ github.workflow }}" + echo "**Run ID:** ${{ github.run_id }}" + echo "**Total Warnings Found:** ${{ steps.check.outputs.warning-count }}" + echo "" + echo "---" + echo "" + echo "${{ steps.check.outputs.detailed-report }}" + echo "" + echo "---" + echo "" + echo "Generated by [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings)" + } > "$ARTIFACT_FILE" echo "artifact-path=$ARTIFACT_FILE" >> $GITHUB_OUTPUT echo "Created warning report artifact: $ARTIFACT_FILE" From 386985e0cee565474006734a88973cc2d16bf9c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 23:10:36 +0000 Subject: [PATCH 07/13] Fix JavaScript syntax error in GitHub issue creation script Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 60 ++++++++++++----------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index fea1653..f198351 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -246,35 +246,37 @@ runs: with: script: | const warningCount = '${{ steps.check.outputs.warning-count }}'; - const detailedReport = `${{ steps.check.outputs.detailed-report }}`; + const detailedReport = ${{ toJSON(steps.check.outputs.detailed-report) }}; const title = '${{ inputs.issue-title }}'; - const body = `# Python Warnings Detected - - 🚨 **${{ steps.check.outputs.warning-count }} Python warning(s)** were found in the HTML output during the documentation build. - - **Details:** - - **Repository:** ${{ github.repository }} - - **Workflow:** ${{ github.workflow }} - - **Run ID:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) - - **Commit:** ${{ github.sha }} - - **Branch:** ${{ github.ref_name }} - - **Date:** ${new Date().toISOString()} - - --- - - ${detailedReport} - - --- - - **Next Steps:** - 1. Review the warnings listed above - 2. Fix the underlying code that's generating these warnings - 3. Re-run the build to verify the warnings are resolved - - **Note:** This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings). - - Please close this issue once all warnings have been addressed.`; + const body = [ + '# Python Warnings Detected', + '', + '🚨 **' + warningCount + ' Python warning(s)** were found in the HTML output during the documentation build.', + '', + '**Details:**', + '- **Repository:** ${{ github.repository }}', + '- **Workflow:** ${{ github.workflow }}', + '- **Run ID:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})', + '- **Commit:** ${{ github.sha }}', + '- **Branch:** ${{ github.ref_name }}', + '- **Date:** ' + new Date().toISOString(), + '', + '---', + '', + detailedReport, + '', + '---', + '', + '**Next Steps:**', + '1. Review the warnings listed above', + '2. Fix the underlying code that\'s generating these warnings', + '3. Re-run the build to verify the warnings are resolved', + '', + '**Note:** This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).', + '', + 'Please close this issue once all warnings have been addressed.' + ].join('\n'); try { const response = await github.rest.issues.create({ @@ -286,13 +288,13 @@ runs: }); const issueUrl = response.data.html_url; - console.log(`Created issue: ${issueUrl}`); + console.log('Created issue: ' + issueUrl); core.setOutput('issue-url', issueUrl); return issueUrl; } catch (error) { console.error('Failed to create issue:', error); - core.setFailed(`Failed to create issue: ${error.message}`); + core.setFailed('Failed to create issue: ' + error.message); } branding: From 7e4745b801b40adedf98bfaf4a58f031392087a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:08:52 +0000 Subject: [PATCH 08/13] Add PR comment feature for fail-on-warning mode Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 34 +++++++++++++- .github/actions/check-warnings/action.yml | 57 +++++++++++++++++++++-- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md index a869407..655dd41 100644 --- a/.github/actions/check-warnings/README.md +++ b/.github/actions/check-warnings/README.md @@ -13,6 +13,7 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the - Optionally fails the workflow when warnings are detected - **Creates GitHub issues** with detailed warning reports - **Generates workflow artifacts** containing warning reports +- **Posts PR comments** with warning reports when failing on warnings - Configurable search path and warning types ## Usage @@ -24,6 +25,17 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the uses: QuantEcon/meta/.github/actions/check-warnings@main ``` +### Advanced Usage with PR Comments + +```yaml +- name: Check for Python warnings with PR feedback + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'true' # This will post a comment to the PR if warnings are found +``` + ### Advanced Usage with Issue Creation ```yaml @@ -102,6 +114,17 @@ When `create-artifact` is set to `true`, the action generates a detailed Markdow - Timestamp and commit information - Downloadable for offline review +### Pull Request Comments + +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: + +- Complete warning information formatted for easy reading +- Direct links to the failing workflow run +- Suggested next steps for fixing the warnings +- Repository and commit context + +This feature helps developers quickly identify and fix warnings without digging through workflow logs. + ### Using Both Features Together You can enable both issue creation and artifact generation simultaneously: @@ -150,9 +173,10 @@ permissions: contents: read # For checking out the repository issues: write # For creating GitHub issues (if create-issue is enabled) actions: read # For creating workflow artifacts (if create-artifact is enabled) + pull-requests: write # For posting PR comments (when fail-on-warning is true in PRs) ``` -If you're only using the basic warning check functionality, only `contents: read` is required. +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. ## Inputs @@ -193,6 +217,7 @@ permissions: contents: read issues: write actions: read + pull-requests: write jobs: build-and-check: @@ -271,4 +296,9 @@ This action is particularly useful for: 8. **Performance considerations**: For large HTML output directories, consider: - Using specific paths rather than scanning entire directories - Limiting warning types to only those relevant to your project - - Setting appropriate artifact retention periods \ No newline at end of file + - Setting appropriate artifact retention periods + +9. **Pull Request feedback**: When `fail-on-warning` is `true`: + - The action automatically posts detailed warning reports as PR comments + - This provides immediate feedback to developers without requiring log diving + - Requires `pull-requests: write` permission in your workflow \ No newline at end of file diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index f198351..af8b64d 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -191,15 +191,62 @@ runs: if [ "$WARNINGS_FOUND" = "true" ]; then echo "❌ Found $TOTAL_WARNINGS warning(s) in HTML files" echo "::error::Found $TOTAL_WARNINGS Python warning(s) in HTML output" - - if [ "$FAIL_ON_WARNING" = "true" ]; then - echo "Failing workflow due to warnings found" - exit 1 - fi else echo "✅ No warnings found in HTML files" fi + - name: Post PR comment with warning report + if: inputs.fail-on-warning == 'true' && steps.check.outputs.warnings-found == 'true' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const warningCount = '${{ steps.check.outputs.warning-count }}'; + const detailedReport = ${{ toJSON(steps.check.outputs.detailed-report) }}; + + const body = [ + '## ⚠️ Python Warnings Detected', + '', + '🚨 **' + warningCount + ' Python warning(s)** were found in the HTML output during the documentation build.', + '', + '**Build Details:**', + '- **Workflow Run:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})', + '- **Commit:** ${{ github.sha }}', + '- **Date:** ' + new Date().toISOString(), + '', + '---', + '', + detailedReport, + '', + '---', + '', + '**Next Steps:**', + '1. Review the warnings listed above', + '2. Fix the underlying code that\'s generating these warnings', + '3. Push the changes to update this PR', + '', + '📝 *This comment was automatically generated by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).*' + ].join('\n'); + + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + console.log('Posted PR comment with warning details'); + } catch (error) { + console.error('Failed to create PR comment:', error); + core.setFailed('Failed to create PR comment: ' + error.message); + } + + - name: Fail workflow on warnings + if: inputs.fail-on-warning == 'true' && steps.check.outputs.warnings-found == 'true' + shell: bash + run: | + echo "Failing workflow due to warnings found" + exit 1 + - name: Create artifact with warning report id: create-artifact if: inputs.create-artifact == 'true' && steps.check.outputs.warnings-found == 'true' From b47f3f926eae958463d7d3ac76516549003a0038 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 03:04:36 +0000 Subject: [PATCH 09/13] Add simple PR comment notification for issue creation feature Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 18 +++++++--- .github/actions/check-warnings/action.yml | 43 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md index 655dd41..108d1eb 100644 --- a/.github/actions/check-warnings/README.md +++ b/.github/actions/check-warnings/README.md @@ -105,6 +105,13 @@ When `create-issue` is set to `true`, the action will automatically create a Git - Suggested next steps for resolution - Automatic labeling (`bug`, `documentation`, `python-warnings`) +Additionally, when issues are created in pull request contexts, a simple notification comment is posted to the PR thread containing: + +- List of files with warnings +- Direct link to the created issue for detailed information + +This provides immediate awareness to PR authors without cluttering the conversation with full warning details. + ### Workflow Artifacts When `create-artifact` is set to `true`, the action generates a detailed Markdown report as a workflow artifact. This report includes: @@ -173,10 +180,10 @@ permissions: contents: read # For checking out the repository issues: write # For creating GitHub issues (if create-issue is enabled) actions: read # For creating workflow artifacts (if create-artifact is enabled) - pull-requests: write # For posting PR comments (when fail-on-warning is true in PRs) + pull-requests: write # For posting PR comments (when fail-on-warning is true OR create-issue is true in PRs) ``` -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. +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 contexts. ## Inputs @@ -298,7 +305,8 @@ This action is particularly useful for: - Limiting warning types to only those relevant to your project - Setting appropriate artifact retention periods -9. **Pull Request feedback**: When `fail-on-warning` is `true`: - - The action automatically posts detailed warning reports as PR comments - - This provides immediate feedback to developers without requiring log diving +9. **Pull Request feedback**: + - When `fail-on-warning` is `true`: The action posts detailed warning reports as PR comments + - When `create-issue` is `true`: The action posts simple notification comments linking to created issues + - Both features provide immediate feedback to developers without requiring log diving - Requires `pull-requests: write` permission in your workflow \ No newline at end of file diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index af8b64d..b8c3a1a 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -344,6 +344,49 @@ runs: core.setFailed('Failed to create issue: ' + error.message); } + - name: Post simple PR comment linking to issue + if: inputs.create-issue == 'true' && steps.check.outputs.warnings-found == 'true' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const warningDetails = '${{ steps.check.outputs.warning-details }}'; + const issueUrl = '${{ steps.create-issue.outputs.issue-url }}'; + + // Extract file names from warning details + const files = []; + const lines = warningDetails.split('\n'); + for (const line of lines) { + if (line.includes(': ') && line.includes('instance(s) of')) { + const fileName = line.split(':')[0].trim(); + if (fileName && !files.includes(fileName)) { + files.push(fileName); + } + } + } + + const fileList = files.map(file => `- ${file}`).join('\n'); + + const body = [ + 'There were code execution warnings found in the following files:', + '', + fileList, + '', + `For further details please check ${issueUrl}` + ].join('\n'); + + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + console.log('Posted simple PR comment linking to issue'); + } catch (error) { + console.error('Failed to create PR comment:', error); + core.setFailed('Failed to create PR comment: ' + error.message); + } + branding: icon: 'alert-triangle' color: 'orange' \ No newline at end of file From a25b94eb398a669da319dacbdafb5cac63f83827 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 03:15:45 +0000 Subject: [PATCH 10/13] Add notify parameter for automatic issue assignment Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/README.md | 27 ++++++++++++++++++ .github/actions/check-warnings/action.yml | 33 ++++++++++++++++++++++ .github/actions/check-warnings/examples.md | 17 +++++++++++ 3 files changed, 77 insertions(+) diff --git a/.github/actions/check-warnings/README.md b/.github/actions/check-warnings/README.md index 108d1eb..1b94ad9 100644 --- a/.github/actions/check-warnings/README.md +++ b/.github/actions/check-warnings/README.md @@ -49,6 +49,20 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the issue-title: 'Python Warnings Found in Documentation Build' ``` +### Advanced Usage with Issue Creation and User Assignment + +```yaml +- name: Check for Python warnings with assigned issue + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' + fail-on-warning: 'false' + create-issue: 'true' + issue-title: 'Python Warnings Found in Documentation Build' + notify: 'username1,username2' # Assign issue to multiple users +``` + ### Advanced Usage with Artifact Creation ```yaml @@ -73,6 +87,7 @@ This GitHub Action scans HTML files for Python warnings and optionally fails the fail-on-warning: 'false' create-issue: 'true' issue-title: 'Python Warnings Detected in Build' + notify: 'maintainer1,reviewer2' # Assign to specific team members create-artifact: 'true' artifact-name: 'detailed-warning-report' ``` @@ -105,6 +120,16 @@ When `create-issue` is set to `true`, the action will automatically create a Git - Suggested next steps for resolution - Automatic labeling (`bug`, `documentation`, `python-warnings`) +#### Automatic User Assignment + +When the `notify` parameter is provided, the created issue will be automatically assigned to the specified GitHub users. This feature supports: + +- Single user assignment: `notify: 'username'` +- Multiple user assignment: `notify: 'user1,user2,user3'` +- Robust error handling: If assignment fails, the issue is still created successfully + +This ensures that the right team members are immediately notified about warnings and can take action to resolve them. + Additionally, when issues are created in pull request contexts, a simple notification comment is posted to the PR thread containing: - List of files with warnings @@ -196,6 +221,7 @@ If you're only using the basic warning check functionality, only `contents: read | `issue-title` | Title for the GitHub issue when warnings are found | No | `Python Warnings Found in Documentation Build` | | `create-artifact` | Whether to create a workflow artifact with the warning report | No | `false` | | `artifact-name` | Name for the workflow artifact containing the warning report | No | `warning-report` | +| `notify` | GitHub username(s) to assign to the created issue (comma-separated for multiple users) | No | `` | ## Outputs @@ -253,6 +279,7 @@ jobs: warnings: 'SyntaxWarning,DeprecationWarning,FutureWarning' fail-on-warning: ${{ github.event_name == 'push' }} # Fail on push, warn on PR create-issue: ${{ github.event_name == 'push' }} # Create issues for main branch + notify: 'maintainer1,reviewer2' # Assign issues to team members create-artifact: 'true' # Always create artifacts artifact-name: 'warning-report' ``` diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index b8c3a1a..d544468 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -31,6 +31,10 @@ inputs: description: 'Name for the workflow artifact containing the warning report' required: false default: 'warning-report' + notify: + description: 'GitHub username(s) to assign to the created issue (comma-separated for multiple users)' + required: false + default: '' outputs: warnings-found: @@ -295,6 +299,7 @@ runs: const warningCount = '${{ steps.check.outputs.warning-count }}'; const detailedReport = ${{ toJSON(steps.check.outputs.detailed-report) }}; const title = '${{ inputs.issue-title }}'; + const notify = '${{ inputs.notify }}'; const body = [ '# Python Warnings Detected', @@ -335,9 +340,37 @@ runs: }); const issueUrl = response.data.html_url; + const issueNumber = response.data.number; console.log('Created issue: ' + issueUrl); core.setOutput('issue-url', issueUrl); + // Assign users to the issue if notify parameter is provided + if (notify && notify.trim()) { + try { + // Parse comma-separated usernames and clean them + const assignees = notify.split(',') + .map(username => username.trim()) + .filter(username => username.length > 0); + + if (assignees.length > 0) { + console.log('Assigning issue to users: ' + assignees.join(', ')); + + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + assignees: assignees + }); + + console.log('Successfully assigned issue to: ' + assignees.join(', ')); + } + } catch (assignError) { + console.error('Failed to assign users to issue:', assignError); + // Don't fail the entire workflow for assignment errors + console.log('Issue was created successfully, but assignment failed. Users may need to be assigned manually.'); + } + } + return issueUrl; } catch (error) { console.error('Failed to create issue:', error); diff --git a/.github/actions/check-warnings/examples.md b/.github/actions/check-warnings/examples.md index e97d5ab..d655cec 100644 --- a/.github/actions/check-warnings/examples.md +++ b/.github/actions/check-warnings/examples.md @@ -47,6 +47,9 @@ jobs: ```yaml name: Build with Issue Creation +permissions: + contents: read + issues: write on: push: @@ -72,6 +75,19 @@ jobs: fail-on-warning: 'false' create-issue: 'true' issue-title: 'Python Warnings Found in Documentation' + notify: 'maintainer1,reviewer2' # Assign to team members +``` + +## Example 2b: Issue Creation with Single User Assignment + +```yaml +- name: Check for warnings with single user assignment + uses: QuantEcon/meta/.github/actions/check-warnings@main + with: + html-path: './_build/html' + create-issue: 'true' + issue-title: 'Critical Python Warnings Detected' + notify: 'team-lead' # Assign to single responsible person ``` ## Example 3: Check with Artifact Generation @@ -146,6 +162,7 @@ jobs: fail-on-warning: 'false' # Don't fail on warnings create-issue: ${{ github.event_name == 'push' }} # Create issues only on push to main issue-title: 'Python Warnings in Documentation Build - ${{ github.sha }}' + notify: 'team-lead,maintainer' # Assign to responsible team members create-artifact: 'true' # Always create artifact for review artifact-name: 'warning-report-${{ github.run_id }}' From ac63b1953a80dda7b15976a1c56b1c46b34ae3c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 03:36:25 +0000 Subject: [PATCH 11/13] Fix JavaScript syntax error by properly escaping warning-details in PR comment script Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index d544468..6c5988e 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -382,7 +382,7 @@ runs: uses: actions/github-script@v7 with: script: | - const warningDetails = '${{ steps.check.outputs.warning-details }}'; + const warningDetails = ${{ toJSON(steps.check.outputs.warning-details) }}; const issueUrl = '${{ steps.create-issue.outputs.issue-url }}'; // Extract file names from warning details From eb50074ea25aae6bc17b72a919724b6c365e2503 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 05:07:57 +0000 Subject: [PATCH 12/13] Add note to PR comment indicating automated creation by Check for Python Warnings Action Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 6c5988e..0ec8f3e 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -404,7 +404,9 @@ runs: '', fileList, '', - `For further details please check ${issueUrl}` + `For further details please check ${issueUrl}`, + '', + 'Note: This issue was automatically created by the [Check for Python Warnings Action](https://github.com/QuantEcon/meta/.github/actions/check-warnings).' ].join('\n'); try { From 0e789a75511f173dec45cf3f9e27d2522a673218 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 05:54:24 +0000 Subject: [PATCH 13/13] Replace 'documentation' label with 'execution' for GitHub issue creation Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --- .github/actions/check-warnings/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/check-warnings/action.yml b/.github/actions/check-warnings/action.yml index 0ec8f3e..4b14af7 100644 --- a/.github/actions/check-warnings/action.yml +++ b/.github/actions/check-warnings/action.yml @@ -336,7 +336,7 @@ runs: repo: context.repo.repo, title: title, body: body, - labels: ['bug', 'documentation', 'python-warnings'] + labels: ['bug', 'execution', 'python-warnings'] }); const issueUrl = response.data.html_url;