feat(patterns): add trust middleware guardrails recipe #531
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Notebook Quality Check | |
| on: | |
| pull_request: | |
| paths: | |
| - '**/*.ipynb' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| push: | |
| branches: [main] | |
| paths: | |
| - '**/*.ipynb' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| id-token: write # Anthropic Workload Identity Federation | |
| jobs: | |
| validate-notebooks: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4.2.0 (sha-pinned) | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "uv.lock" | |
| - name: Set up Python 3.11 | |
| run: uv python install 3.11 | |
| - name: Install dependencies | |
| run: | | |
| uv sync --frozen --all-extras | |
| - name: Lint notebooks with Ruff | |
| run: | | |
| uv run ruff check **/*.ipynb --show-fixes || true | |
| uv run ruff format **/*.ipynb --check || true | |
| - name: Validate notebook structure | |
| id: validate | |
| run: | | |
| uv run python scripts/validate_notebooks.py | tee validation_output.txt | |
| # Check if validation found issues | |
| if grep -q "❌" validation_output.txt; then | |
| echo "has_issues=true" >> $GITHUB_OUTPUT | |
| exit 1 | |
| else | |
| echo "has_issues=false" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Summarize validation issues with Claude | |
| if: github.event_name == 'pull_request' && steps.validate.outputs.has_issues == 'true' | |
| uses: anthropics/claude-code-action@bbfaf8e1ffe3e688f7ab65ceee78de241e24a238 # v1.0.132 (>=v1.0.130 for WIF inputs) | |
| with: | |
| # Anthropic auth via Workload Identity Federation — the action | |
| # exchanges this job's GitHub OIDC token (id-token: write above) | |
| # for a short-lived access token instead of a static API key. | |
| anthropic_federation_rule_id: fdrl_01SqmTwzmEE547mtaYN1mqHL | |
| anthropic_organization_id: 1ec12c5c-6542-4da8-bf2f-c15919aef01c | |
| anthropic_service_account_id: svac_01BHcCBa1UWFvNrHMqJjuaUZ | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| prompt: | | |
| The notebook validation found these issues: | |
| ``` | |
| $(cat validation_output.txt) | |
| ``` | |
| Create a helpful PR comment that: | |
| - Summarizes the validation issues found | |
| - Groups similar issues together (e.g., "7 notebooks have empty cells") | |
| - Explains why empty cells are problematic and how to fix them (delete them or add content) | |
| - If there are error outputs, explain they should be cleared before committing | |
| - Uses friendly, constructive language | |
| - Includes specific notebook names and cell numbers for reference | |
| Format as a nice GitHub comment with markdown. Use emoji sparingly for clarity. | |
| Post using: gh pr comment $PR_NUMBER --body "your comment" | |
| claude_args: | | |
| --allowedTools "Bash(gh pr comment:*),Bash(cat:*),Read" | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| # Only run API tests on main branch or for maintainers (costs money) | |
| # TODO: this step still reads the static ANTHROPIC_API_KEY secret. The | |
| # claude-code-action steps above use Workload Identity Federation; this | |
| # direct-API step needs a separate inline OIDC mint+exchange (or the | |
| # anthropic SDK's WIF env-var trio). Non-fatal in the meantime — each | |
| # notebook execution failure is caught by the `|| echo` below. | |
| - name: Execute notebooks (API Testing) | |
| if: | | |
| github.event_name == 'push' || | |
| github.event.pull_request.author_association == 'MEMBER' || | |
| github.event.pull_request.author_association == 'OWNER' | |
| env: | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| mkdir -p test_outputs | |
| for notebook in $(find . -name "*.ipynb" -not -path "*/.*" -not -path "*/test_outputs/*"); do | |
| echo "📓 Testing: $notebook" | |
| output_name=$(echo "$notebook" | sed 's|/|_|g' | sed 's|\.|_|g') | |
| # Use nbconvert to execute notebooks and save outputs | |
| uv run jupyter nbconvert --to notebook \ | |
| --execute "$notebook" \ | |
| --ExecutePreprocessor.kernel_name=python3 \ | |
| --ExecutePreprocessor.timeout=120 \ | |
| --output "test_outputs/${output_name}_executed.ipynb" \ | |
| --output-dir=. \ | |
| || echo "⚠️ Failed: $notebook" | |
| done | |
| # Mock testing for external contributors | |
| - name: Execute notebooks (Mock Testing) | |
| if: | | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.author_association != 'MEMBER' && | |
| github.event.pull_request.author_association != 'OWNER' | |
| run: | | |
| echo "🔒 Running in mock mode for external contributor" | |
| for notebook in $(find . -name "*.ipynb" -not -path "*/.*"); do | |
| echo "📓 Validating structure: $notebook" | |
| uv run python -m nbformat.validator "$notebook" | |
| done | |
| - name: Upload test outputs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: notebook-test-outputs | |
| path: test_outputs/ | |
| retention-days: 7 |