AI Web FeedsAI Web FeedsOpen web AI reader
  • Documentation

    CLI Integration in Workflows

    How the ai-web-feeds CLI powers our CI/CD pipeline

    Source: apps/web/content/docs/development/cli-workflows.mdx

    CLI Integration in GitHub Actions

    The ai-web-feeds CLI is the backbone of our CI/CD pipeline. Every workflow leverages CLI commands for consistent, reliable automation.

    🎯 Why CLI-First Workflows?

    Benefits

    1. Consistency: Same commands in CI/CD and local development
    2. Testability: CLI is fully tested (90%+ coverage)
    3. Maintainability: Logic in Python, not YAML
    4. Reusability: One command, many workflows
    5. Debugging: Run exact CI command locally

    Anti-Pattern ❌

    # DON'T: Duplicate logic in YAML
    - name: Validate feeds
      run: |
        # Inline Python validation shell logic
        # ... 50 lines of shell script validation logic

    Best Practice ✅

    # DO: Use CLI command
    - name: Validate feeds
      run: uv run ai-web-feeds validate all --strict

    🔧 Available CLI Commands

    Validation Commands

    validate - Comprehensive Feed Validation

    Purpose: Validate feed data, schemas, URLs, and parsing

    Workflow Usage:

    # Validate all feeds
    - name: Validate all feeds
      run: uv run ai-web-feeds validate all
    
    # Schema validation only
    - name: Validate schema
      run: uv run ai-web-feeds validate feeds --strict
    
    # Check URL accessibility
    - name: Check feed URLs
      run: uv run ai-web-feeds validate http
    
    # Validate specific feeds (for PR changes)
    - name: Validate changed feeds
      run: |
        CHANGED_FEEDS=$(git diff origin/main -- data/feeds.yaml | grep -oP 'url:\s*\K\S+')
        uv run ai-web-feeds validate feeds --file $CHANGED_FEEDS

    Options:

    • validate all - Run schema and reference checks
    • validate feeds - Validate feeds.yaml against feeds.schema.json
    • validate topics - Validate topics.yaml
    • validate http - Test URL accessibility from the database
    • --strict - Fail on warnings
    • --timeout - Request timeout (default: 30s)
    • --feeds - Validate specific feed URLs

    Exit Codes:

    • 0 - All validations passed
    • 1 - Validation failures
    • 2 - Schema errors

    test - Run Test Suite

    Purpose: Execute pytest test suite with coverage

    Workflow Usage:

    # Full test suite
    - name: Run tests
      run: uv run ai-web-feeds test coverage
    
    # Quick tests only
    - name: Quick test
      run: uv run ai-web-feeds test quick
    
    # Specific test markers
    - name: Unit tests
      run: uv run ai-web-feeds test unit

    Options:

    • --coverage - Generate coverage report
    • --quick - Fast tests only (no slow/integration)
    • --marker - Run specific test markers (unit, integration, e2e)
    • --verbose - Detailed output

    Output:

    • Creates reports/coverage/ directory
    • Generates coverage.xml for Codecov
    • Exit code 1 if tests fail or coverage below 90%

    Analytics Commands

    analytics - Generate Feed Statistics

    Purpose: Calculate feed metrics and insights

    Workflow Usage:

    # Generate analytics CSV
    - name: Generate analytics
      run: uv run ai-web-feeds analytics export --output data/analytics.csv
    
    # Display in workflow
    - name: Show analytics
      run: uv run ai-web-feeds analytics summary
    
    # Track changes
    - name: Analytics diff
      run: |
        uv run ai-web-feeds analytics export --output /tmp/new.csv
        diff data/analytics.csv /tmp/new.csv || echo "Analytics changed"

    Options:

    • summary - Display summary metrics
    • trending - Display active topics
    • velocity - Display publication velocity
    • snapshot - Generate a daily analytics snapshot
    • export --output <file> - Export analytics as CSV

    Metrics:

    • Total feed count
    • Feeds per source type
    • Topic coverage
    • Language distribution
    • Feed health status
    • Update frequency statistics

    stats - Display Feed Statistics

    Purpose: Show human-readable feed statistics

    Workflow Usage:

    # Post stats as PR comment
    - name: Generate stats
      id: stats
      run: |
        STATS=$(uv run ai-web-feeds stats show)
        echo "stats<<EOF" >> $GITHUB_OUTPUT
        echo "$STATS" >> $GITHUB_OUTPUT
        echo "EOF" >> $GITHUB_OUTPUT
    
    - name: Comment PR
      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: ${{ steps.stats.outputs.stats }}
          })

    Subcommands and options:

    • show - Display total feed count, verified count, and source-type distribution
    • show --database <url> - Read stats from a specific database URL

    Export Commands

    export - Export Feed Data

    Purpose: Generate output in various formats

    Workflow Usage:

    # Export to JSON for artifacts
    - name: Export feeds
      run: uv run ai-web-feeds export json --output feeds.json
    
    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: feed-data
        path: feeds.json
    
    # Export OPML artifact
    - name: Export OPML
      run: uv run ai-web-feeds export opml --output data/feeds.opml

    Options:

    • json --output <file> - Export catalog JSON
    • opml --output <file> - Export OPML
    • csv --output <file> - Export CSV
    • all --output-dir <dir> - Export JSON and OPML variants

    opml - OPML Management

    Purpose: Generate OPML feed lists from database-backed sources

    Workflow Usage:

    # Export to OPML
    - name: Generate OPML
      run: uv run ai-web-feeds opml all --output data/feeds.opml
    
    # Export categorized OPML
    - name: Generate categorized OPML
      run: uv run ai-web-feeds opml categorized --output data/feeds.categorized.opml
    
    # Filtered OPML
    - name: Generate filtered OPML
      run: uv run ai-web-feeds opml filtered data/feeds.filtered.opml --topic machine-learning

    Subcommands:

    • all - Generate OPML for all database sources
    • categorized - Generate source-type grouped OPML
    • filtered - Generate OPML from topic, type, tag, or verified filters

    Options:

    • --output - Output file for all and categorized
    • --topic - Filter by canonical topic
    • --type - Filter by source type
    • --tag - Filter by tag
    • --verified - Include only verified sources

    Enrichment Commands

    enrich - Enhance Feed Metadata

    Purpose: Add/update feed metadata automatically

    Workflow Usage:

    # Enrich all feeds
    - name: Enrich feeds
      run: uv run ai-web-feeds enrich all --output data/feeds.enriched.yaml
    
    # Enrich specific feed
    - name: Enrich new feed
      run: |
        FEED_URL="${{ github.event.inputs.feed_url }}"
        uv run ai-web-feeds enrich one <feed-id> --input data/feeds.yaml
    
    # Fix schema issues
    - name: Fix schema
      run: uv run ai-web-feeds enrich all
    
    # Fetch feed metadata
    - name: Fetch metadata
      run: uv run ai-web-feeds fetch one <feed-id>

    Options:

    • all --input <file> --output <file> - Enrich a full catalog
    • one <feed-id> --input <file> - Enrich one configured source
    • --database - Store enriched source metadata in a selected database
    • --schema - Write the enriched JSON Schema

    Enrichment Process:

    1. Fetches feed content
    2. Extracts title, description, language
    3. Detects feed type (RSS/Atom)
    4. Validates against schema
    5. Adds missing required fields
    6. Updates timestamps

    🔄 Workflow Patterns

    Pattern 1: Incremental Validation

    Use Case: Only validate feeds changed in PR

    name: Validate Changed Feeds
    
    on:
      pull_request:
        paths:
          - "data/feeds.yaml"
    
    jobs:
      validate:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0 # Need history for diff
    
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Get changed feeds
            id: changes
            run: |
              # Extract URLs from diff
              CHANGED=$(git diff origin/${{ github.base_ref }} -- data/feeds.yaml | \
                        grep -oP '^\+\s+url:\s*\K\S+' | \
                        tr '\n' ' ')
              echo "feeds=$CHANGED" >> $GITHUB_OUTPUT
    
          - name: Validate changed feeds
            if: steps.changes.outputs.feeds != ''
            run: uv run ai-web-feeds validate feeds --file ${{ steps.changes.outputs.feeds }}

    Pattern 2: Matrix Validation

    Use Case: Validate feeds in parallel for speed

    name: Parallel Feed Validation
    
    on:
      push:
        branches: [main]
    
    jobs:
      prepare:
        runs-on: ubuntu-latest
        outputs:
          matrix: ${{ steps.feeds.outputs.matrix }}
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Generate feed matrix
            id: feeds
            run: |
              # Extract all feed URLs into JSON array
              FEEDS=$(uv run python -c "
              import yaml, json
              with open('data/feeds.yaml') as f:
                  data = yaml.safe_load(f)
              feeds = [item['url'] for item in data['feeds']]
              # Split into chunks of 10
              chunks = [feeds[i:i+10] for i in range(0, len(feeds), 10)]
              print(json.dumps({'chunk': list(range(len(chunks)))}))
              ")
              echo "matrix=$FEEDS" >> $GITHUB_OUTPUT
    
      validate:
        needs: prepare
        runs-on: ubuntu-latest
        strategy:
          matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
          fail-fast: false
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Validate chunk ${{ matrix.chunk }}
            run: |
              # Get feeds for this chunk
              FEEDS=$(uv run python -c "
              import yaml
              with open('data/feeds.yaml') as f:
                  data = yaml.safe_load(f)
              feeds = [item['url'] for item in data['feeds']]
              chunk = feeds[${{ matrix.chunk }}*10:(${{ matrix.chunk }}+1)*10]
              print(' '.join(chunk))
              ")
              uv run ai-web-feeds validate feeds --file $FEEDS

    Pattern 3: Conditional Workflow Steps

    Use Case: Run different CLI commands based on file changes

    name: Smart Validation
    
    on: [pull_request]
    
    jobs:
      detect-changes:
        runs-on: ubuntu-latest
        outputs:
          feeds: ${{ steps.filter.outputs.feeds }}
          python: ${{ steps.filter.outputs.python }}
          web: ${{ steps.filter.outputs.web }}
        steps:
          - uses: actions/checkout@v4
          - uses: dorny/paths-filter@v3
            id: filter
            with:
              filters: |
                feeds:
                  - 'data/feeds.yaml'
                python:
                  - 'packages/**/*.py'
                  - 'apps/cli/**/*.py'
                web:
                  - 'apps/web/**/*'
    
      validate-feeds:
        needs: detect-changes
        if: needs.detect-changes.outputs.feeds == 'true'
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
          - name: Validate feeds
            run: uv run ai-web-feeds validate all --strict
    
      test-python:
        needs: detect-changes
        if: needs.detect-changes.outputs.python == 'true'
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
          - name: Run Python tests
            run: uv run ai-web-feeds test coverage
    
      test-web:
        needs: detect-changes
        if: needs.detect-changes.outputs.web == 'true'
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: pnpm/action-setup@v4
          - name: Test web
            run: |
              cd apps/web
              pnpm install
              pnpm lint
              pnpm build

    Pattern 4: PR Comments with CLI Output

    Use Case: Post CLI results as PR comments

    name: Post Feed Stats
    
    on:
      pull_request:
        paths:
          - "data/feeds.yaml"
    
    jobs:
      stats:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Generate stats
            id: stats
            run: |
              {
                echo 'stats<<EOF'
                uv run ai-web-feeds stats show
                echo EOF
              } >> $GITHUB_OUTPUT
    
          - name: Generate analytics
            id: analytics
            run: |
              {
                echo 'analytics<<EOF'
                uv run ai-web-feeds analytics summary
                echo EOF
              } >> $GITHUB_OUTPUT
    
          - name: Comment PR
            uses: actions/github-script@v7
            with:
              script: |
                const stats = `${{ steps.stats.outputs.stats }}`;
                const analytics = `${{ steps.analytics.outputs.analytics }}`;
    
                const body = `## 📊 Feed Statistics
    
                ${stats}
    
                ## 📈 Analytics
    
                \`\`\`
                ${analytics}
                \`\`\`
                `;
    
                github.rest.issues.createComment({
                  issue_number: context.issue.number,
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  body: body
                });

    Pattern 5: Workflow Artifacts

    Use Case: Save CLI output as downloadable artifacts

    name: Generate Feed Reports
    
    on:
      schedule:
        - cron: "0 0 * * 0" # Weekly on Sunday
    
    jobs:
      reports:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Generate reports
            run: |
              mkdir -p reports
    
              # Analytics report
              uv run ai-web-feeds analytics export --output reports/analytics.csv
    
              # Export feeds
              uv run ai-web-feeds export json --output reports/feeds.json
    
              # OPML export
              uv run ai-web-feeds opml all --output reports/feeds.opml
              uv run ai-web-feeds opml categorized --output reports/feeds.categorized.opml
    
              # Validation report
              uv run ai-web-feeds validate all > reports/validation.txt || true
    
              # Stats
              uv run ai-web-feeds stats show > reports/stats.md
    
          - name: Upload reports
            uses: actions/upload-artifact@v4
            with:
              name: weekly-reports
              path: reports/
              retention-days: 90

    🎨 Workflow Reports

    Use the registered CLI commands directly in workflow report jobs.

    - name: Generate PR report inputs
      run: |
        uv run ai-web-feeds stats show > reports/stats.txt
        uv run ai-web-feeds analytics export --output reports/analytics.csv
        uv run ai-web-feeds validate all --strict > reports/validation.txt

    🐛 Debugging CLI in Workflows

    Enable Verbose Output

    - name: Validate with debug
      run: uv run ai-web-feeds validate all
      env:
        AIWEBFEEDS_LOG_LEVEL: DEBUG

    Capture Logs

    - name: Validate and save logs
      run: |
        uv run ai-web-feeds validate all 2>&1 | tee validation.log
    
    - name: Upload logs
      if: failure()
      uses: actions/upload-artifact@v4
      with:
        name: validation-logs
        path: validation.log

    Test CLI Locally

    # Run exact command from workflow
    uv run ai-web-feeds validate all --strict
    
    # With environment variables
    AIWEBFEEDS_LOG_LEVEL=DEBUG uv run ai-web-feeds validate all

    📊 Monitoring & Metrics

    Track CLI Command Usage

    Add telemetry to CLI commands:

    # In CLI command
    import time
    from loguru import logger
    
    start = time.time()
    # ... command logic ...
    duration = time.time() - start
    
    logger.info(f"Command completed in {duration:.2f}s")
    
    # In workflow
    - name: Track validation time
      run: |
        START=$(date +%s)
        uv run ai-web-feeds validate all
        END=$(date +%s)
        DURATION=$((END - START))
        echo "validation_duration=$DURATION" >> $GITHUB_OUTPUT

    Workflow Performance

    name: Performance Tracking
    
    on: [push]
    
    jobs:
      benchmark:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Install uv
            uses: astral-sh/setup-uv@v5
    
          - name: Benchmark CLI commands
            run: |
              echo "## CLI Performance" > benchmark.md
    
              time_command() {
                START=$(date +%s.%N)
                $1
                END=$(date +%s.%N)
                DURATION=$(echo "$END - $START" | bc)
                echo "- $1: ${DURATION}s" >> benchmark.md
              }
    
              time_command "uv run ai-web-feeds validate feeds"
              time_command "uv run ai-web-feeds analytics summary"
              time_command "uv run ai-web-feeds export json --output benchmark-feeds.json"
    
              cat benchmark.md


    Last Updated: October 2025

    CLI Integration in Workflows | AI Web Feeds