fix: update security commands and resolve vulnerabilities #64
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: Documentation - Build & Deploy | |
| on: | |
| push: | |
| branches: [main, develop] | |
| tags: ['v*'] | |
| paths: | |
| - 'docs/**' | |
| - 'src/**' | |
| - 'README.md' | |
| - 'CHANGELOG.md' | |
| - 'pyproject.toml' | |
| - '.github/workflows/docs.yml' | |
| pull_request: | |
| branches: [main, develop] | |
| paths: | |
| - 'docs/**' | |
| - 'src/**' | |
| - 'README.md' | |
| - 'CHANGELOG.md' | |
| - 'pyproject.toml' | |
| - '.github/workflows/docs.yml' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHONUNBUFFERED: "1" | |
| FORCE_COLOR: "1" | |
| jobs: | |
| # Build documentation | |
| build-docs: | |
| name: Build Documentation | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| outputs: | |
| docs-artifact: ${{ steps.upload.outputs.artifact-id }} | |
| docs-size: ${{ steps.check-size.outputs.size }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Full history for version info | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: 'pip' | |
| cache-dependency-path: 'pyproject.toml' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e .[docs] | |
| - name: Verify documentation structure | |
| run: | | |
| if [ ! -d "docs" ]; then | |
| echo "❌ docs/ directory not found!" | |
| exit 1 | |
| fi | |
| if [ ! -f "docs/conf.py" ]; then | |
| echo "❌ docs/conf.py not found!" | |
| exit 1 | |
| fi | |
| if [ ! -f "docs/index.rst" ]; then | |
| echo "❌ docs/index.rst not found!" | |
| exit 1 | |
| fi | |
| echo "✅ Documentation structure verified" | |
| echo "📁 Documentation files found:" | |
| find docs -name "*.rst" -o -name "*.md" | head -10 | |
| - name: Clean previous builds | |
| run: | | |
| cd docs | |
| rm -rf _build | |
| echo "🧹 Cleaned previous builds" | |
| - name: Build with Sphinx | |
| run: | | |
| cd docs | |
| echo "🔨 Building HTML documentation..." | |
| sphinx-build -b html . _build/html -W --keep-going -j auto -v | |
| echo "✅ Documentation built successfully" | |
| - name: Check for build warnings | |
| run: | | |
| cd docs | |
| # Check if there were any warnings in the build | |
| if sphinx-build -b html . _build/html -W --keep-going 2>&1 | grep -i warning; then | |
| echo "⚠️ Build completed with warnings" | |
| else | |
| echo "✅ Build completed without warnings" | |
| fi | |
| - name: Run quality checks | |
| run: | | |
| cd docs | |
| echo "🔗 Checking documentation links..." | |
| sphinx-build -b linkcheck . _build/linkcheck || echo "⚠️ Some links may be broken" | |
| echo "📊 Generating documentation coverage..." | |
| sphinx-build -b coverage . _build/coverage | |
| if [ -f "_build/coverage/python.txt" ]; then | |
| echo "📈 Documentation coverage report:" | |
| cat _build/coverage/python.txt | head -20 | |
| fi | |
| - name: Check build size and quality | |
| id: check-size | |
| run: | | |
| cd docs/_build/html | |
| # Check total size | |
| DOCS_SIZE=$(du -sh . | cut -f1) | |
| DOCS_SIZE_BYTES=$(du -sb . | cut -f1) | |
| echo "size=$DOCS_SIZE" >> $GITHUB_OUTPUT | |
| echo "size_bytes=$DOCS_SIZE_BYTES" >> $GITHUB_OUTPUT | |
| echo "📊 Documentation metrics:" | |
| echo " Size: $DOCS_SIZE" | |
| echo " Files: $(find . -type f | wc -l)" | |
| echo " HTML files: $(find . -name "*.html" | wc -l)" | |
| # Check for essential files | |
| if [ ! -f "index.html" ]; then | |
| echo "❌ index.html not found!" | |
| exit 1 | |
| fi | |
| if [ ! -f "genindex.html" ]; then | |
| echo "⚠️ genindex.html not found - API documentation may be incomplete" | |
| fi | |
| # Size warning | |
| if [ $DOCS_SIZE_BYTES -gt 104857600 ]; then # 100MB | |
| echo "⚠️ Documentation is quite large ($DOCS_SIZE). Consider optimization." | |
| fi | |
| echo "✅ Build quality checks completed" | |
| - name: Upload documentation artifact | |
| id: upload | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: documentation-${{ github.sha }} | |
| path: docs/_build/html/ | |
| retention-days: 30 | |
| compression-level: 6 | |
| # Validate documentation | |
| validate-docs: | |
| name: Validate Documentation | |
| runs-on: ubuntu-latest | |
| needs: build-docs | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Download documentation | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: documentation-${{ github.sha }} | |
| path: ./docs | |
| - name: Validate HTML structure | |
| run: | | |
| echo "🔍 Validating HTML structure..." | |
| # Check for broken internal links in HTML | |
| echo "🔗 Checking for broken internal references..." | |
| BROKEN_LINKS=$(find docs -name "*.html" -exec grep -l "404\|File not found\|broken reference" {} \; || true) | |
| if [ -n "$BROKEN_LINKS" ]; then | |
| echo "⚠️ Found files with potential broken references:" | |
| echo "$BROKEN_LINKS" | head -5 | |
| else | |
| echo "✅ No obvious broken references found" | |
| fi | |
| # Check for missing images | |
| echo "🖼️ Checking for missing images..." | |
| MISSING_IMAGES=$(find docs -name "*.html" -exec grep -o 'src="[^"]*"' {} \; | sed 's/src="//;s/"//' | while read img; do | |
| if [[ "$img" == http* ]] || [[ "$img" == //* ]]; then | |
| continue # Skip external URLs | |
| fi | |
| if [ ! -f "docs/$img" ]; then | |
| echo "Missing: $img" | |
| fi | |
| done) | |
| if [ -n "$MISSING_IMAGES" ]; then | |
| echo "⚠️ Missing image files:" | |
| echo "$MISSING_IMAGES" | head -5 | |
| else | |
| echo "✅ All local images found" | |
| fi | |
| - name: Performance analysis | |
| run: | | |
| echo "⚡ Performance analysis:" | |
| # File counts | |
| TOTAL_FILES=$(find docs -type f | wc -l) | |
| HTML_FILES=$(find docs -name "*.html" | wc -l) | |
| echo "📊 File statistics:" | |
| echo " Total files: $TOTAL_FILES" | |
| echo " HTML files: $HTML_FILES" | |
| # Size breakdown | |
| echo "📦 Size breakdown:" | |
| find docs -name "*.html" -exec du -sch {} + 2>/dev/null | tail -1 | awk '{print " HTML: " $1}' || echo " HTML: N/A" | |
| find docs -name "*.css" -exec du -sch {} + 2>/dev/null | tail -1 | awk '{print " CSS: " $1}' || echo " CSS: N/A" | |
| find docs -name "*.js" -exec du -sch {} + 2>/dev/null | tail -1 | awk '{print " JS: " $1}' || echo " JS: N/A" | |
| find docs \( -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.svg" \) -exec du -sch {} + 2>/dev/null | tail -1 | awk '{print " Images: " $1}' || echo " Images: N/A" | |
| # Large files check | |
| echo "🏋️ Large files (>1MB):" | |
| find docs -type f -size +1M -exec ls -lh {} \; 2>/dev/null | awk '{print " " $5 " " $9}' | head -5 || echo " None found" | |
| - name: Accessibility check | |
| run: | | |
| echo "♿ Basic accessibility check..." | |
| # Check for images without alt text | |
| IMAGES_NO_ALT=$(find docs -name "*.html" -exec grep -l '<img[^>]*>' {} \; | xargs grep '<img[^>]*>' | grep -v 'alt=' | wc -l) | |
| if [ "$IMAGES_NO_ALT" -gt 0 ]; then | |
| echo "⚠️ Found $IMAGES_NO_ALT images without alt text" | |
| else | |
| echo "✅ All images have alt text" | |
| fi | |
| echo "✅ Validation completed" | |
| # Deploy to GitHub Pages | |
| deploy-pages: | |
| name: Deploy to GitHub Pages | |
| runs-on: ubuntu-latest | |
| needs: [build-docs, validate-docs] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check if tag is on main branch | |
| id: check-branch | |
| run: | | |
| TAG_NAME=${GITHUB_REF#refs/tags/} | |
| echo "Checking if tag $TAG_NAME exists on main branch..." | |
| # Check if the tag points to a commit that exists on main | |
| if git merge-base --is-ancestor $TAG_NAME origin/main; then | |
| echo "✅ Tag $TAG_NAME is on main branch" | |
| echo "deploy=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Tag $TAG_NAME is NOT on main branch" | |
| echo "🚫 Skipping deployment - tags must be on main branch" | |
| echo "deploy=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Download documentation | |
| if: steps.check-branch.outputs.deploy == 'true' || github.event.inputs.deploy_to_pages == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: documentation-${{ github.sha }} | |
| path: ./docs | |
| - name: Setup GitHub Pages | |
| if: steps.check-branch.outputs.deploy == 'true' || github.event.inputs.deploy_to_pages == 'true' | |
| uses: actions/configure-pages@v4 | |
| - name: Upload to GitHub Pages | |
| if: steps.check-branch.outputs.deploy == 'true' || github.event.inputs.deploy_to_pages == 'true' | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: ./docs | |
| - name: Deploy to GitHub Pages | |
| if: steps.check-branch.outputs.deploy == 'true' || github.event.inputs.deploy_to_pages == 'true' | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| - name: Report deployment | |
| if: steps.check-branch.outputs.deploy == 'true' || github.event.inputs.deploy_to_pages == 'true' | |
| run: | | |
| echo "🚀 Documentation deployed successfully!" | |
| echo "📖 Documentation URL: ${{ steps.deployment.outputs.page_url }}" | |
| echo "📊 Documentation size: ${{ needs.build-docs.outputs.docs-size }}" | |
| - name: Report skipped deployment | |
| if: steps.check-branch.outputs.deploy == 'false' | |
| run: | | |
| echo "🚫 Deployment skipped - tag must be created from main branch" | |
| echo "ℹ️ To deploy documentation, create tags from the main branch only" |