Final ProjectGenerative Pipeline

AI Blog Writer

The ultimate GitHub Actions masterpiece. Bridge Git, Google Gemini AI, and Hashnode to automate your technical writing process from commit to publication.

AI Content Factory

Automated Pipeline: Topic → AI → Published Post

Input Topicbullets.md
Gemini AIContent Expansion
HashnodePublishing Draft
Git RepoPersisting Post
Latency~42s
Word Count1200+
Cost~$0.00
SecuritySSH-Gate

Secret Vault

GEMINI_API_KEY

Google AI Studio

HASHNODE_API_KEY

Hashnode Dev Settings

HASHNODE_TOKEN

Publication ID

Project Structure

├── topics/ # Your bullet points

├── posts/ # AI generated posts

├── scripts/ # Generator logic

└── .github/workflows/

Phase 1: The Orchestrator

Detecting changes and triggering the AI expansion.

.github/workflows/auto-blog.yml
# AUTO BLOG WRITER — GitHub Actions + Google Gemini + Hashnode
name: Auto Blog Writer

on:
  push:
    paths:
      - "topics/**" # Only triggers when you add/edit a file in topics/

  workflow_dispatch: # Also run manually from GitHub UI
    inputs:
      topic_file:
        description: "Which topic file to process? (e.g. topics/my-topic.md)"
        type: string
        required: false

jobs:
  write-and-publish:
    name: "Write Blog Post with AI and Publish"
    runs-on: ubuntu-latest

    permissions:
      contents: write # Needed to commit the generated post back

    steps:
      - name: "1. Download the repo"
        uses: actions/checkout@v4
        with:
          persist-credentials: true
          fetch-depth: 2 # Need 2 commits to detect which files changed

      - name: "2. Set up Node.js"
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: "3. Find which topic file was just added or changed"
        id: find-topic
        run: |
          if [ -n "${{ inputs.topic_file }}" ]; then
            # Manual trigger — use the file they specified
            TOPIC_FILE="${{ inputs.topic_file }}"
          else
            # Auto trigger — find which topics/ file changed in this push
            TOPIC_FILE=$(git diff --name-only HEAD~1 HEAD -- 'topics/*.md' | head -1)
          fi

          if [ -z "$TOPIC_FILE" ]; then
            echo "No topic file found. Skipping."
            echo "found=false" >> $GITHUB_OUTPUT
          else
            echo "Found topic file: $TOPIC_FILE"
            echo "found=true"       >> $GITHUB_OUTPUT
            echo "file=$TOPIC_FILE" >> $GITHUB_OUTPUT
          fi

      - name: "4. Write and publish the blog post"
        if: steps.find-topic.outputs.found == 'true'
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          HASHNODE_API_KEY: ${{ secrets.HASHNODE_API_KEY }}
          HASHNODE_PUBLICATION_TOKEN: ${{ secrets.HASHNODE_PUBLICATION_TOKEN }}
          TOPIC_FILE: ${{ steps.find-topic.outputs.file }}
        run: node scripts/auto-blog-writer.js

      - name: "5. Commit generated post"
        if: steps.find-topic.outputs.found == 'true'
        run: |
          git config user.name  "Blog Writer Bot"
          git config user.email "adityatrivedi612@gmail.com"
          git add posts/
          git add .published.json || true
          
          if git diff --staged --quiet; then
            echo "Nothing to commit."
          else
            git commit -m "📝 Auto-generated blog post [skip ci]"
            git push
          fi

Hybrid Detection

Detects topics via 'git diff' for auto-runs, or uses 'inputs.topic_file' for manual runs. No code duplication.

Write Access

Explicitly grants 'contents: write' so the bot can commit the generated markdown back to your repo.

[skip ci] Guard

Prevents infinite loops by telling GitHub not to trigger another run from this bot's commit.

1

Checkout with Fetch-Depth

By default, checkout only fetches 1 commit. We use 'fetch-depth: 2' so Git can compare HEAD with the previous commit to see which files changed.

actions/checkout@v4 with fetch-depth: 2
2

The 'Find Topic' Logic

A Bash script that identifies the topic file. It prioritizes manual input; if empty, it uses 'git diff' to find the first renamed or added markdown file in 'topics/'.

TOPIC_FILE=$(git diff --name-only HEAD~1 HEAD -- 'topics/*.md')
3

Bot Identity & Push

Configures a custom bot name/email for the commit. It checks if there are actual changes using 'git diff --staged' before pushing to avoid empty commits.

git commit -m '📝 Auto-generated blog post [skip ci]'

Phase 2: The Generator

Inside the Node.js script that bridges AI and Publishing.

1

Prompt Engineering

The heart of the system. We feed Gemini a structured prompt that sets the tone (Conversational), audience (Developers), and format (Markdown).

Instructs Gemini to avoid jargon and use Relatable Hooks.
2

GraphQL Publishing

Bypasses standard REST. We use GraphQL mutations to create a draft on Hashnode with the title and AI-generated content.

mutation CreateDraft { createDraft(input: $input) { ... } }
3

State Persistence

Maintains a '.published.json' file to track already processed topics, preventing duplicate posts if a workflow re-runs.

Automatically skipped if topic exists in log.

Get the Full Source Code

The complete GitHub Actions Series repository is open-sourced. Dive into the code for all 10 tutorials.

View on GitHub
git clone https://github.com/Yuvadi29/Github-Actions-Series.git