Skip to content

Blog Draft Pipeline

Blog Draft Pipeline #6

Workflow file for this run

name: Blog Draft (Every 2 Days)
on:
schedule:
# Every 2 days at 8am UTC (odd days: 1st, 3rd, 5th...)
# NOTE: 1-31/2 fires on consecutive days at month boundaries (31st then 1st).
# The pipeline is idempotent (skips if no Detected cards), so extra runs are harmless.
- cron: '0 8 1-31/2 * *'
workflow_dispatch: # Manual trigger
concurrency:
group: blog-draft
cancel-in-progress: false
permissions:
repository-projects: write
contents: write
id-token: write
jobs:
pick-and-draft:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: develop
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pipeline dependencies
working-directory: blog/pipeline
run: npm ci
- name: Pick next topic
id: pick
working-directory: blog/pipeline
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
run: |
OUTPUT=$(node index.js --pick 2>&1)
echo "$OUTPUT"
PICKED=$(echo "$OUTPUT" | grep '::picked::' | sed 's/::picked:://')
if [ -z "$PICKED" ]; then
echo "No topic picked — all clusters have active work"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "$PICKED" > /tmp/picked-topic.json
TOPIC=$(echo "$PICKED" | jq -r '.cluster')
CONTENT_TYPE=$(echo "$PICKED" | jq -r '.contentType')
CARD_ID=$(echo "$PICKED" | jq -r '.id')
CARD_BODY=$(echo "$PICKED" | jq -r '.body // ""')
echo "skip=false" >> $GITHUB_OUTPUT
echo "topic=$TOPIC" >> $GITHUB_OUTPUT
echo "content_type=$CONTENT_TYPE" >> $GITHUB_OUTPUT
echo "card_id=$CARD_ID" >> $GITHUB_OUTPUT
# Write card body to workspace file so Claude can read it
echo "$CARD_BODY" > "$GITHUB_WORKSPACE/blog/pipeline/.card-body.md"
fi
- name: Move card to Researched
if: steps.pick.outputs.skip != 'true'
working-directory: blog/pipeline
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
CARD_ID: ${{ steps.pick.outputs.card_id }}
run: |
node --input-type=module -e "
import { updateCardStatus } from './project.js';
updateCardStatus(process.env.CARD_ID, 'researched');
console.log('Card moved to Researched');
"
- name: Research and draft
id: draft
if: steps.pick.outputs.skip != 'true'
uses: anthropics/claude-code-action@9469d113c6afd29550c402740f22d1a97dd1209b # v1
with:
prompt: |
You are writing a blog article for the Ethernal blog (On-Chain Engineering).
A trending topic has been detected by our pipeline:
**Topic:** ${{ steps.pick.outputs.topic }}
**Content Type:** ${{ steps.pick.outputs.content_type }}
Read the file `blog/pipeline/.card-body.md` for the full trend card content with sources to research.
Steps:
1. Run /blog:research for this topic. Focus on the specific EIPs, ERCs, papers, and forum posts listed in the trend card — those are your primary sources.
2. Run /blog:draft to write the article. Use the Content Type to determine the format:
- "ERC Tutorial": Code-heavy, include working Solidity, deploy instructions, practical examples
- "EIP Explainer": What it changes, why it matters, code impact with before/after examples
- "Research Deep Dive": Break down the paper/proposal, extract practical insights, include code snippets
- "Upgrade Guide": Step-by-step migration guide with code changes needed
- "Trend Survey": Survey multiple related proposals, compare approaches, include interface examples
Cite the sources from the trend card as references in the article.
3. IMPORTANT: Set the article frontmatter status to "draft" (not "published").
4. Commit the article directly to the develop branch with message "blog: add draft — <article title>".
5. Push to origin develop.
6. After pushing, output the article file path relative to the repo root on a line starting with "::article-path::"
Example: ::article-path::blog/src/content/blog/my-article.md
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
settings: |
{
"permissions": {
"allow": ["Read", "Write", "Edit", "Bash", "WebSearch", "WebFetch", "Glob", "Grep", "Skill"]
}
}
- name: Update project card
if: steps.pick.outputs.skip != 'true'
working-directory: blog/pipeline
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
run: |
# Search last 5 commits in case Claude pushed multiple
ARTICLE_PATH=$(git log -5 --name-only --pretty=format: | grep 'blog/src/content/blog/' | head -1)
if [ -n "$ARTICLE_PATH" ]; then
echo "Article: $ARTICLE_PATH"
CARD_ID="${{ steps.pick.outputs.card_id }}" ARTICLE_PATH="$ARTICLE_PATH" node --input-type=module -e "
import { updateCardStatus, setArticlePath } from './project.js';
updateCardStatus(process.env.CARD_ID, 'drafting');
setArticlePath(process.env.CARD_ID, process.env.ARTICLE_PATH);
console.log('Card updated');
"
else
echo "WARNING: Could not find article path in last commit"
fi
- name: Reset card on failure
if: failure() && steps.draft.outcome == 'failure' && steps.pick.outputs.skip != 'true'
working-directory: blog/pipeline
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
CARD_ID: ${{ steps.pick.outputs.card_id }}
run: |
node --input-type=module -e "
import { updateCardStatus } from './project.js';
updateCardStatus(process.env.CARD_ID, 'detected');
console.log('Card reset to Detected after failure');
"