Repository navigation
Content pipelines: Update content #157
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: 'Content pipelines: Update content' | |
| # Updates reference docs from upstream source docs without storing source copies here. | |
| on: | |
| schedule: | |
| - cron: '20 16 * * 1-5' # Weekdays at 16:20 UTC. | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| env: | |
| HUSKY: 0 | |
| jobs: | |
| update: | |
| if: github.repository == 'github/docs-internal' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Add each new pipeline to src/content-pipelines/config.yml and this matrix. | |
| # src/content-pipelines/scripts/update.ts reads config.yml for source and target values. | |
| - id: copilot-cli | |
| - id: gh-stack | |
| steps: | |
| - name: Checkout docs-internal | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: ./.github/actions/node-npm-setup | |
| - name: Install Copilot CLI | |
| run: npm install -g @github/copilot@prerelease | |
| - name: Derive branch name | |
| id: branch | |
| run: echo "update_branch=docs/content-pipeline-${{ matrix.id }}-update" >> "$GITHUB_OUTPUT" | |
| - name: Check for existing PR | |
| id: check-pr | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| UPDATE_BRANCH: ${{ steps.branch.outputs.update_branch }} | |
| run: | | |
| PR_NUMBER=$(gh pr list --head "$UPDATE_BRANCH" --state open --json number --jq '.[0].number // empty' 2>/dev/null || echo "") | |
| echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" | |
| - name: Setup branch | |
| id: setup-branch | |
| env: | |
| UPDATE_BRANCH: ${{ steps.branch.outputs.update_branch }} | |
| PR_NUMBER: ${{ steps.check-pr.outputs.pr_number }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| if git ls-remote --exit-code --heads origin "$UPDATE_BRANCH" > /dev/null 2>&1; then | |
| git fetch --unshallow origin "$UPDATE_BRANCH" main 2>/dev/null || git fetch origin "$UPDATE_BRANCH" main | |
| git checkout "$UPDATE_BRANCH" | |
| git merge origin/main --no-edit || { | |
| echo "Merge conflict with main — resetting branch to main" | |
| git merge --abort 2>/dev/null || true | |
| git checkout -f main | |
| git branch -D "$UPDATE_BRANCH" | |
| if [ -z "$PR_NUMBER" ]; then | |
| git push origin --delete "$UPDATE_BRANCH" || true | |
| else | |
| echo "Skipping remote branch delete — PR #$PR_NUMBER is open" | |
| echo "force_push=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| git checkout -b "$UPDATE_BRANCH" | |
| } | |
| else | |
| git checkout -b "$UPDATE_BRANCH" | |
| fi | |
| - name: Run content pipeline update script | |
| env: | |
| GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| COPILOT_GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_COPILOT }} | |
| run: npx tsx src/content-pipelines/scripts/update.ts --id "${{ matrix.id }}" | |
| - name: Commit changes | |
| id: commit | |
| env: | |
| ID: ${{ matrix.id }} | |
| run: | | |
| git add content/ data/ | |
| if git diff --cached --quiet; then | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| echo "No documentation changes to commit" | |
| else | |
| git add "src/content-pipelines/state/${ID}.sha" | |
| git commit -m "docs: update ${ID} content from source docs" \ | |
| -m "Updated by the content-pipeline-update agent (${ID}) via GitHub Actions." \ | |
| -m "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Push changes | |
| if: steps.commit.outputs.has_changes == 'true' | |
| env: | |
| UPDATE_BRANCH: ${{ steps.branch.outputs.update_branch }} | |
| FORCE_PUSH: ${{ steps.setup-branch.outputs.force_push }} | |
| run: | | |
| if [ "$FORCE_PUSH" = "true" ]; then | |
| echo "Force-pushing to align branch after merge conflict reset" | |
| git push --force-with-lease origin "$UPDATE_BRANCH" | |
| else | |
| git push origin "$UPDATE_BRANCH" | |
| fi | |
| - name: Read source repo info from config | |
| id: source-info | |
| env: | |
| PIPELINE_ID: ${{ matrix.id }} | |
| run: | | |
| SOURCE_REPO=$(yq -r ".[\"${PIPELINE_ID}\"].\"source-repo\"" src/content-pipelines/config.yml) | |
| SOURCE_PATH=$(yq -r ".[\"${PIPELINE_ID}\"].\"source-path\"" src/content-pipelines/config.yml) | |
| echo "source_repo=$SOURCE_REPO" >> "$GITHUB_OUTPUT" | |
| echo "source_path=$SOURCE_PATH" >> "$GITHUB_OUTPUT" | |
| - name: Create or update PR | |
| if: steps.commit.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
| UPDATE_BRANCH: ${{ steps.branch.outputs.update_branch }} | |
| PIPELINE_ID: ${{ matrix.id }} | |
| SOURCE_REPO: ${{ steps.source-info.outputs.source_repo }} | |
| SOURCE_PATH: ${{ steps.source-info.outputs.source_path }} | |
| run: | | |
| PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" | |
| PR_TITLE="docs: update ${PIPELINE_ID} content from source docs" | |
| SOURCE_LINK="See the [upstream repo](https://github.com/${SOURCE_REPO}/tree/main/${SOURCE_PATH}) for changes that triggered this update." | |
| if [ -n "$PR_NUMBER" ]; then | |
| echo "PR #$PR_NUMBER already exists" | |
| echo "Ensuring PR #$PR_NUMBER is marked ready for review" | |
| gh pr ready "$PR_NUMBER" || echo "Unable to mark PR #$PR_NUMBER as ready (it may already be ready)" | |
| else | |
| echo "Creating new PR" | |
| PR_BODY="_GitHub Copilot generated this pull request._"$'\n\n' | |
| PR_BODY+="> [!NOTE]"$'\n' | |
| PR_BODY+="> This PR is **automatically generated** by the [content pipeline update workflow](${{ github.server_url }}/${{ github.repository }}/actions/workflows/content-pipelines.yml). Each run adds a new commit with any documentation changes detected."$'\n\n' | |
| PR_BODY+="> [!WARNING]"$'\n' | |
| PR_BODY+="> **Review with care before merging.** The agent generally does a good job, but these PRs can contain:"$'\n' | |
| PR_BODY+="> * Information customers don't need to know"$'\n' | |
| PR_BODY+="> * Internal or feature-flagged details that shouldn't be public"$'\n' | |
| PR_BODY+="> * Excessive or low-value detail"$'\n' | |
| PR_BODY+=">"$'\n' | |
| PR_BODY+="> Cross-check changes against the source docs linked below. If in doubt, leave the review to a subject-matter expert on the Technical Content team."$'\n\n' | |
| PR_BODY+="## What this does"$'\n\n' | |
| PR_BODY+="Runs the \`content-pipeline-update\` agent (${PIPELINE_ID}) against the latest source docs and updates official articles under \`content/\` that have fallen out of sync."$'\n\n' | |
| PR_BODY+="## Source changes"$'\n\n' | |
| PR_BODY+="${SOURCE_LINK}"$'\n\n' | |
| PR_BODY+="## Review"$'\n\n' | |
| PR_BODY+="* Review each commit for accuracy — the agent uses AI, so spot-check important changes against the source docs"$'\n' | |
| PR_BODY+="* Watch especially for invented examples, internal-only content, and excessive detail (see warning above)"$'\n' | |
| PR_BODY+="* To adjust agent behavior, see [Modifying results](${{ github.server_url }}/${{ github.repository }}/blob/main/src/content-pipelines/README.md#modifying-results)"$'\n' | |
| PR_BODY+="* Once satisfied, merge to keep docs up to date"$'\n' | |
| PR_BODY+="* A new PR will be created on the next run if there are further changes" | |
| gh pr create \ | |
| --title "$PR_TITLE" \ | |
| --body "$PR_BODY" \ | |
| --base main \ | |
| --head "$UPDATE_BRANCH" \ | |
| --label "workflow-generated,content-pipeline-update,ready-for-doc-review,skip FR board" | |
| fi | |
| - uses: ./.github/actions/create-workflow-failure-issue | |
| id: create-failure-issue | |
| if: ${{ failure() && github.event_name != 'workflow_dispatch' }} | |
| with: | |
| token: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
| - uses: ./.github/actions/slack-alert | |
| if: ${{ failure() && github.event_name != 'workflow_dispatch' }} | |
| with: | |
| slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} | |
| issue_url: ${{ steps.create-failure-issue.outputs.issue_url }} |