Skip to content

News Pipeline

News Pipeline #90

Workflow file for this run

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 }}