News Pipeline #84
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: News Pipeline | |
| on: | |
| schedule: | |
| - cron: '0 6 * * *' # daily 06:00 UTC | |
| workflow_dispatch: | |
| inputs: | |
| force_synthesis: | |
| description: 'Force general weekly synthesis (ignore day-of-week check)' | |
| type: boolean | |
| required: false | |
| default: false | |
| force_ai_synthesis: | |
| description: 'Force dedicated AI synthesis (ignore day-of-week check)' | |
| type: boolean | |
| required: false | |
| default: false | |
| force_forecast: | |
| description: 'Force monthly forecast (ignore day-of-month check)' | |
| type: boolean | |
| required: false | |
| default: false | |
| score_date: | |
| description: 'Score a specific date YYYY-MM-DD instead of today' | |
| required: false | |
| default: '' | |
| score_week: | |
| description: 'Score an entire ISO week YYYY-WNN (e.g. 2026-W22)' | |
| required: false | |
| default: '' | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| pipeline: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout main | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| - name: Install dependencies | |
| run: pip install -r scripts/requirements.txt | |
| # ── Step 1: Fetch ────────────────────────────────────────────────────── | |
| - name: Fetch news (no AI) | |
| run: python scripts/fetch_news.py | |
| # ── Step 2: Score ────────────────────────────────────────────────────── | |
| - name: Build score arguments | |
| id: score_args | |
| run: | | |
| ARGS="" | |
| if [ -n "${{ github.event.inputs.score_date }}" ]; then | |
| ARGS="$ARGS --date ${{ github.event.inputs.score_date }}" | |
| elif [ -n "${{ github.event.inputs.score_week }}" ]; then | |
| ARGS="$ARGS --week ${{ github.event.inputs.score_week }}" | |
| fi | |
| echo "args=$ARGS" >> $GITHUB_OUTPUT | |
| - name: Score and select articles (Claude Haiku) | |
| run: python scripts/score_articles.py ${{ steps.score_args.outputs.args }} | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # ── Step 3a: General synthesis (Mon-Wed, or forced) ───────────────────── | |
| # Mon-Wed (not Monday-only): the target week's synthesis_id stays the same | |
| # all week (ISO week number only rolls over on the next Monday), so if | |
| # Monday's generation fails, synthesize_news.py's own "already exists" | |
| # check makes Tue/Wed a free, idempotent retry instead of losing the week. | |
| - name: Check if general synthesis should run | |
| id: should_synthesize | |
| run: | | |
| FORCED="${{ github.event.inputs.force_synthesis }}" | |
| DAY=$(date +%u) # 1=Monday … 7=Sunday | |
| if [ "$FORCED" = "true" ] || [ "$DAY" = "1" ] || [ "$DAY" = "2" ] || [ "$DAY" = "3" ]; then | |
| echo "run=true" >> $GITHUB_OUTPUT | |
| echo "General synthesis will run (forced=$FORCED, day=$DAY)" | |
| else | |
| echo "run=false" >> $GITHUB_OUTPUT | |
| echo "General synthesis skipped (not Mon-Wed, not forced)" | |
| fi | |
| - name: Generate general weekly synthesis (Claude Sonnet, AI excluded) | |
| if: steps.should_synthesize.outputs.run == 'true' | |
| run: python scripts/synthesize_news.py --track general | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # ── Step 3b: Dedicated AI synthesis (Mon + Thu, or forced) ───────────── | |
| - name: Check if AI synthesis should run | |
| id: should_synthesize_ai | |
| run: | | |
| FORCED="${{ github.event.inputs.force_ai_synthesis }}" | |
| DAY=$(date +%u) # 1=Monday, 4=Thursday | |
| if [ "$FORCED" = "true" ] || [ "$DAY" = "1" ] || [ "$DAY" = "4" ]; then | |
| echo "run=true" >> $GITHUB_OUTPUT | |
| echo "AI synthesis will run (forced=$FORCED, day=$DAY)" | |
| else | |
| echo "run=false" >> $GITHUB_OUTPUT | |
| echo "AI synthesis skipped (not Mon/Thu, not forced)" | |
| fi | |
| - name: Generate dedicated AI synthesis (Claude Sonnet, EN-only, lean) | |
| if: steps.should_synthesize_ai.outputs.run == 'true' | |
| run: python scripts/synthesize_news.py --track ai | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # ── Step 3c: Monthly forecast (1st of month, or forced) ──────────────── | |
| - name: Check if forecast should run | |
| id: should_forecast | |
| run: | | |
| FORCED="${{ github.event.inputs.force_forecast }}" | |
| DOM=$(date +%-d) # day of month | |
| if [ "$FORCED" = "true" ] || [ "$DOM" = "1" ]; then | |
| echo "run=true" >> $GITHUB_OUTPUT | |
| echo "Forecast will run (forced=$FORCED, day-of-month=$DOM)" | |
| else | |
| echo "run=false" >> $GITHUB_OUTPUT | |
| echo "Forecast skipped (not 1st of month, not forced)" | |
| fi | |
| - name: Generate monthly forecast (Claude Opus, trend ledger + web_search) | |
| if: steps.should_forecast.outputs.run == 'true' | |
| run: python scripts/forecast_news.py | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # ── Step 4: Single commit ────────────────────────────────────────────── | |
| - name: Commit changes | |
| id: commit | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add public/news.json data/trend_ledger.jsonl | |
| git diff --staged --quiet && echo "No changes." || { | |
| GEN="${{ steps.should_synthesize.outputs.run }}" | |
| AI="${{ steps.should_synthesize_ai.outputs.run }}" | |
| FORECAST="${{ steps.should_forecast.outputs.run }}" | |
| if [ "$GEN" = "true" ] && [ "$AI" = "true" ]; then | |
| MSG="chore: weekly tech + AI synthesis — $(date +%G-W%V)" | |
| elif [ "$GEN" = "true" ]; then | |
| MSG="chore: weekly tech synthesis — $(date +%G-W%V)" | |
| elif [ "$AI" = "true" ]; then | |
| MSG="chore: AI synthesis — $(date +%Y-%m-%d)" | |
| elif [ "$FORECAST" = "true" ]; then | |
| MSG="chore: monthly forecast — $(date +%Y-%m)" | |
| else | |
| MSG="chore: update tech news feed" | |
| fi | |
| git commit -m "$MSG" && git push | |
| } | |
| # ── Step 5: Push notifications ────────────────────────────────────────── | |
| - name: Notifiy push subscribers | |
| run: | | |
| npm install web-push | |
| node scripts/notify_push.mjs | |
| env: | |
| VAPID_PUBLIC_KEY: ${{ secrets.VAPID_PUBLIC_KEY }} | |
| VAPID_PRIVATE_KEY: ${{ secrets.VAPID_PRIVATE_KEY }} | |
| VAPID_SUBJECT: mailto:bourbasquet.k@etik.com | |
| WORKER_URL: https://bourbask-contact.bourbask.workers.dev | |
| NOTIFY_SECRET: ${{ secrets.NOTIFY_SECRET }} |