docs_automation.yml

  1name: Documentation Automation
  2
  3on:
  4  push:
  5    branches: [main]
  6    paths:
  7      - 'crates/**'
  8      - 'extensions/**'
  9  workflow_dispatch:
 10    inputs:
 11      pr_number:
 12        description: 'PR number to analyze (gets full PR diff)'
 13        required: false
 14        type: string
 15      trigger_sha:
 16        description: 'Commit SHA to analyze (ignored if pr_number is set)'
 17        required: false
 18        type: string
 19
 20permissions:
 21  contents: write
 22  pull-requests: write
 23
 24env:
 25  FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
 26  DROID_MODEL: claude-opus-4-5
 27
 28jobs:
 29  docs-automation:
 30    runs-on: ubuntu-latest
 31    timeout-minutes: 30
 32    
 33    steps:
 34      - name: Checkout repository
 35        uses: actions/checkout@v4
 36        with:
 37          fetch-depth: 0
 38
 39      - name: Install Droid CLI
 40        run: |
 41          curl -fsSL https://cli.factory.ai/install.sh | bash
 42          echo "${HOME}/.factory/bin" >> "$GITHUB_PATH"
 43
 44      - name: Setup Node.js (for Prettier)
 45        uses: actions/setup-node@v4
 46        with:
 47          node-version: '20'
 48
 49      - name: Install Prettier
 50        run: npm install -g prettier
 51
 52      - name: Get changed files
 53        id: changed
 54        run: |
 55          if [ -n "${{ inputs.pr_number }}" ]; then
 56            # Get full PR diff
 57            echo "Analyzing PR #${{ inputs.pr_number }}"
 58            echo "source=pr" >> "$GITHUB_OUTPUT"
 59            echo "ref=${{ inputs.pr_number }}" >> "$GITHUB_OUTPUT"
 60            gh pr diff "${{ inputs.pr_number }}" --name-only > /tmp/changed_files.txt
 61          elif [ -n "${{ inputs.trigger_sha }}" ]; then
 62            # Get single commit diff
 63            SHA="${{ inputs.trigger_sha }}"
 64            echo "Analyzing commit $SHA"
 65            echo "source=commit" >> "$GITHUB_OUTPUT"
 66            echo "ref=$SHA" >> "$GITHUB_OUTPUT"
 67            git diff --name-only "${SHA}^" "$SHA" > /tmp/changed_files.txt
 68          else
 69            # Default to current commit
 70            SHA="${{ github.sha }}"
 71            echo "Analyzing commit $SHA"
 72            echo "source=commit" >> "$GITHUB_OUTPUT"
 73            echo "ref=$SHA" >> "$GITHUB_OUTPUT"
 74            git diff --name-only "${SHA}^" "$SHA" > /tmp/changed_files.txt || git diff --name-only HEAD~1 HEAD > /tmp/changed_files.txt
 75          fi
 76          
 77          echo "Changed files:"
 78          cat /tmp/changed_files.txt
 79        env:
 80          GH_TOKEN: ${{ github.token }}
 81
 82      # Phase 0: Guardrails are loaded via AGENTS.md in each phase
 83
 84      # Phase 2: Explore Repository (Read-Only)
 85      - name: "Phase 2: Explore Repository"
 86        id: phase2
 87        run: |
 88          droid exec \
 89            --model "$DROID_MODEL" \
 90            --autonomy read-only \
 91            --prompt-file .factory/prompts/docs-automation/phase2-explore.md \
 92            --output /tmp/phase2-output.json \
 93            --format json
 94          echo "Repository exploration complete"
 95          cat /tmp/phase2-output.json
 96
 97      # Phase 3: Analyze Changes (Read-Only)
 98      - name: "Phase 3: Analyze Changes"
 99        id: phase3
100        run: |
101          CHANGED_FILES=$(tr '\n' ' ' < /tmp/changed_files.txt)
102          droid exec \
103            --model "$DROID_MODEL" \
104            --autonomy read-only \
105            --prompt-file .factory/prompts/docs-automation/phase3-analyze.md \
106            --context "Changed files: $CHANGED_FILES" \
107            --context-file /tmp/phase2-output.json \
108            --output /tmp/phase3-output.md \
109            --format markdown
110          echo "Change analysis complete"
111          cat /tmp/phase3-output.md
112
113      # Phase 4: Plan Documentation Impact (Read-Only)
114      - name: "Phase 4: Plan Documentation Impact"
115        id: phase4
116        run: |
117          droid exec \
118            --model "$DROID_MODEL" \
119            --autonomy read-only \
120            --prompt-file .factory/prompts/docs-automation/phase4-plan.md \
121            --context-file /tmp/phase3-output.md \
122            --context-file docs/AGENTS.md \
123            --output /tmp/phase4-plan.md \
124            --format markdown
125          echo "Documentation plan complete"
126          cat /tmp/phase4-plan.md
127          
128          # Check if updates are required
129          if grep -q "Documentation Updates Required: No" /tmp/phase4-plan.md; then
130            echo "updates_required=false" >> "$GITHUB_OUTPUT"
131          else
132            echo "updates_required=true" >> "$GITHUB_OUTPUT"
133          fi
134
135      # Phase 5: Apply Plan (Write-Enabled)
136      - name: "Phase 5: Apply Documentation Plan"
137        id: phase5
138        if: steps.phase4.outputs.updates_required == 'true'
139        run: |
140          droid exec \
141            --model "$DROID_MODEL" \
142            --autonomy medium \
143            --prompt-file .factory/prompts/docs-automation/phase5-apply.md \
144            --context-file /tmp/phase4-plan.md \
145            --context-file docs/AGENTS.md \
146            --context-file docs/.rules \
147            --output /tmp/phase5-report.md \
148            --format markdown
149          echo "Documentation updates applied"
150          cat /tmp/phase5-report.md
151
152      # Phase 5b: Format with Prettier
153      - name: "Phase 5b: Format with Prettier"
154        id: phase5b
155        if: steps.phase4.outputs.updates_required == 'true'
156        run: |
157          echo "Formatting documentation with Prettier..."
158          cd docs && prettier --write src/
159          
160          echo "Verifying Prettier formatting passes..."
161          cd docs && prettier --check src/
162          
163          echo "Prettier formatting complete"
164
165      # Phase 6: Summarize Changes
166      - name: "Phase 6: Summarize Changes"
167        id: phase6
168        if: steps.phase4.outputs.updates_required == 'true'
169        run: |
170          # Get git diff of docs
171          git diff docs/src/ > /tmp/docs-diff.txt || true
172          
173          droid exec \
174            --model "$DROID_MODEL" \
175            --autonomy read-only \
176            --prompt-file .factory/prompts/docs-automation/phase6-summarize.md \
177            --context-file /tmp/phase5-report.md \
178            --context-file /tmp/phase3-output.md \
179            --context "Trigger SHA: ${{ steps.changed.outputs.sha }}" \
180            --output /tmp/phase6-summary.md \
181            --format markdown
182          echo "Summary generated"
183          cat /tmp/phase6-summary.md
184
185      # Phase 7: Commit and Open PR
186      - name: "Phase 7: Create PR"
187        id: phase7
188        if: steps.phase4.outputs.updates_required == 'true'
189        run: |
190          # Check if there are actual changes
191          if git diff --quiet docs/src/; then
192            echo "No documentation changes detected"
193            exit 0
194          fi
195          
196          # Configure git
197          git config user.name "factory-droid[bot]"
198          git config user.email "138933559+factory-droid[bot]@users.noreply.github.com"
199          
200          # Daily batch branch - one branch per day, multiple commits accumulate
201          BRANCH_NAME="docs/auto-update-$(date +%Y-%m-%d)"
202          
203          # Check if branch already exists on remote
204          if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then
205            echo "Branch $BRANCH_NAME exists, checking out and updating..."
206            git fetch origin "$BRANCH_NAME"
207            git checkout -B "$BRANCH_NAME" "origin/$BRANCH_NAME"
208          else
209            echo "Creating new branch $BRANCH_NAME..."
210            git checkout -b "$BRANCH_NAME"
211          fi
212          
213          # Stage and commit
214          git add docs/src/
215          SUMMARY=$(head -50 < /tmp/phase6-summary.md)
216          git commit -m "docs: auto-update documentation
217
218          ${SUMMARY}
219
220          Triggered by: ${{ steps.changed.outputs.source }} ${{ steps.changed.outputs.ref }}
221
222          Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>"
223          
224          # Push
225          git push -u origin "$BRANCH_NAME"
226          
227          # Check if PR already exists for this branch
228          EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' || echo "")
229          
230          if [ -n "$EXISTING_PR" ]; then
231            echo "PR #$EXISTING_PR already exists for branch $BRANCH_NAME, updated with new commit"
232          else
233            # Create new PR
234            gh pr create \
235              --title "docs: automated documentation update ($(date +%Y-%m-%d))" \
236              --body-file /tmp/phase6-summary.md \
237              --base main || true
238            echo "PR created on branch: $BRANCH_NAME"
239          fi
240        env:
241          GH_TOKEN: ${{ github.token }}
242
243      # Summary output
244      - name: "Summary"
245        if: always()
246        run: |
247          echo "## Documentation Automation Summary" >> "$GITHUB_STEP_SUMMARY"
248          echo "" >> "$GITHUB_STEP_SUMMARY"
249          
250          if [ "${{ steps.phase4.outputs.updates_required }}" == "false" ]; then
251            echo "No documentation updates required for this change." >> "$GITHUB_STEP_SUMMARY"
252          elif [ -f /tmp/phase6-summary.md ]; then
253            cat /tmp/phase6-summary.md >> "$GITHUB_STEP_SUMMARY"
254          else
255            echo "Workflow completed. Check individual phase outputs for details." >> "$GITHUB_STEP_SUMMARY"
256          fi